terraback 0.1.5__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.
Files changed (61) hide show
  1. cli/__init__.py +0 -0
  2. cli/aws/__init__.py +3 -0
  3. cli/aws/apigateway/__init__.py +25 -0
  4. cli/aws/apigateway/rest_apis.py +341 -0
  5. cli/aws/ec2/__init__.py +121 -0
  6. cli/aws/ec2/amis.py +42 -0
  7. cli/aws/ec2/instances.py +42 -0
  8. cli/aws/ec2/key_pairs.py +31 -0
  9. cli/aws/ec2/launch_templates.py +34 -0
  10. cli/aws/ec2/network_interfaces.py +34 -0
  11. cli/aws/ec2/snapshots.py +31 -0
  12. cli/aws/ec2/volumes.py +34 -0
  13. cli/aws/eips/__init__.py +35 -0
  14. cli/aws/eips/addresses.py +22 -0
  15. cli/aws/elb/__init__.py +32 -0
  16. cli/aws/elb/classic_load_balancers.py +37 -0
  17. cli/aws/elbv2/__init__.py +72 -0
  18. cli/aws/elbv2/classic_load_balancers.py +37 -0
  19. cli/aws/elbv2/listeners.py +33 -0
  20. cli/aws/elbv2/load_balancers.py +37 -0
  21. cli/aws/elbv2/target_groups.py +34 -0
  22. cli/aws/iam/__init__.py +36 -0
  23. cli/aws/iam/policies.py +39 -0
  24. cli/aws/iam/roles.py +25 -0
  25. cli/aws/lambda_func/__init__.py +53 -0
  26. cli/aws/lambda_func/functions.py +60 -0
  27. cli/aws/lambda_func/layers.py +108 -0
  28. cli/aws/rds/__init__.py +92 -0
  29. cli/aws/rds/instances.py +33 -0
  30. cli/aws/rds/parameter_groups.py +43 -0
  31. cli/aws/rds/subnet_groups.py +33 -0
  32. cli/aws/route53/__init__.py +51 -0
  33. cli/aws/route53/records.py +73 -0
  34. cli/aws/route53/zones.py +34 -0
  35. cli/aws/s3/__init__.py +33 -0
  36. cli/aws/s3/buckets.py +54 -0
  37. cli/aws/session.py +11 -0
  38. cli/aws/vpc/__init__.py +77 -0
  39. cli/aws/vpc/security_groups.py +24 -0
  40. cli/aws/vpc/subnets.py +21 -0
  41. cli/aws/vpc/vpcs.py +21 -0
  42. cli/commands/__init__.py +1 -0
  43. cli/commands/analyse.py +126 -0
  44. cli/commands/clean.py +49 -0
  45. cli/commands/list.py +54 -0
  46. cli/main.py +62 -0
  47. templates/__init__.py +0 -0
  48. terraback-0.1.5.dist-info/METADATA +159 -0
  49. terraback-0.1.5.dist-info/RECORD +61 -0
  50. terraback-0.1.5.dist-info/WHEEL +5 -0
  51. terraback-0.1.5.dist-info/entry_points.txt +2 -0
  52. terraback-0.1.5.dist-info/licenses/LICENSE +66 -0
  53. terraback-0.1.5.dist-info/top_level.txt +4 -0
  54. terraform_generator/__init__.py +1 -0
  55. terraform_generator/imports.py +57 -0
  56. terraform_generator/writer.py +15 -0
  57. tests/__init__.py +0 -0
  58. tests/integration/__init__.py +0 -0
  59. tests/integration/test_vpc_scanner.py +74 -0
  60. tests/unit/__init__.py +0 -0
  61. tests/unit/test_cross_scan_registry.py +102 -0
