runbooks 1.1.4__py3-none-any.whl → 1.1.6__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 (273) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -15,317 +15,362 @@ import os
15
15
  from datetime import datetime
16
16
  from typing import Any, Dict, List
17
17
 
18
- from .markdown_exporter import MarkdownExporter
19
18
  from runbooks.common.rich_utils import console
20
19
 
20
+ from .markdown_exporter import MarkdownExporter
21
+
22
+
23
+ class VPCCleanupExporter:
24
+ """VPC Cleanup results exporter class."""
25
+
26
+ def __init__(self, output_dir: str = "./"):
27
+ """Initialize VPC cleanup exporter."""
28
+ self.output_dir = output_dir
29
+
30
+ def export_results(self, vpc_result: Any, export_formats: List[str]) -> Dict[str, str]:
31
+ """Export VPC cleanup results in specified formats."""
32
+ return export_vpc_cleanup_results(vpc_result, export_formats, self.output_dir)
33
+
21
34
 
22
35
  def _format_tags_for_display(tags_dict: Dict[str, str]) -> str:
23
36
  """Format tags for display with priority order, emphasizing ownership tags."""
24
37
  if not tags_dict:
25
38
  return "No tags"
26
-
39
+
27
40
  # Enhanced priority keys with focus on ownership and approvals
28
- priority_keys = ['Name', 'Owner', 'BusinessOwner', 'TechnicalOwner', 'Team', 'Contact',
29
- 'Environment', 'Project', 'CostCenter', 'CreatedBy', 'ManagedBy']
41
+ priority_keys = [
42
+ "Name",
43
+ "Owner",
44
+ "BusinessOwner",
45
+ "TechnicalOwner",
46
+ "Team",
47
+ "Contact",
48
+ "Environment",
49
+ "Project",
50
+ "CostCenter",
51
+ "CreatedBy",
52
+ "ManagedBy",
53
+ ]
30
54
  relevant_tags = []
31
-
55
+
32
56
  for key in priority_keys:
33
57
  if key in tags_dict and tags_dict[key]:
34
58
  relevant_tags.append(f"{key}:{tags_dict[key]}")
35
-
59
+
36
60
  # Add CloudFormation/Terraform tags for IaC detection
37
- iac_keys = ['aws:cloudformation:stack-name', 'terraform:module', 'cdktf:stack', 'pulumi:project']
61
+ iac_keys = ["aws:cloudformation:stack-name", "terraform:module", "cdktf:stack", "pulumi:project"]
38
62
  for key in iac_keys:
39
63
  if key in tags_dict and tags_dict[key] and len(relevant_tags) < 6:
40
64
  relevant_tags.append(f"IaC:{tags_dict[key]}")
41
-
65
+
42
66
  # Add other important tags
43
67
  for key, value in tags_dict.items():
44
68
  if key not in priority_keys + iac_keys and value and len(relevant_tags) < 5:
45
69
  relevant_tags.append(f"{key}:{value}")
46
-
70
+
47
71
  return "; ".join(relevant_tags) if relevant_tags else f"({len(tags_dict)} tags)"
48
72
 
49
73
 
50
74
  def export_vpc_cleanup_results(vpc_result: Any, export_formats: List[str], output_dir: str = "./") -> Dict[str, str]:
51
75
  """
52
76
  Export VPC cleanup results in multiple formats.
53
-
77
+
54
78
  Args:
55
79
  vpc_result: VPC cleanup analysis result object
56
80
  export_formats: List of formats to export (markdown, csv, json, pdf)
57
81
  output_dir: Directory to save exported files
58
-
82
+
59
83
  Returns:
60
84
  Dict mapping format to exported filename
61
85
  """
62
86
  results = {}
63
-
87
+
64
88
  # Extract VPC candidates from result - use correct attribute name
65
- vpc_candidates = getattr(vpc_result, 'cleanup_candidates', [])
89
+ vpc_candidates = getattr(vpc_result, "cleanup_candidates", [])
66
90
  if not vpc_candidates:
67
91
  # Fallback to other possible attribute names
