aws-inventory-manager 0.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of aws-inventory-manager might be problematic. Click here for more details.

Files changed (145) hide show
  1. aws_inventory_manager-0.13.2.dist-info/LICENSE +21 -0
  2. aws_inventory_manager-0.13.2.dist-info/METADATA +1226 -0
  3. aws_inventory_manager-0.13.2.dist-info/RECORD +145 -0
  4. aws_inventory_manager-0.13.2.dist-info/WHEEL +5 -0
  5. aws_inventory_manager-0.13.2.dist-info/entry_points.txt +2 -0
  6. aws_inventory_manager-0.13.2.dist-info/top_level.txt +1 -0
  7. src/__init__.py +3 -0
  8. src/aws/__init__.py +11 -0
  9. src/aws/client.py +128 -0
  10. src/aws/credentials.py +191 -0
  11. src/aws/rate_limiter.py +177 -0
  12. src/cli/__init__.py +12 -0
  13. src/cli/config.py +130 -0
  14. src/cli/main.py +3626 -0
  15. src/config_service/__init__.py +21 -0
  16. src/config_service/collector.py +346 -0
  17. src/config_service/detector.py +256 -0
  18. src/config_service/resource_type_mapping.py +328 -0
  19. src/cost/__init__.py +5 -0
  20. src/cost/analyzer.py +226 -0
  21. src/cost/explorer.py +209 -0
  22. src/cost/reporter.py +237 -0
  23. src/delta/__init__.py +5 -0
  24. src/delta/calculator.py +206 -0
  25. src/delta/differ.py +185 -0
  26. src/delta/formatters.py +272 -0
  27. src/delta/models.py +154 -0
  28. src/delta/reporter.py +234 -0
  29. src/models/__init__.py +21 -0
  30. src/models/config_diff.py +135 -0
  31. src/models/cost_report.py +87 -0
  32. src/models/deletion_operation.py +104 -0
  33. src/models/deletion_record.py +97 -0
  34. src/models/delta_report.py +122 -0
  35. src/models/efs_resource.py +80 -0
  36. src/models/elasticache_resource.py +90 -0
  37. src/models/group.py +318 -0
  38. src/models/inventory.py +133 -0
  39. src/models/protection_rule.py +123 -0
  40. src/models/report.py +288 -0
  41. src/models/resource.py +111 -0
  42. src/models/security_finding.py +102 -0
  43. src/models/snapshot.py +122 -0
  44. src/restore/__init__.py +20 -0
  45. src/restore/audit.py +175 -0
  46. src/restore/cleaner.py +461 -0
  47. src/restore/config.py +209 -0
  48. src/restore/deleter.py +976 -0
  49. src/restore/dependency.py +254 -0
  50. src/restore/safety.py +115 -0
  51. src/security/__init__.py +0 -0
  52. src/security/checks/__init__.py +0 -0
  53. src/security/checks/base.py +56 -0
  54. src/security/checks/ec2_checks.py +88 -0
  55. src/security/checks/elasticache_checks.py +149 -0
  56. src/security/checks/iam_checks.py +102 -0
  57. src/security/checks/rds_checks.py +140 -0
  58. src/security/checks/s3_checks.py +95 -0
  59. src/security/checks/secrets_checks.py +96 -0
  60. src/security/checks/sg_checks.py +142 -0
  61. src/security/cis_mapper.py +97 -0
  62. src/security/models.py +53 -0
  63. src/security/reporter.py +174 -0
  64. src/security/scanner.py +87 -0
  65. src/snapshot/__init__.py +6 -0
  66. src/snapshot/capturer.py +451 -0
  67. src/snapshot/filter.py +259 -0
  68. src/snapshot/inventory_storage.py +236 -0
  69. src/snapshot/report_formatter.py +250 -0
  70. src/snapshot/reporter.py +189 -0
  71. src/snapshot/resource_collectors/__init__.py +5 -0
  72. src/snapshot/resource_collectors/apigateway.py +140 -0
  73. src/snapshot/resource_collectors/backup.py +136 -0
  74. src/snapshot/resource_collectors/base.py +81 -0
  75. src/snapshot/resource_collectors/cloudformation.py +55 -0
  76. src/snapshot/resource_collectors/cloudwatch.py +109 -0
  77. src/snapshot/resource_collectors/codebuild.py +69 -0
  78. src/snapshot/resource_collectors/codepipeline.py +82 -0
  79. src/snapshot/resource_collectors/dynamodb.py +65 -0
  80. src/snapshot/resource_collectors/ec2.py +240 -0
  81. src/snapshot/resource_collectors/ecs.py +215 -0
  82. src/snapshot/resource_collectors/efs_collector.py +102 -0
  83. src/snapshot/resource_collectors/eks.py +200 -0
  84. src/snapshot/resource_collectors/elasticache_collector.py +79 -0
  85. src/snapshot/resource_collectors/elb.py +126 -0
  86. src/snapshot/resource_collectors/eventbridge.py +156 -0
  87. src/snapshot/resource_collectors/iam.py +188 -0
  88. src/snapshot/resource_collectors/kms.py +111 -0
  89. src/snapshot/resource_collectors/lambda_func.py +139 -0
  90. src/snapshot/resource_collectors/rds.py +109 -0
  91. src/snapshot/resource_collectors/route53.py +86 -0
  92. src/snapshot/resource_collectors/s3.py +105 -0
  93. src/snapshot/resource_collectors/secretsmanager.py +70 -0
  94. src/snapshot/resource_collectors/sns.py +68 -0
  95. src/snapshot/resource_collectors/sqs.py +82 -0
  96. src/snapshot/resource_collectors/ssm.py +160 -0
  97. src/snapshot/resource_collectors/stepfunctions.py +74 -0
  98. src/snapshot/resource_collectors/vpcendpoints.py +79 -0
  99. src/snapshot/resource_collectors/waf.py +159 -0
  100. src/snapshot/storage.py +351 -0
  101. src/storage/__init__.py +21 -0
  102. src/storage/audit_store.py +419 -0
  103. src/storage/database.py +294 -0
  104. src/storage/group_store.py +749 -0
  105. src/storage/inventory_store.py +320 -0
  106. src/storage/resource_store.py +413 -0
  107. src/storage/schema.py +288 -0
  108. src/storage/snapshot_store.py +346 -0
  109. src/utils/__init__.py +12 -0
  110. src/utils/export.py +305 -0
  111. src/utils/hash.py +60 -0
  112. src/utils/logging.py +63 -0
  113. src/utils/pagination.py +41 -0
  114. src/utils/paths.py +51 -0
  115. src/utils/progress.py +41 -0
  116. src/utils/unsupported_resources.py +306 -0
  117. src/web/__init__.py +5 -0
  118. src/web/app.py +97 -0
  119. src/web/dependencies.py +69 -0
  120. src/web/routes/__init__.py +1 -0
  121. src/web/routes/api/__init__.py +18 -0
  122. src/web/routes/api/charts.py +156 -0
  123. src/web/routes/api/cleanup.py +186 -0
  124. src/web/routes/api/filters.py +253 -0
  125. src/web/routes/api/groups.py +305 -0
  126. src/web/routes/api/inventories.py +80 -0
  127. src/web/routes/api/queries.py +202 -0
  128. src/web/routes/api/resources.py +379 -0
  129. src/web/routes/api/snapshots.py +314 -0
  130. src/web/routes/api/views.py +260 -0
  131. src/web/routes/pages.py +198 -0
  132. src/web/services/__init__.py +1 -0
  133. src/web/templates/base.html +949 -0
  134. src/web/templates/components/navbar.html +31 -0
  135. src/web/templates/components/sidebar.html +104 -0
  136. src/web/templates/pages/audit_logs.html +86 -0
  137. src/web/templates/pages/cleanup.html +279 -0
  138. src/web/templates/pages/dashboard.html +227 -0
  139. src/web/templates/pages/diff.html +175 -0
  140. src/web/templates/pages/error.html +30 -0
  141. src/web/templates/pages/groups.html +721 -0
  142. src/web/templates/pages/queries.html +246 -0
  143. src/web/templates/pages/resources.html +2251 -0
  144. src/web/templates/pages/snapshot_detail.html +271 -0
  145. src/web/templates/pages/snapshots.html +429 -0