cli/__init__.py ADDED
File without changes
cli/aws/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ # This file intentionally left blank.
2
+ # Its presence makes the 'aws' directory a Python package,
3
+ # allowing for the modular imports used in main.py.
@@ -0,0 +1,25 @@
1
+ import typer
2
+ from pathlib import Path
3
+ from .rest_apis import scan_rest_apis
4
+
5
+ from terraback.utils.cross_scan_registry import register_scan_function, cross_scan_registry
6
+
7
+ app = typer.Typer(
8
+ name="apigateway",
9
+ help="Manage API Gateway REST API resources.",
10
+ no_args_is_help=True
11
+ )
12
+
13
+ @app.command(name="scan-rest-apis", help="Scan REST APIs and all their sub-resources.")
14
+ def scan_apis_command(output_dir: Path = typer.Option("generated"), profile: str = typer.Option(None), region: str = typer.Option("us-east-1")):
15
+ scan_rest_apis(output_dir, profile, region)
16
+
17
+ # Note: list and import commands are omitted for now due to the complexity of nested resources.
18
+
19
+ def register():
20
+ """Registers scan functions and dependencies for the API Gateway module."""
21
+ register_scan_function("api_gateway_rest_api", scan_rest_apis)
22
+
23
+ # Define dependencies
24
+ cross_scan_registry.register_dependency("api_gateway_integration", "lambda_function")
25
+ cross_scan_registry.register_dependency("lambda_function", "api_gateway_rest_api")
@@ -0,0 +1,341 @@
1
+ from pathlib import Path
2
+ from typing import List, Dict, Any, Tuple, Optional
3
+ import concurrent.futures
4
+ from dataclasses import dataclass, field
5
+ from terraback.cli.aws.session import get_boto_session
6
+ from terraback.terraform_generator.writer import generate_tf
7
+ from terraback.terraform_generator.imports import generate_imports_file
8
+ from terraback.utils.importer import ImportManager
9
+
10
+
11
+ @dataclass
12
+ class ApiGatewayResources:
13
+ """Container for all API Gateway resources discovered during scanning."""
14
+ apis: List[Dict[str, Any]] = field(default_factory=list)
15
+ resources: List[Dict[str, Any]] = field(default_factory=list)
16
+ methods: List[Dict[str, Any]] = field(default_factory=list)
17
+ integrations: List[Dict[str, Any]] = field(default_factory=list)
18
+ deployments: List[Dict[str, Any]] = field(default_factory=list)
19
+ stages: List[Dict[str, Any]] = field(default_factory=list)
20
+ lambda_permissions: List[Dict[str, Any]] = field(default_factory=list)
21
+
22
+ def extend(self, other: 'ApiGatewayResources'):
23
+ """Extend this container with resources from another container."""
24
+ self.apis.extend(other.apis)
25
+ self.resources.extend(other.resources)
26
+ self.methods.extend(other.methods)
27
+ self.integrations.extend(other.integrations)
28
+ self.deployments.extend(other.deployments)
29
+ self.stages.extend(other.stages)
30
+ self.lambda_permissions.extend(other.lambda_permissions)
31
+
32
+
33
+ class ApiGatewayScanner:
34
+ """Scanner for API Gateway resources with optimized processing."""
35
+
36
+ def __init__(self, apigw_client, region: str):
37
+ self.client = apigw_client
38
+ self.region = region
39
+
40
+ def get_all_apis(self) -> List[Dict[str, Any]]:
41
+ """Fetch all REST APIs efficiently."""
42
+ apis = []
43
+ paginator = self.client.get_paginator('get_rest_apis')
44
+ for page in paginator.paginate():
45
+ apis.extend(page.get('items', []))
46
+ return apis
47
+
48
+ def get_api_resources(self, api_id: str) -> List[Dict[str, Any]]:
49
+ """Fetch all resources for an API with proper ID mapping."""
50
+ resources = []
51
+ paginator = self.client.get_paginator('get_resources')
52
+
53
+ for page in paginator.paginate(restApiId=api_id):
54
+ for resource in page.get('items', []):
55
+ resource['restApiId'] = api_id # Add for composite keys
56
+ resources.append(resource)
57
+
58
+ return resources
59
+
60
+ def get_deployments_and_stages(self, api_id: str) -> Tuple[List[Dict], List[Dict]]:
61
+ """Fetch deployments and stages with proper ID mapping."""
62
+ try:
63
+ # Get deployments
64
+ deployments_response = self.client.get_deployments(restApiId=api_id)
65
+ deployments = [
66
+ {**dep, 'restApiId': api_id}
67
+ for dep in deployments_response.get('items', [])
68
+ ]
69
+
70
+ # Get stages
71
+ stages_response = self.client.get_stages(restApiId=api_id)
72
+ stages = [
73
+ {**stage, 'restApiId': api_id}
74
+ for stage in stages_response.get('items', [])
75
+ ]
76
+
77
+ return deployments, stages
78
+
79
+ except Exception as e:
80
+ print(f" - Warning: Could not fetch deployments/stages for API {api_id}: {e}")
81
+ return [], []
82
+
83
+ def extract_lambda_function_name(self, uri: str) -> Optional[str]:
84
+ """Extract Lambda function name from integration URI."""
85
+ try:
86
+ if 'lambda' not in uri:
87
+ return None
88
+ return uri.split(':')[-2].split('/')[-1]
89
+ except (IndexError, AttributeError):
90
+ return None
91
+
92
+ def process_method_integration(self, api_id: str, resource_id: str,
93
+ method_name: str, method_details: Dict) -> Tuple[Dict, Optional[Dict], Optional[Dict]]:
94
+ """Process a single method and its integration."""
95
+ # Build method info
96
+ method_info = {
97
+ "rest_api_id": api_id,
98
+ "resource_id": resource_id,
99
+ "http_method": method_name,
100
+ **method_details
101
+ }
102
+
103
+ integration_info = None
104
+ lambda_permission_info = None
105
+
106
+ # Process integration if present
107
+ integration_data = method_details.get('methodIntegration')
108
+ if integration_data:
109
+ integration_info = {
110
+ "rest_api_id": api_id,
111
+ "resource_id": resource_id,
112
+ "http_method": method_name,
113
+ **integration_data
114
+ }
115
+
116
+ # Create Lambda permission for Lambda integrations
117
+ if (integration_data.get('type') == 'AWS_PROXY' and
118
+ integration_data.get('uri')):
119
+
120
+ function_name = self.extract_lambda_function_name(integration_data['uri'])
121
+ if function_name:
122
+ lambda_permission_info = {
123
+ "rest_api_id": api_id,
124
+ "function_name": function_name,
125
+ "statement_id": f"AllowAPIGatewayInvoke{api_id}{resource_id}{method_name}"
126
+ }
127
+
128
+ return method_info, integration_info, lambda_permission_info
129
+
130
+ def process_resource_methods(self, api_id: str, resource: Dict[str, Any]) -> Tuple[List[Dict], List[Dict], List[Dict]]:
131
+ """Process all methods for a single resource."""
132
+ methods, integrations, lambda_permissions = [], [], []
133
+ resource_id = resource['id']
134
+
135
+ for method_name, method_details in resource.get('resourceMethods', {}).items():
136
+ method, integration, lambda_perm = self.process_method_integration(
137
+ api_id, resource_id, method_name, method_details
138
+ )
139
+
140
+ methods.append(method)
141
+ if integration:
142
+ integrations.append(integration)
143
+ if lambda_perm:
144
+ lambda_permissions.append(lambda_perm)
145
+
146
+ return methods, integrations, lambda_permissions
147
+
148
+ def scan_single_api(self, api: Dict[str, Any]) -> ApiGatewayResources:
149
+ """Scan a single API and return all its resources."""
150
+ api_id = api['id']
151
+ api_name = api.get('name', 'Unknown')
152
+
153
+ print(f" - Processing API: {api_name} ({api_id})")
154
+
155
+ resources = ApiGatewayResources(apis=[api])
156
+
157
+ # Get API resources
158
+ api_resources = self.get_api_resources(api_id)
159
+ resources.resources = api_resources
160
+
161
+ # Process methods and integrations
162
+ for resource in api_resources:
163
+ methods, integrations, lambda_permissions = self.process_resource_methods(api_id, resource)
164
+ resources.methods.extend(methods)
165
+ resources.integrations.extend(integrations)
166
+ resources.lambda_permissions.extend(lambda_permissions)
167
+
168
+ # Get deployments and stages
169
+ deployments, stages = self.get_deployments_and_stages(api_id)
170
+ resources.deployments = deployments
171
+ resources.stages = stages
172
+
173
+ return resources
174
+
175
+
176
+ def scan_apis_parallel(scanner: ApiGatewayScanner, apis: List[Dict[str, Any]], max_workers: int = 5) -> ApiGatewayResources:
177
+ """Process multiple APIs in parallel using the scanner."""
178
+ combined_resources = ApiGatewayResources()
179
+
180
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
181
+ # Submit all API processing tasks
182
+ future_to_api = {
183
+ executor.submit(scanner.scan_single_api, api): api
184
+ for api in apis
185
+ }
186
+
187
+ # Collect results as they complete
188
+ for future in concurrent.futures.as_completed(future_to_api):
189
+ api = future_to_api[future]
190
+ try:
191
+ api_resources = future.result()
192
+ combined_resources.extend(api_resources)
193
+ except Exception as exc:
194
+ api_name = api.get('name', 'Unknown')
195
+ print(f" - Error processing API {api_name}: {exc}")
196
+
197
+ return combined_resources
198
+
199
+
200
+ # Configuration for Terraform file generation
201
+ TERRAFORM_CONFIGS = [
202
+ ("apis", "api_gateway_rest_api", "id", None),
203
+ ("resources", "api_gateway_resource", "id", ['restApiId', 'id']),
204
+ ("methods", "api_gateway_method", "http_method", ['rest_api_id', 'resource_id', 'http_method']),
205
+ ("integrations", "api_gateway_integration", "http_method", ['rest_api_id', 'resource_id', 'http_method']),
206
+ ("deployments", "api_gateway_deployment", "id", ['restApiId', 'id']),
207
+ ("stages", "api_gateway_stage", "stageName", ['restApiId', 'stageName']),
208
+ ("lambda_permissions", "lambda_permission", None, None) # No direct import
209
+ ]
210
+
211
+
212
+ def generate_terraform_files(resources: ApiGatewayResources, output_dir: Path):
213
+ """Generate all Terraform files and import files efficiently."""
214
+ for attr_name, resource_type, id_key, composite_keys in TERRAFORM_CONFIGS:
215
+ resource_list = getattr(resources, attr_name)
216
+
217
+ if not resource_list:
218
+ continue
219
+
220
+ output_file = output_dir / f"{resource_type}.tf"
221
+ generate_tf(resource_list, resource_type, output_file)
222
+
223
+ # Generate imports file if we have an ID key
224
+ if id_key:
225
+ generate_imports_file(
226
+ resource_type,
227
+ resource_list,
228
+ id_key,
229
+ output_dir,
230
+ composite_keys=composite_keys
231
+ )
232
+
233
+ resource_name = resource_type.replace('_', ' ').title()
234
+ print(f"Generated Terraform for {len(resource_list)} {resource_name}s.")
235
+
236
+
237
+ def print_scan_summary(resources: ApiGatewayResources):
238
+ """Print a summary of the scan results."""
239
+ print(f"\nScan complete! Summary:")
240
+ print(f" - APIs: {len(resources.apis)}")
241
+ print(f" - Resources: {len(resources.resources)}")
242
+ print(f" - Methods: {len(resources.methods)}")
243
+ print(f" - Integrations: {len(resources.integrations)}")
244
+ print(f" - Deployments: {len(resources.deployments)}")
245
+ print(f" - Stages: {len(resources.stages)}")
246
+ print(f" - Lambda Permissions: {len(resources.lambda_permissions)}")
247
+
248
+
249
+ def scan_rest_apis(output_dir: Path, profile: str = None, region: str = "us-east-1", max_workers: int = 5):
250
+ """
251
+ Scan API Gateway REST APIs and generate Terraform code efficiently.
252
+
253
+ Args:
254
+ output_dir: Directory to write Terraform files
255
+ profile: AWS profile to use
256
+ region: AWS region to scan
257
+ max_workers: Maximum number of parallel workers for API processing
258
+ """
259
+ boto_session = get_boto_session(profile, region)
260
+ apigw_client = boto_session.client("apigateway")
261
+
262
+ print(f"Scanning for API Gateway REST APIs in region {region}...")
263
+
264
+ # Initialize scanner
265
+ scanner = ApiGatewayScanner(apigw_client, region)
266
+
267
+ # Get all APIs
268
+ apis = scanner.get_all_apis()
269
+ if not apis:
270
+ print("No REST APIs found in the region")
271
+ return
272
+
273
+ print(f"Found {len(apis)} APIs to process")
274
+
275
+ # Process APIs in parallel
276
+ all_resources = scan_apis_parallel(scanner, apis, max_workers)
277
+
278
+ # Generate Terraform files
279
+ generate_terraform_files(all_resources, output_dir)
280
+
281
+ # Print summary
282
+ print_scan_summary(all_resources)
283
+
284
+
285
+ # List and import functions
286
+ def list_rest_apis(output_dir: Path):
287
+ """Lists all API Gateway REST API resources previously generated."""
288
+ ImportManager(output_dir, "api_gateway_rest_api").list_all()
289
+
290
+
291
+ def import_rest_api(api_id: str, output_dir: Path):
292
+ """Runs terraform import for a specific REST API by its ID."""
293
+ ImportManager(output_dir, "api_gateway_rest_api").find_and_import(api_id)
294
+
295
+
296
+ def list_api_gateway_resources(output_dir: Path):
297
+ """Lists all API Gateway Resource resources previously generated."""
298
+ ImportManager(output_dir, "api_gateway_resource").list_all()
299
+
300
+
301
+ def list_api_gateway_methods(output_dir: Path):
302
+ """Lists all API Gateway Method resources previously generated."""
303
+ ImportManager(output_dir, "api_gateway_method").list_all()
304
+
305
+
306
+ def list_api_gateway_integrations(output_dir: Path):
307
+ """Lists all API Gateway Integration resources previously generated."""
308
+ ImportManager(output_dir, "api_gateway_integration").list_all()
309
+
310
+
311
+ def list_api_gateway_stages(output_dir: Path):
312
+ """Lists all API Gateway Stage resources previously generated."""
313
+ ImportManager(output_dir, "api_gateway_stage").list_all()
314
+
315
+
316
+ def import_api_gateway_method(composite_id: str, output_dir: Path):
317
+ """
318
+ Runs terraform import for a specific API Gateway Method.
319
+ composite_id format: "rest_api_id/resource_id/http_method"
320
+ """
321
+ ImportManager(output_dir, "api_gateway_method").find_and_import(composite_id)
322
+
323
+
324
+ def import_api_gateway_integration(composite_id: str, output_dir: Path):
325
+ """
326
+ Runs terraform import for a specific API Gateway Integration.
327
+ composite_id format: "rest_api_id/resource_id/http_method"
328
+ """
329
+ ImportManager(output_dir, "api_gateway_integration").find_and_import(composite_id)
330
+
331
+
332
+ def import_api_gateway_stage(composite_id: str, output_dir: Path):
333
+ """
334
+ Runs terraform import for a specific API Gateway Stage.
335
+ composite_id format: "rest_api_id/stage_name"
336
+ """
337
+ ImportManager(output_dir, "api_gateway_stage").find_and_import(composite_id)
338
+
339
+
340
+ # Note: Sub-resource imports require composite keys (e.g., "api_id/resource_id/method").
341
+ # The generated import files contain the correct format for each resource type.
@@ -0,0 +1,121 @@
1
+ import typer
2
+ from pathlib import Path
3
+ from functools import partial
4
+
5
+ # Import the simplified scan functions and other commands
6
+ from .instances import scan_ec2, list_ec2, import_ec2
7
+ from .volumes import scan_volumes, list_volumes, import_volume
8
+ from .amis import scan_amis, list_amis, import_amis
9
+ from .key_pairs import scan_key_pairs, list_key_pairs, import_key_pairs
10
+ from .launch_templates import scan_launch_templates, list_launch_templates, import_launch_template
11
+ from .network_interfaces import scan_network_interfaces, list_network_interfaces, import_network_interfaces
12
+ from .snapshots import scan_snapshots, list_snapshots, import_snapshot # <-- ADD THIS LINE
13
+
14
+ # Import registry utilities
15
+ from terraback.utils.cross_scan_registry import (
16
+ register_scan_function,
17
+ cross_scan_registry,
18
+ recursive_scan
19
+ )
20
+
21
+ app = typer.Typer(
22
+ name="ec2",
23
+ help="Manage EC2 resources like instances, volumes, and amis.",
24
+ no_args_is_help=True
25
+ )
26
+
27
+ # --- Instance Commands ---
28
+ @app.command(name="scan", help="Scan EC2 instances and generate Terraform code.")
29
+ def scan_instances_command(
30
+ output_dir: Path = typer.Option("generated", help="Directory to save Terraform files"),
31
+ profile: str = typer.Option(None, help="AWS CLI profile to use"),
32
+ region: str = typer.Option("us-east-1", help="AWS region"),
33
+ include_all_states: bool = typer.Option(False, help="Include all instance states"),
34
+ with_deps: bool = typer.Option(False, help="Recursively scan dependencies")
35
+ ):
36
+ if with_deps:
37
+ recursive_scan("ec2", output_dir=output_dir, profile=profile, region=region, include_all_states=include_all_states)
38
+ else:
39
+ scan_ec2(
40
+ output_dir=output_dir,
41
+ profile=profile,
42
+ region=region,
43
+ include_all_states=include_all_states
44
+ )
45
+
46
+ @app.command(name="list", help="List all EC2 instance resources previously generated.")
47
+ def list_instances_command(output_dir: Path = typer.Option("generated", help="Directory containing import file")):
48
+ list_ec2(output_dir=output_dir)
49
+
50
+ @app.command(name="import", help="Run terraform import for a specific EC2 instance by its ID.")
51
+ def import_instance_command(instance_id: str, output_dir: Path = typer.Option("generated", help="Directory with import file")):
52
+ import_ec2(instance_id=instance_id, output_dir=output_dir)
53
+
54
+
55
+ # --- Snapshot Commands (NEW) ---
56
+ @app.command(name="scan-snapshots", help="Scan EBS snapshots and generate Terraform code.")
57
+ def scan_snapshots_command(
58
+ output_dir: Path = typer.Option("generated", help="Directory to save Terraform files"),
59
+ profile: str = typer.Option(None, help="AWS CLI profile to use"),
60
+ region: str = typer.Option("us-east-1", help="AWS region"),
61
+ ):
62
+ scan_snapshots(output_dir=output_dir, profile=profile, region=region)
63
+
64
+ @app.command(name="list-snapshots", help="List all EBS snapshot resources previously generated.")
65
+ def list_snapshots_command(output_dir: Path = typer.Option("generated", help="Directory containing import file")):
66
+ list_snapshots(output_dir=output_dir)
67
+
68
+ @app.command(name="import-snapshot", help="Run terraform import for a specific EBS snapshot by its ID.")
69
+ def import_snapshot_command(
70
+ snapshot_id: str,
71
+ output_dir: Path = typer.Option("generated", help="Directory with import file")
72
+ ):
73
+ import_snapshot(snapshot_id=snapshot_id, output_dir=output_dir)
74
+
75
+
76
+ # --- Add other resource commands (volumes, amis, etc.) back in ---
77
+ # For brevity, I've omitted the other command definitions, but you should have them here.
78
+
79
+
80
+ # --- Self-Registration Function ---
81
+ def register():
82
+ """Registers the scan functions and dependencies for the EC2 module."""
83
+
84
+ # EC2 Instances
85
+ scan_ec2_core = partial(scan_ec2, include_all_states=True)
86
+ register_scan_function("ec2", scan_ec2_core)
87
+ cross_scan_registry.register_dependency("ec2", "security_groups")
88
+ cross_scan_registry.register_dependency("ec2", "subnets")
89
+ cross_scan_registry.register_dependency("ec2", "iam_roles")
90
+ cross_scan_registry.register_dependency("ec2", "amis")
91
+ cross_scan_registry.register_dependency("ec2", "volumes")
92
+ cross_scan_registry.register_dependency("ec2", "key_pairs")
93
+ cross_scan_registry.register_dependency("ec2", "route53_record")
94
+
95
+ # EBS Volumes
96
+ scan_volumes_core = partial(scan_volumes, include_attached_only=False)
97
+ register_scan_function("volumes", scan_volumes_core)
98
+ cross_scan_registry.register_dependency("volumes", "ec2")
99
+ cross_scan_registry.register_dependency("volumes", "ebs_snapshot") # <-- ADD THIS DEPENDENCY
100
+
101
+ # EBS Snapshots (NEW)
102
+ register_scan_function("ebs_snapshot", scan_snapshots)
103
+
104
+ # amis
105
+ scan_amis_core = partial(scan_amis, owned_by_me=True, include_public=False)
106
+ register_scan_function("amis", scan_amis_core)
107
+
108
+ # Key Pairs
109
+ register_scan_function("key_pairs", scan_key_pairs)
110
+
111
+ # Launch Templates
112
+ register_scan_function("launch_template", scan_launch_templates)
113
+ cross_scan_registry.register_dependency("launch_template", "ec2")
114
+ cross_scan_registry.register_dependency("launch_template", "security_groups")
115
+ cross_scan_registry.register_dependency("launch_template", "amis")
116
+
117
+ # Network Interfaces
118
+ scan_eni_core = partial(scan_network_interfaces, attached_only=True)
119
+ register_scan_function("network_interfaces", scan_eni_core)
120
+ cross_scan_registry.register_dependency("network_interfaces", "ec2")
121
+ cross_scan_registry.register_dependency("network_interfaces", "security_groups")
cli/aws/ec2/amis.py ADDED
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+ from terraback.cli.aws.session import get_boto_session
3
+ from terraback.terraform_generator.writer import generate_tf
4
+ from terraback.terraform_generator.imports import generate_imports_file
5
+ from terraback.utils.importer import ImportManager
6
+
7
+ def scan_amis(
8
+ output_dir: Path,
9
+ profile: str = None,
10
+ region: str = "us-east-1",
11
+ amis_id: str = None,
12
+ owned_by_me: bool = True,
13
+ include_public: bool = False
14
+ ):
15
+ boto_session = get_boto_session(profile, region)
16
+ ec2_client = boto_session.client("ec2")
17
+
18
+ filters = []
19
+ owners = []
20
+ if amis_id:
21
+ filters.append({'Name': 'image-id', 'Values': [amis_id]})
22
+ if owned_by_me:
23
+ owners.append('self')
24
+ if include_public:
25
+ owners.append('amazon')
26
+
27
+ if not owners and not amis_id:
28
+ print("Warning: No owners specified for amis scan. Defaulting to 'self'.")
29
+ owners.append('self')
30
+
31
+ amis = ec2_client.describe_images(Owners=owners, Filters=filters)["Images"]
32
+
33
+ output_file = output_dir / "amis.tf"
34
+ generate_tf(amis, "amis", output_file)
35
+ print(f"Generated Terraform for {len(amis)} amis -> {output_file}")
36
+ generate_imports_file("amis", amis, remote_resource_id_key="ImageId", output_dir=output_dir)
37
+
38
+ def list_amis(output_dir: Path):
39
+ ImportManager(output_dir, "amis").list_all()
40
+
41
+ def import_amis(amis_id: str, output_dir: Path):
42
+ ImportManager(output_dir, "amis").find_and_import(amis_id)
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+ from terraback.cli.aws.session import get_boto_session
3
+ from terraback.terraform_generator.writer import generate_tf
4
+ from terraback.terraform_generator.imports import generate_imports_file
5
+ from terraback.utils.importer import ImportManager
6
+
7
+ def scan_ec2(
8
+ output_dir: Path,
9
+ profile: str = None,
10
+ region: str = "us-east-1",
11
+ include_all_states: bool = False
12
+ ):
13
+ boto_session = get_boto_session(profile, region)
14
+ ec2_client = boto_session.client("ec2")
15
+ paginator = ec2_client.get_paginator("describe_instances")
16
+
17
+ terraform_resources = []
18
+
19
+ states_to_include = {
20
+ 'pending', 'running', 'shutting-down',
21
+ 'terminated', 'stopping', 'stopped'
22
+ } if include_all_states else {'running'}
23
+
24
+ print(f"Scanning for EC2 instances in region {region}...")
25
+
26
+ page_iterator = paginator.paginate()
27
+ for page in page_iterator:
28
+ for reservation in page["Reservations"]:
29
+ for instance in reservation["Instances"]:
30
+ if instance['State']['Name'] in states_to_include:
31
+ terraform_resources.append(instance)
32
+
33
+ output_file = output_dir / "ec2.tf"
34
+ generate_tf(terraform_resources, "ec2", output_file)
35
+ print(f"Generated Terraform for {len(terraform_resources)} EC2 instances -> {output_file}")
36
+ generate_imports_file("ec2", terraform_resources, remote_resource_id_key="InstanceId", output_dir=output_dir)
37
+
38
+ def list_ec2(output_dir: Path):
39
+ ImportManager(output_dir, "ec2").list_all()
40
+
41
+ def import_ec2(instance_id: str, output_dir: Path):
42
+ ImportManager(output_dir, "ec2").find_and_import(instance_id)
@@ -0,0 +1,31 @@
1
+ from pathlib import Path
2
+ from terraback.cli.aws.session import get_boto_session
3
+ from terraback.terraform_generator.writer import generate_tf
4
+ from terraback.terraform_generator.imports import generate_imports_file
5
+ from terraback.utils.importer import ImportManager
6
+
7
+ def scan_key_pairs(
8
+ output_dir: Path,
9
+ profile: str = None,
10
+ region: str = "us-east-1",
11
+ key_name: str = None
12
+ ):
13
+ boto_session = get_boto_session(profile, region)
14
+ ec2_client = boto_session.client("ec2")
15
+
16
+ filters = []
17
+ if key_name:
18
+ filters.append({'Name': 'key-name', 'Values': [key_name]})
19
+
20
+ key_pairs = ec2_client.describe_key_pairs(Filters=filters)["KeyPairs"]
21
+
22
+ output_file = output_dir / "key_pairs.tf"
23
+ generate_tf(key_pairs, "key_pairs", output_file)
24
+ print(f"Generated Terraform for {len(key_pairs)} Key Pairs -> {output_file}")
25
+ generate_imports_file("key_pairs", key_pairs, remote_resource_id_key="KeyName", output_dir=output_dir)
26
+
27
+ def list_key_pairs(output_dir: Path):
28
+ ImportManager(output_dir, "key_pairs").list_all()
29
+
30
+ def import_key_pairs(key_name: str, output_dir: Path):
31
+ ImportManager(output_dir, "key_pairs").find_and_import(key_name)
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+ from terraback.cli.aws.session import get_boto_session
3
+ from terraback.terraform_generator.writer import generate_tf
4
+ from terraback.terraform_generator.imports import generate_imports_file
5
+ from terraback.utils.importer import ImportManager
6
+
7
+ def scan_launch_templates(
8
+ output_dir: Path,
9
+ profile: str = None,
10
+ region: str = "us-east-1",
11
+ template_id: str = None,
12
+ template_name: str = None
13
+ ):
14
+ boto_session = get_boto_session(profile, region)
15
+ ec2_client = boto_session.client("ec2")
16
+
17
+ filters = []
18
+ if template_id:
19
+ filters.append({'Name': 'launch-template-id', 'Values': [template_id]})
20
+ if template_name:
21
+ filters.append({'Name': 'launch-template-name', 'Values': [template_name]})
22
+
23
+ launch_templates = ec2_client.describe_launch_templates(Filters=filters)["LaunchTemplates"]
24
+
25
+ output_file = output_dir / "launch_template.tf"
26
+ generate_tf(launch_templates, "launch_template", output_file)
27
+ print(f"Generated Terraform for {len(launch_templates)} Launch Templates -> {output_file}")
28
+ generate_imports_file("launch_template", launch_templates, remote_resource_id_key="LaunchTemplateId", output_dir=output_dir)
29
+
30
+ def list_launch_templates(output_dir: Path):
31
+ ImportManager(output_dir, "launch_template").list_all()
32
+
33
+ def import_launch_template(template_id: str, output_dir: Path):
34
+ ImportManager(output_dir, "launch_template").find_and_import(template_id)