68
- vpc_candidates = getattr(vpc_result, 'vpc_candidates', [])
69
-
70
- if 'markdown' in export_formats:
92
+ vpc_candidates = getattr(vpc_result, "vpc_candidates", [])
93
+
94
+ if "markdown" in export_formats:
71
95
  try:
72
96
  exporter = MarkdownExporter()
73
97
  markdown_filename = exporter.export_vpc_analysis_to_file(
74
- vpc_candidates,
75
- filename="vpc-cleanup-candidates.md",
76
- output_dir=output_dir
98
+ vpc_candidates, filename="vpc-cleanup-candidates.md", output_dir=output_dir
77
99
  )
78
- results['markdown'] = markdown_filename
100
+ results["markdown"] = markdown_filename
79
101
  except Exception as e:
80
102
  console.print(f"[yellow]Warning: Markdown export failed: {e}[/yellow]")
81
- results['markdown'] = None
82
-
103
+ results["markdown"] = None
104
+
83
105
  # Real implementations for other formats
84
- if 'csv' in export_formats:
106
+ if "csv" in export_formats:
85
107
  try:
86
108
  csv_filename = _export_vpc_candidates_csv(vpc_candidates, output_dir)
87
- results['csv'] = csv_filename
109
+ results["csv"] = csv_filename
88
110
  except Exception as e:
89
111
  print(f"Warning: CSV export failed: {e}")
90
- results['csv'] = None
91
-
92
- if 'json' in export_formats:
112
+ results["csv"] = None
113
+
114
+ if "json" in export_formats:
93
115
  try:
94
116
  json_filename = _export_vpc_candidates_json(vpc_candidates, output_dir)
95
- results['json'] = json_filename
117
+ results["json"] = json_filename
96
118
  except Exception as e:
97
119
  print(f"Warning: JSON export failed: {e}")
98
- results['json'] = None
99
-
100
- if 'pdf' in export_formats:
120
+ results["json"] = None
121
+
122
+ if "pdf" in export_formats:
101
123
  try:
102
124
  pdf_filename = _export_vpc_candidates_pdf(vpc_candidates, output_dir)
103
- results['pdf'] = pdf_filename
125
+ results["pdf"] = pdf_filename
104
126
  except Exception as e:
105
127
  print(f"Warning: PDF export failed: {e}")
106
- results['pdf'] = None
107
-
128
+ results["pdf"] = None
129
+
108
130
  return results
109
131
 
110
132
 
111
133
  def _export_vpc_candidates_csv(vpc_candidates: List[Any], output_dir: str) -> str:
112
134
  """Export VPC candidates to CSV format with all 15 columns."""
113
135
  filename = os.path.join(output_dir, "vpc-cleanup-candidates.csv")
114
-
136
+
115
137
  # 15-column headers for comprehensive VPC analysis
116
138
  headers = [
117
- "Account_ID", "VPC_ID", "VPC_Name", "CIDR_Block", "Overlapping",
118
- "Is_Default", "ENI_Count", "Tags", "Flow_Logs", "TGW/Peering",
119
- "LBs_Present", "IaC", "Timeline", "Decision", "Owners/Approvals", "Notes"
139
+ "Account_ID",
140
+ "VPC_ID",
141
+ "VPC_Name",
142
+ "CIDR_Block",
143
+ "Overlapping",
144
+ "Is_Default",
145
+ "ENI_Count",
146
+ "Tags",
147
+ "Flow_Logs",
148
+ "TGW/Peering",
149
+ "LBs_Present",
150
+ "IaC",
151
+ "Timeline",
152
+ "Decision",
153
+ "Owners/Approvals",
154
+ "Notes",
120
155
  ]
121
-
122
- with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
156
+
157
+ with open(filename, "w", newline="", encoding="utf-8") as csvfile:
123
158
  writer = csv.writer(csvfile)
124
159
  writer.writerow(headers)
125
-
160
+
126
161
  for candidate in vpc_candidates:
127
162
  # Extract data with enhanced tag and owner handling
128
- tags_dict = getattr(candidate, 'tags', {}) or {}
129
-
163
+ tags_dict = getattr(candidate, "tags", {}) or {}
164
+
130
165
  # Use enhanced tag formatting function