@@ -0,0 +1,240 @@
1
+ """EC2 resource collector."""
2
+
3
+ from typing import List
4
+
5
+ from ...models.resource import Resource
6
+ from ...utils.hash import compute_config_hash
7
+ from .base import BaseResourceCollector
8
+
9
+
10
+ class EC2Collector(BaseResourceCollector):
11
+ """Collector for AWS EC2 resources (instances, volumes, VPCs, security groups, etc.)."""
12
+
13
+ @property
14
+ def service_name(self) -> str:
15
+ return "ec2"
16
+
17
+ def collect(self) -> List[Resource]:
18
+ """Collect EC2 resources.
19
+
20
+ Returns:
21
+ List of EC2 resources
22
+ """
23
+ resources = []
24
+ account_id = self._get_account_id()
25
+
26
+ # Collect instances
27
+ resources.extend(self._collect_instances(account_id))
28
+
29
+ # Collect volumes
30
+ resources.extend(self._collect_volumes(account_id))
31
+
32
+ # Collect VPCs
33
+ resources.extend(self._collect_vpcs(account_id))
34
+
35
+ # Collect security groups
36
+ resources.extend(self._collect_security_groups(account_id))
37
+
38
+ # Collect subnets
39
+ resources.extend(self._collect_subnets(account_id))
40
+
41
+ self.logger.debug(f"Collected {len(resources)} EC2 resources in {self.region}")
42
+ return resources
43
+
44
+ def _collect_instances(self, account_id: str) -> List[Resource]:
45
+ """Collect EC2 instances."""
46
+ resources = []
47
+ client = self._create_client()
48
+
49
+ try:
50
+ paginator = client.get_paginator("describe_instances")
51
+ for page in paginator.paginate():
52
+ for reservation in page["Reservations"]:
53
+ for instance in reservation["Instances"]:
54
+ instance_id = instance["InstanceId"]
55
+
56
+ # Extract tags
57
+ tags = {}
58
+ for tag in instance.get("Tags", []):
59
+ tags[tag["Key"]] = tag["Value"]
60
+
61
+ # Get instance name from tags
62
+ name = tags.get("Name", instance_id)
63
+
64
+ # Build ARN
65
+ arn = f"arn:aws:ec2:{self.region}:{account_id}:instance/{instance_id}"
66
+
67
+ # Create resource
68
+ resource = Resource(
69
+ arn=arn,
70
+ resource_type="AWS::EC2::Instance",
71
+ name=name,
72
+ region=self.region,
73
+ tags=tags,
74
+ config_hash=compute_config_hash(instance),
75
+ created_at=instance.get("LaunchTime"),
76
+ raw_config=instance,
77
+ )
78
+ resources.append(resource)
79
+
80
+ except Exception as e:
81
+ self.logger.error(f"Error collecting EC2 instances in {self.region}: {e}")
82
+
83
+ return resources
84
+
85
+ def _collect_volumes(self, account_id: str) -> List[Resource]:
86
+ """Collect EBS volumes."""
87
+ resources = []
88
+ client = self._create_client()
89
+
90
+ try:
91
+ paginator = client.get_paginator("describe_volumes")
92
+ for page in paginator.paginate():
93
+ for volume in page["Volumes"]:
94
+ volume_id = volume["VolumeId"]
95
+
96
+ # Extract tags
97
+ tags = {}
98
+ for tag in volume.get("Tags", []):
99
+ tags[tag["Key"]] = tag["Value"]
100
+
101
+ # Get volume name from tags
102
+ name = tags.get("Name", volume_id)
103
+
104
+ # Build ARN
105
+ arn = f"arn:aws:ec2:{self.region}:{account_id}:volume/{volume_id}"
106
+
107
+ # Create resource
108
+ resource = Resource(
109
+ arn=arn,
110
+ resource_type="AWS::EC2::Volume",
111
+ name=name,
112
+ region=self.region,
113
+ tags=tags,
114
+ config_hash=compute_config_hash(volume),
115
+ created_at=volume.get("CreateTime"),
116
+ raw_config=volume,
117
+ )
118
+ resources.append(resource)
119
+
120
+ except Exception as e:
121
+ self.logger.error(f"Error collecting EBS volumes in {self.region}: {e}")
122
+
123
+ return resources
124
+
125
+ def _collect_vpcs(self, account_id: str) -> List[Resource]:
126
+ """Collect VPCs."""
127
+ resources = []
128
+ client = self._create_client()
129
+
130
+ try:
131
+ response = client.describe_vpcs()
132
+ for vpc in response["Vpcs"]:
133
+ vpc_id = vpc["VpcId"]
134
+
135
+ # Extract tags
136
+ tags = {}
137
+ for tag in vpc.get("Tags", []):
138
+ tags[tag["Key"]] = tag["Value"]
139
+
140
+ # Get VPC name from tags
141
+ name = tags.get("Name", vpc_id)
142
+
143
+ # Build ARN
144
+ arn = f"arn:aws:ec2:{self.region}:{account_id}:vpc/{vpc_id}"
145
+
146
+ # Create resource
147
+ resource = Resource(
148
+ arn=arn,
149
+ resource_type="AWS::EC2::VPC",
150
+ name=name,
151
+ region=self.region,
152
+ tags=tags,
153
+ config_hash=compute_config_hash(vpc),
154
+ created_at=None, # VPCs don't have creation timestamp
155
+ raw_config=vpc,
156
+ )
157
+ resources.append(resource)
158
+
159
+ except Exception as e:
160
+ self.logger.error(f"Error collecting VPCs in {self.region}: {e}")
161
+
162
+ return resources
163
+
164
+ def _collect_security_groups(self, account_id: str) -> List[Resource]:
165
+ """Collect security groups."""
166
+ resources = []
167
+ client = self._create_client()
168
+
169
+ try:
170
+ paginator = client.get_paginator("describe_security_groups")
171
+ for page in paginator.paginate():
172
+ for sg in page["SecurityGroups"]:
173
+ sg_id = sg["GroupId"]
174
+ sg_name = sg["GroupName"]
175
+
176
+ # Extract tags
177
+ tags = {}
178
+ for tag in sg.get("Tags", []):
179
+ tags[tag["Key"]] = tag["Value"]
180
+
181
+ # Build ARN
182
+ arn = f"arn:aws:ec2:{self.region}:{account_id}:security-group/{sg_id}"
183
+
184
+ # Create resource
185
+ resource = Resource(
186
+ arn=arn,
187
+ resource_type="AWS::EC2::SecurityGroup",
188
+ name=sg_name,
189
+ region=self.region,
190
+ tags=tags,
191
+ config_hash=compute_config_hash(sg),
192
+ created_at=None, # Security groups don't have creation timestamp
193
+ raw_config=sg,
194
+ )
195
+ resources.append(resource)
196
+
197
+ except Exception as e:
198
+ self.logger.error(f"Error collecting security groups in {self.region}: {e}")
199
+
200
+ return resources
201
+
202
+ def _collect_subnets(self, account_id: str) -> List[Resource]:
203
+ """Collect subnets."""
204
+ resources = []
205
+ client = self._create_client()
206
+
207
+ try:
208
+ paginator = client.get_paginator("describe_subnets")
209
+ for page in paginator.paginate():
210
+ for subnet in page["Subnets"]:
211
+ subnet_id = subnet["SubnetId"]
212
+
213
+ # Extract tags
214
+ tags = {}
215
+ for tag in subnet.get("Tags", []):
216
+ tags[tag["Key"]] = tag["Value"]
217
+
218
+ # Get subnet name from tags
219
+ name = tags.get("Name", subnet_id)
220
+
221
+ # Build ARN
222
+ arn = f"arn:aws:ec2:{self.region}:{account_id}:subnet/{subnet_id}"
223
+
224
+ # Create resource
225
+ resource = Resource(
226
+ arn=arn,
227
+ resource_type="AWS::EC2::Subnet",
228
+ name=name,
229
+ region=self.region,
230
+ tags=tags,
231
+ config_hash=compute_config_hash(subnet),
232
+ created_at=None, # Subnets don't have creation timestamp
233
+ raw_config=subnet,
234
+ )
235
+ resources.append(resource)
236
+
237
+ except Exception as e:
238
+ self.logger.error(f"Error collecting subnets in {self.region}: {e}")
239
+
240
+ return resources
@@ -0,0 +1,215 @@
1
+ """ECS resource collector."""
2
+
3
+ from typing import List
4
+
5
+ from ...models.resource import Resource
6
+ from ...utils.hash import compute_config_hash
7
+ from .base import BaseResourceCollector
8
+
9
+
10
+ class ECSCollector(BaseResourceCollector):
11
+ """Collector for Amazon ECS (Elastic Container Service) resources."""
12
+
13
+ @property
14
+ def service_name(self) -> str:
15
+ return "ecs"
16
+
17
+ def collect(self) -> List[Resource]:
18
+ """Collect ECS resources.
19
+
20
+ Collects:
21
+ - ECS Clusters
22
+ - ECS Services (within each cluster)
23
+ - ECS Task Definitions (active revisions)
24
+
25
+ Returns:
26
+ List of ECS resources
27
+ """
28
+ resources = []
29
+
30
+ # Collect clusters
31
+ resources.extend(self._collect_clusters())
32
+
33
+ # Collect services (across all clusters)
34
+ resources.extend(self._collect_services())
35
+
36
+ # Collect task definitions
37
+ resources.extend(self._collect_task_definitions())
38
+
39
+ self.logger.debug(f"Collected {len(resources)} ECS resources in {self.region}")
40
+ return resources
41
+
42
+ def _collect_clusters(self) -> List[Resource]:
43
+ """Collect ECS clusters.
44
+
45
+ Returns:
46
+ List of ECS cluster resources
47
+ """
48
+ resources = []
49
+ client = self._create_client()
50
+
51
+ try:
52
+ paginator = client.get_paginator("list_clusters")
53
+ for page in paginator.paginate():
54
+ cluster_arns = page.get("clusterArns", [])
55
+
56
+ if not cluster_arns:
57
+ continue
58
+
59
+ # Get detailed info for clusters (in batches of 100)
60
+ for i in range(0, len(cluster_arns), 100):
61
+ batch = cluster_arns[i : i + 100]
62
+ try:
63
+ response = client.describe_clusters(clusters=batch, include=["TAGS"])
64
+ for cluster in response.get("clusters", []):
65
+ cluster_name = cluster["clusterName"]
66
+ cluster_arn = cluster["clusterArn"]
67
+
68
+ # Extract tags
69
+ tags = {}
70
+ for tag in cluster.get("tags", []):
71
+ tags[tag["key"]] = tag["value"]
72
+
73
+ # Extract creation timestamp (not always available)
74
+ created_at = None
75
+
76
+ # Create resource
77
+ resource = Resource(
78
+ arn=cluster_arn,
79
+ resource_type="AWS::ECS::Cluster",
80
+ name=cluster_name,
81
+ region=self.region,
82
+ tags=tags,
83
+ config_hash=compute_config_hash(cluster),
84
+ created_at=created_at,
85
+ raw_config=cluster,
86
+ )
87
+ resources.append(resource)
88
+
89
+ except Exception as e:
90
+ self.logger.debug(f"Error describing cluster batch: {e}")
91
+
92
+ except Exception as e:
93
+ self.logger.error(f"Error collecting ECS clusters in {self.region}: {e}")
94
+
95
+ return resources
96
+
97
+ def _collect_services(self) -> List[Resource]:
98
+ """Collect ECS services across all clusters.
99
+
100
+ Returns:
101
+ List of ECS service resources
102
+ """
103
+ resources = []
104
+ client = self._create_client()
105
+
106
+ try:
107
+ # First, get all clusters
108
+ cluster_arns = []
109
+ paginator = client.get_paginator("list_clusters")
110
+ for page in paginator.paginate():
111
+ cluster_arns.extend(page.get("clusterArns", []))
112
+
113
+ # Collect services from each cluster
114
+ for cluster_arn in cluster_arns:
115
+ try:
116
+ service_paginator = client.get_paginator("list_services")
117
+ service_arns = []
118
+ for page in service_paginator.paginate(cluster=cluster_arn):
119
+ service_arns.extend(page.get("serviceArns", []))
120
+
121
+ if not service_arns:
122
+ continue
123
+
124
+ # Get detailed info for services (in batches of 10)
125
+ for i in range(0, len(service_arns), 10):
126
+ batch = service_arns[i : i + 10]
127
+ try:
128
+ response = client.describe_services(cluster=cluster_arn, services=batch, include=["TAGS"])
129
+ for service in response.get("services", []):
130
+ service_name = service["serviceName"]
131
+ service_arn = service["serviceArn"]
132
+
133
+ # Extract tags
134
+ tags = {}
135
+ for tag in service.get("tags", []):
136
+ tags[tag["key"]] = tag["value"]
137
+
138
+ # Extract creation date
139
+ created_at = service.get("createdAt")
140
+
141
+ # Create resource
142
+ resource = Resource(
143
+ arn=service_arn,
144
+ resource_type="AWS::ECS::Service",
145
+ name=service_name,
146
+ region=self.region,
147
+ tags=tags,
148
+ config_hash=compute_config_hash(service),
149
+ created_at=created_at,
150
+ raw_config=service,
151
+ )
152
+ resources.append(resource)
153
+
154
+ except Exception as e:
155
+ self.logger.debug(f"Error describing service batch: {e}")
156
+
157
+ except Exception as e:
158
+ self.logger.debug(f"Error collecting services from cluster {cluster_arn}: {e}")
159
+
160
+ except Exception as e:
161
+ self.logger.error(f"Error collecting ECS services in {self.region}: {e}")
162
+
163
+ return resources
164
+
165
+ def _collect_task_definitions(self) -> List[Resource]:
166
+ """Collect ECS task definitions (active revisions only).
167
+
168
+ Returns:
169
+ List of ECS task definition resources
170
+ """
171
+ resources = []
172
+ client = self._create_client()
173
+
174
+ try:
175
+ paginator = client.get_paginator("list_task_definitions")
176
+ for page in paginator.paginate(status="ACTIVE"):
177
+ for task_def_arn in page.get("taskDefinitionArns", []):
178
+ try:
179
+ # Get task definition details
180
+ response = client.describe_task_definition(taskDefinition=task_def_arn, include=["TAGS"])
181
+ task_def = response.get("taskDefinition", {})
182
+
183
+ # Extract family and revision
184
+ family = task_def.get("family", "unknown")
185
+ revision = task_def.get("revision", 0)
186
+ name = f"{family}:{revision}"
187
+
188
+ # Extract tags
189
+ tags = {}
190
+ for tag in response.get("tags", []):
191
+ tags[tag["key"]] = tag["value"]
192
+
193
+ # Task definitions don't have creation timestamp
194
+ created_at = None
195
+
196
+ # Create resource
197
+ resource = Resource(
198
+ arn=task_def_arn,
199
+ resource_type="AWS::ECS::TaskDefinition",
200
+ name=name,
201
+ region=self.region,
202
+ tags=tags,
203
+ config_hash=compute_config_hash(task_def),
204
+ created_at=created_at,
205
+ raw_config=task_def,
206
+ )
207
+ resources.append(resource)
208
+
209
+ except Exception as e:
210
+ self.logger.debug(f"Error describing task definition {task_def_arn}: {e}")
211
+
212
+ except Exception as e:
213
+ self.logger.error(f"Error collecting ECS task definitions in {self.region}: {e}")
214
+
215
+ return resources
@@ -0,0 +1,102 @@
1
+ """EFS resource collector."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List
6
+
7
+ from botocore.exceptions import ClientError
8
+
9
+ from ...models.efs_resource import EFSFileSystem
10
+ from ...models.resource import Resource
11
+ from .base import BaseResourceCollector
12
+
13
+
14
+ class EFSCollector(BaseResourceCollector):
15
+ """Collector for AWS EFS (Elastic File System) resources."""
16
+
17
+ @property
18
+ def service_name(self) -> str:
19
+ return "efs"
20
+
21
+ @property
22
+ def is_global_service(self) -> bool:
23
+ return False
24
+
25
+ def collect(self) -> List[Resource]:
26
+ """Collect EFS file systems.
27
+
28
+ Returns:
29
+ List of EFS file system resources
30
+ """
31
+ resources = []
32
+
33
+ try:
34
+ client = self._create_client()
35
+
36
+ # Use paginator for describe_file_systems
37
+ paginator = client.get_paginator("describe_file_systems")
38
+
39
+ for page in paginator.paginate():
40
+ for fs in page.get("FileSystems", []):
41
+ try:
42
+ # Extract basic file system information
43
+ file_system_id = fs["FileSystemId"]
44
+ arn = fs["FileSystemArn"]
45
+ created_at = fs.get("CreationTime")
46
+ lifecycle_state = fs.get("LifeCycleState", "unknown")
47
+ performance_mode = fs.get("PerformanceMode", "generalPurpose")
48
+ encrypted = fs.get("Encrypted", False)
49
+ kms_key_id = fs.get("KmsKeyId")
50
+
51
+ # Extract tags
52
+ tags = {}
53
+ for tag in fs.get("Tags", []):
54
+ tags[tag["Key"]] = tag["Value"]
55
+
56
+ # Create EFSFileSystem model
57
+ efs_fs = EFSFileSystem(
58
+ file_system_id=file_system_id,
59
+ arn=arn,
60
+ encryption_enabled=encrypted,
61
+ kms_key_id=kms_key_id,
62
+ performance_mode=performance_mode,
63
+ lifecycle_state=lifecycle_state,
64
+ tags=tags,
65
+ region=self.region,
66
+ created_at=created_at,
67
+ )
68
+
69
+ # Convert to Resource
70
+ resource_dict = efs_fs.to_resource_dict()
71
+ resource = Resource(
72
+ arn=resource_dict["arn"],
73
+ resource_type=resource_dict["resource_type"],
74
+ name=resource_dict["name"],
75
+ region=resource_dict["region"],
76
+ tags=resource_dict["tags"],
77
+ config_hash=resource_dict["config_hash"],
78
+ created_at=resource_dict["created_at"],
79
+ raw_config=resource_dict["raw_config"],
80
+ )
81
+
82
+ resources.append(resource)
83
+
84
+ except Exception as e:
85
+ self.logger.debug(f"Error processing EFS file system {fs.get('FileSystemId', 'unknown')}: {e}")
86
+ continue
87
+
88
+ except ClientError as e:
89
+ error_code = e.response.get("Error", {}).get("Code", "Unknown")
90
+ # Handle regions where EFS is not available or access denied
91
+ if error_code in ["OptInRequired", "AccessDenied", "InvalidAction"]:
92
+ self.logger.debug(f"EFS not available or access denied in {self.region}: {error_code}")
93
+ else:
94
+ self.logger.error(f"Error collecting EFS file systems in {self.region}: {e}")
95
+ return []
96
+
97
+ except Exception as e:
98
+ self.logger.error(f"Error collecting EFS file systems in {self.region}: {e}")
99
+ return []
100
+
101
+ self.logger.debug(f"Collected {len(resources)} EFS file systems in {self.region}")
102
+ return resources