131
166
  tags_str = _format_tags_for_display(tags_dict)
132
-
133
- load_balancers = getattr(candidate, 'load_balancers', []) or []
167
+
168
+ load_balancers = getattr(candidate, "load_balancers", []) or []
134
169
  lbs_present = "Yes" if load_balancers else "No"
135
-
170
+
136
171
  # Enhanced owner extraction from multiple sources
137
- owners = getattr(candidate, 'owners_approvals', []) or []
138
-
172
+ owners = getattr(candidate, "owners_approvals", []) or []
173
+
139
174
  # Extract owners from tags with enhanced logic
140
175
  if not owners and tags_dict:
141
- owner_keys = ['Owner', 'BusinessOwner', 'TechnicalOwner', 'Team', 'Contact', 'CreatedBy', 'ManagedBy']
176
+ owner_keys = ["Owner", "BusinessOwner", "TechnicalOwner", "Team", "Contact", "CreatedBy", "ManagedBy"]
142
177
  for key in owner_keys:
143
178
  if key in tags_dict and tags_dict[key]:
144
179
  value = tags_dict[key]
145
- if 'business' in key.lower() or 'manager' in value.lower():
180
+ if "business" in key.lower() or "manager" in value.lower():
146
181
  owners.append(f"{value} (Business)")
147
- elif 'technical' in key.lower() or 'engineer' in value.lower():
182
+ elif "technical" in key.lower() or "engineer" in value.lower():
148
183
  owners.append(f"{value} (Technical)")
149
- elif 'team' in key.lower():
184
+ elif "team" in key.lower():
150
185
  owners.append(f"{value} (Team)")
151
186
  else:
152
187
  owners.append(f"{value} ({key})")
153
-
188
+
154
189
  # For default VPCs, add system indicator
155
- is_default = getattr(candidate, 'is_default', False)
190
+ is_default = getattr(candidate, "is_default", False)
156
191
  if is_default and not owners:
157
192
  owners.append("System Default")
158
-
193
+
159
194
  if owners:
160
195
  owners_str = "; ".join(owners)
161
196
  else:
162
197
  # Enhanced fallback for CSV
163
- if getattr(candidate, 'is_default', False):
198
+ if getattr(candidate, "is_default", False):
164
199
  owners_str = "System Default VPC"
165
- elif getattr(candidate, 'iac_detected', False):
200
+ elif getattr(candidate, "iac_detected", False):
166
201
  owners_str = "IaC Managed"
167
202
  else:
168
203
  owners_str = "No owner tags found"
169
-
204
+
170
205
  row = [
171
- getattr(candidate, 'account_id', 'Unknown'),
172
- getattr(candidate, 'vpc_id', ''),
173
- getattr(candidate, 'vpc_name', 'Unnamed'),
174
- getattr(candidate, 'cidr_block', ''),
206
+ getattr(candidate, "account_id", "Unknown"),
207
+ getattr(candidate, "vpc_id", ""),
208
+ getattr(candidate, "vpc_name", "Unnamed"),
209
+ getattr(candidate, "cidr_block", ""),
175
210
  "No", # Overlapping analysis would need CIDR comparison
176
- "Yes" if getattr(candidate, 'is_default', False) else "No",
177
- getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0,
211
+ "Yes" if getattr(candidate, "is_default", False) else "No",
212
+ getattr(candidate, "dependency_analysis", {}).eni_count
213
+ if hasattr(candidate, "dependency_analysis")
214
+ else 0,
178
215
  tags_str,
179
- "Yes" if getattr(candidate, 'flow_logs_enabled', False) else "No",
216
+ "Yes" if getattr(candidate, "flow_logs_enabled", False) else "No",
180
217
  "No", # TGW/Peering analysis placeholder
181
218
  lbs_present,
182
- "Yes" if getattr(candidate, 'iac_detected', False) else "No",
219
+ "Yes" if getattr(candidate, "iac_detected", False) else "No",
183
220
  "Unknown", # Timeline analysis placeholder
184
- getattr(candidate, 'cleanup_recommendation', 'unknown'),
221
+ getattr(candidate, "cleanup_recommendation", "unknown"),
185
222
  owners_str,
186
- "Generated by CloudOps Runbooks VPC Module"
223
+ "Generated by CloudOps Runbooks VPC Module",
187
224
  ]
188
225
  writer.writerow(row)
189
-
226
+
190
227
  return filename
191
228
 
192
229
 
193
230
  def _export_vpc_candidates_json(vpc_candidates: List[Any], output_dir: str) -> str:
194
231
  """Export VPC candidates to JSON format with full data structure."""
195
232
  filename = os.path.join(output_dir, "vpc-cleanup-candidates.json")
196
-
233
+
197
234
  # Convert candidates to serializable format
198
235
  candidates_data = []
199
236
  for candidate in vpc_candidates:
200
237
  candidate_dict = {
201
- "account_id": getattr(candidate, 'account_id', 'Unknown'),
202
- "vpc_id": getattr(candidate, 'vpc_id', ''),
203
- "vpc_name": getattr(candidate, 'vpc_name', 'Unnamed'),
204
- "cidr_block": getattr(candidate, 'cidr_block', ''),
205
- "region": getattr(candidate, 'region', 'unknown'),
206
- "is_default": getattr(candidate, 'is_default', False),
207
- "state": getattr(candidate, 'state', 'unknown'),
208
- "tags": getattr(candidate, 'tags', {}) or {},
209
- "tags_summary": _format_tags_for_display(getattr(candidate, 'tags', {}) or {}),
210
- "flow_logs_enabled": getattr(candidate, 'flow_logs_enabled', False),
211
- "load_balancers": getattr(candidate, 'load_balancers', []) or [],
212
- "iac_detected": getattr(candidate, 'iac_detected', False),
213
- "owners_approvals": getattr(candidate, 'owners_approvals', []) or [],
214
- "cleanup_bucket": getattr(candidate, 'cleanup_bucket', 'unknown'),
215
- "cleanup_recommendation": getattr(candidate, 'cleanup_recommendation', 'unknown'),
216
- "risk_assessment": getattr(candidate, 'risk_assessment', 'unknown'),
217
- "business_impact": getattr(candidate, 'business_impact', 'unknown')
238
+ "account_id": getattr(candidate, "account_id", "Unknown"),
239
+ "vpc_id": getattr(candidate, "vpc_id", ""),
240
+ "vpc_name": getattr(candidate, "vpc_name", "Unnamed"),
241
+ "cidr_block": getattr(candidate, "cidr_block", ""),
242
+ "region": getattr(candidate, "region", "unknown"),
243
+ "is_default": getattr(candidate, "is_default", False),
244
+ "state": getattr(candidate, "state", "unknown"),
245
+ "tags": getattr(candidate, "tags", {}) or {},
246
+ "tags_summary": _format_tags_for_display(getattr(candidate, "tags", {}) or {}),
247
+ "flow_logs_enabled": getattr(candidate, "flow_logs_enabled", False),
248
+ "load_balancers": getattr(candidate, "load_balancers", []) or [],
249
+ "iac_detected": getattr(candidate, "iac_detected", False),
250
+ "owners_approvals": getattr(candidate, "owners_approvals", []) or [],
251
+ "cleanup_bucket": getattr(candidate, "cleanup_bucket", "unknown"),
252
+ "cleanup_recommendation": getattr(candidate, "cleanup_recommendation", "unknown"),
253
+ "risk_assessment": getattr(candidate, "risk_assessment", "unknown"),
254
+ "business_impact": getattr(candidate, "business_impact", "unknown"),
218
255
  }
219
-
256
+
220
257
  # Add dependency analysis if available
221
- if hasattr(candidate, 'dependency_analysis') and candidate.dependency_analysis:
258
+ if hasattr(candidate, "dependency_analysis") and candidate.dependency_analysis:
222
259
  dep_analysis = candidate.dependency_analysis
223
260
  candidate_dict["dependency_analysis"] = {
224
- "eni_count": getattr(dep_analysis, 'eni_count', 0),
225
- "route_tables": getattr(dep_analysis, 'route_tables', []),
226
- "security_groups": getattr(dep_analysis, 'security_groups', []),
227
- "internet_gateways": getattr(dep_analysis, 'internet_gateways', []),
228
- "nat_gateways": getattr(dep_analysis, 'nat_gateways', []),
229
- "vpc_endpoints": getattr(dep_analysis, 'vpc_endpoints', []),
230
- "peering_connections": getattr(dep_analysis, 'peering_connections', []),
231
- "dependency_risk_level": getattr(dep_analysis, 'dependency_risk_level', 'unknown')
261
+ "eni_count": getattr(dep_analysis, "eni_count", 0),
262
+ "route_tables": getattr(dep_analysis, "route_tables", []),
263
+ "security_groups": getattr(dep_analysis, "security_groups", []),
264
+ "internet_gateways": getattr(dep_analysis, "internet_gateways", []),
265
+ "nat_gateways": getattr(dep_analysis, "nat_gateways", []),
266
+ "vpc_endpoints": getattr(dep_analysis, "vpc_endpoints", []),
267
+ "peering_connections": getattr(dep_analysis, "peering_connections", []),
268
+ "dependency_risk_level": getattr(dep_analysis, "dependency_risk_level", "unknown"),
232
269
  }
233
-
270
+
234
271
  candidates_data.append(candidate_dict)
235
-
272
+
236
273
  # Create export metadata
237
274
  export_data = {
238
275
  "metadata": {
239
276
  "export_timestamp": datetime.now().isoformat(),
240
277
  "total_candidates": len(candidates_data),
241
- "generator": "CloudOps Runbooks VPC Module latest version"
278
+ "generator": "CloudOps Runbooks VPC Module latest version",
242
279
  },
243
- "vpc_candidates": candidates_data
280
+ "vpc_candidates": candidates_data,
244
281
  }
245
-
246
- with open(filename, 'w', encoding='utf-8') as jsonfile:
282
+
283
+ with open(filename, "w", encoding="utf-8") as jsonfile:
247
284
  json.dump(export_data, jsonfile, indent=2, ensure_ascii=False)
248
-
285
+
249
286
  return filename
250
287
 
251
288
 
252
289
  def _export_vpc_candidates_pdf(vpc_candidates: List[Any], output_dir: str) -> str:
253
290
  """Export VPC candidates to PDF format for executive presentation."""
254
291
  filename = os.path.join(output_dir, "vpc-cleanup-candidates.pdf")
255
-
292
+
256
293
  try:
257
294
  # Try to use reportlab for PDF generation
258
295
  from reportlab.lib import colors
259
- from reportlab.lib.pagesizes import letter, A4
260
- from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
296
+ from reportlab.lib.pagesizes import A4, letter
261
297
  from reportlab.lib.styles import getSampleStyleSheet
262
-
298
+ from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle
299
+
263
300
  doc = SimpleDocTemplate(filename, pagesize=A4)
264
301
  styles = getSampleStyleSheet()
265
302
  story = []
266
-
303
+
267
304
  # Title
268
- title = Paragraph("VPC Cleanup Analysis Report", styles['Title'])
305
+ title = Paragraph("VPC Cleanup Analysis Report", styles["Title"])
269
306
  story.append(title)
270
307
  story.append(Spacer(1, 20))
271
-
308
+
272
309
  # Summary
273
310
  summary_text = f"""
274
- <b>Generated:</b> {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}<br/>
311
+ <b>Generated:</b> {datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")}<br/>
275
312
  <b>Total VPC Candidates:</b> {len(vpc_candidates)}<br/>
276
313
  <b>Analysis Source:</b> CloudOps Runbooks VPC Module latest version
277
314
  """
278
- summary = Paragraph(summary_text, styles['Normal'])
315
+ summary = Paragraph(summary_text, styles["Normal"])
279
316
  story.append(summary)
280
317
  story.append(Spacer(1, 20))
281
-
318
+
282
319
  # Create table data
283
- table_data = [
284
- ["Account ID", "VPC ID", "VPC Name", "CIDR", "Default", "ENI Count", "Decision"]
285
- ]
286
-
320
+ table_data = [["Account ID", "VPC ID", "VPC Name", "CIDR", "Default", "ENI Count", "Decision"]]
321
+
287
322
  for candidate in vpc_candidates:
288
323
  row = [
289
- str(getattr(candidate, 'account_id', 'Unknown'))[:15], # Truncate for PDF width
290
- str(getattr(candidate, 'vpc_id', ''))[:20],
291
- str(getattr(candidate, 'vpc_name', 'Unnamed'))[:15],
292
- str(getattr(candidate, 'cidr_block', ''))[:15],
293
- "Yes" if getattr(candidate, 'is_default', False) else "No",
294
- str(getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0),
295
- str(getattr(candidate, 'cleanup_recommendation', 'unknown'))[:10]
324
+ str(getattr(candidate, "account_id", "Unknown"))[:15], # Truncate for PDF width
325
+ str(getattr(candidate, "vpc_id", ""))[:20],
326
+ str(getattr(candidate, "vpc_name", "Unnamed"))[:15],
327
+ str(getattr(candidate, "cidr_block", ""))[:15],
328
+ "Yes" if getattr(candidate, "is_default", False) else "No",
329
+ str(
330
+ getattr(candidate, "dependency_analysis", {}).eni_count
331
+ if hasattr(candidate, "dependency_analysis")
332
+ else 0
333
+ ),
334
+ str(getattr(candidate, "cleanup_recommendation", "unknown"))[:10],
296
335
  ]
297
336
  table_data.append(row)
298
-
337
+
299
338
  # Create table
300
339
  table = Table(table_data)
301
- table.setStyle(TableStyle([
302
- ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
303
- ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
304
- ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
305
- ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
306
- ('FONTSIZE', (0, 0), (-1, 0), 10),
307
- ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
308
- ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
309
- ('FONTSIZE', (0, 1), (-1, -1), 8),
310
- ('GRID', (0, 0), (-1, -1), 1, colors.black)
311
- ]))
312
-
340
+ table.setStyle(
341
+ TableStyle(
342
+ [
343
+ ("BACKGROUND", (0, 0), (-1, 0), colors.grey),
344
+ ("TEXTCOLOR", (0, 0), (-1, 0), colors.whitesmoke),
345
+ ("ALIGN", (0, 0), (-1, -1), "CENTER"),
346
+ ("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
347
+ ("FONTSIZE", (0, 0), (-1, 0), 10),
348
+ ("BOTTOMPADDING", (0, 0), (-1, 0), 12),
349
+ ("BACKGROUND", (0, 1), (-1, -1), colors.beige),
350
+ ("FONTSIZE", (0, 1), (-1, -1), 8),
351
+ ("GRID", (0, 0), (-1, -1), 1, colors.black),
352
+ ]
353
+ )
354
+ )
355
+
313
356
  story.append(table)
314
357
  doc.build(story)
315
-
358
+
316
359
  except ImportError:
317
360
  # Fallback: create a simple text-based PDF placeholder
318
- with open(filename, 'w', encoding='utf-8') as f:
361
+ with open(filename, "w", encoding="utf-8") as f:
319
362
  f.write("VPC Cleanup Analysis Report (PDF)\n")
320
363
  f.write("=" * 40 + "\n\n")
321
364
  f.write(f"Generated: {datetime.now().isoformat()}\n")
322
365
  f.write(f"Total VPC Candidates: {len(vpc_candidates)}\n\n")
323
-
366
+
324
367
  for i, candidate in enumerate(vpc_candidates, 1):
325
368
  f.write(f"{i}. VPC {getattr(candidate, 'vpc_id', 'Unknown')}\n")
326
369
  f.write(f" Account: {getattr(candidate, 'account_id', 'Unknown')}\n")
327
370
  f.write(f" CIDR: {getattr(candidate, 'cidr_block', 'Unknown')}\n")
328
- f.write(f" ENI Count: {getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0}\n")
371
+ f.write(
372
+ f" ENI Count: {getattr(candidate, 'dependency_analysis', {}).eni_count if hasattr(candidate, 'dependency_analysis') else 0}\n"
373
+ )
329
374
  f.write(f" Decision: {getattr(candidate, 'cleanup_recommendation', 'unknown')}\n\n")
330
-
331
- return filename
375
+
376
+ return filename