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
@@ -22,6 +22,9 @@ from runbooks.common.profile_utils import create_operational_session, validate_p
22
22
  from .vpc_cleanup_integration import VPCCleanupFramework
23
23
  from .cleanup_wrapper import VPCCleanupCLI
24
24
  from .networking_wrapper import VPCNetworkingWrapper
25
+ from .cloudtrail_audit_integration import CloudTrailMCPIntegration, analyze_vpc_deletions_with_cloudtrail
26
+ from .test_data_loader import VPCTestDataLoader
27
+ from .cost_explorer_integration import VPCCostExplorerMCP
25
28
 
26
29
  logger = logging.getLogger(__name__)
27
30
 
@@ -29,15 +32,15 @@ logger = logging.getLogger(__name__)
29
32
  class RunbooksAdapter:
30
33
  """
31
34
  Enhanced adapter for runbooks VPC operations with comprehensive dependency scanning.
32
-
35
+
33
36
  Consolidates VPC cleanup functionality from notebooks into enterprise framework integration.
34
37
  Provides backward compatibility while leveraging existing VPC infrastructure.
35
38
  """
36
-
39
+
37
40
  def __init__(self, profile: Optional[str] = None, region: str = "us-east-1"):
38
41
  """
39
42
  Initialize RunbooksAdapter with universal AWS profile support.
40
-
43
+
41
44
  Args:
42
45
  profile: AWS profile for operations (uses universal profile selection if None)
43
46
  region: AWS region
@@ -45,7 +48,11 @@ class RunbooksAdapter:
45
48
  self.user_profile = profile
46
49
  self.region = region
47
50
  self.have_runbooks = self._detect_runbooks_availability()
48
-
51
+
52
+ # Initialize test data loader for validation
53
+ self.test_data_loader = VPCTestDataLoader()
54
+ self.test_mode = self.test_data_loader.test_data is not None
55
+
49
56
  # Universal profile selection - works with ANY AWS setup
50
57
  if profile:
51
58
  # Validate user-specified profile
@@ -56,36 +63,37 @@ class RunbooksAdapter:
56
63
  self.profile = profile
57
64
  else:
58
65
  self.profile = None
59
-
66
+
60
67
  # Initialize enterprise VPC components
61
68
  self.vpc_wrapper = None
62
69
  self.cleanup_framework = None
63
70
  self.cleanup_cli = None
64
71
  self.session = None
65
-
72
+
66
73
  self._initialize_components()
67
-
74
+
68
75
  def _detect_runbooks_availability(self) -> bool:
69
76
  """Detect if runbooks framework is available."""
70
77
  try:
71
78
  # Test imports for runbooks availability
72
79
  from runbooks.vpc import VPCNetworkingWrapper # noqa: F401
73
80
  from runbooks.vpc.vpc_cleanup_integration import VPCCleanupFramework # noqa: F401
81
+
74
82
  return True
75
83
  except ImportError:
76
84
  return False
77
-
85
+
78
86
  def _initialize_components(self):
79
87
  """Initialize runbooks components and boto3 session with universal profile support."""
80
88
  # Initialize boto3 session using universal profile management
81
89
  try:
82
90
  if self.profile:
83
91
  # Use operational session for VPC operations
84
- self.session = create_operational_session(profile=self.profile)
92
+ self.session = create_operational_session(profile_name=self.profile)
85
93
  print_success(f"Universal profile session created: {self.profile}")
86
94
  else:
87
95
  # Fallback to universal profile selection
88
- self.session = create_operational_session(profile=None)
96
+ self.session = create_operational_session(profile_name=None)
89
97
  print_success("Universal fallback session created")
90
98
  except Exception as e:
91
99
  print_warning(f"Universal session creation failed: {e}")
@@ -96,41 +104,41 @@ class RunbooksAdapter:
96
104
  except Exception as e2:
97
105
  print_error(f"All session creation methods failed: {e2}")
98
106
  self.session = None
99
-
107
+
100
108
  if not self.have_runbooks:
101
109
  print_warning("Runbooks not available - operating in enhanced fallback mode")
102
110
  return
103
-
111
+
104
112
  try:
105
113
  # Initialize VPC wrapper for network operations
106
114
  self.vpc_wrapper = VPCNetworkingWrapper(profile=self.profile, region=self.region)
107
-
115
+
108
116
  # Initialize cleanup framework for comprehensive operations
109
117
  self.cleanup_framework = VPCCleanupFramework(
110
- profile=self.profile,
111
- region=self.region,
112
- console=console,
113
- safety_mode=True
118
+ profile=self.profile, region=self.region, console=console, safety_mode=True
114
119
  )
115
-
120
+
116
121
  # Initialize CLI wrapper for business operations
117
122
  self.cleanup_cli = VPCCleanupCLI(
118
- profile=self.profile,
119
- region=self.region,
120
- safety_mode=True,
121
- console=console
123
+ profile=self.profile, region=self.region, safety_mode=True, console=console
122
124
  )
123
-
125
+
126
+ # Initialize CloudTrail MCP integration for audit trails
127
+ self.cloudtrail_audit = CloudTrailMCPIntegration(profile=self.profile, audit_period_days=90)
128
+
129
+ # Initialize Cost Explorer MCP integration for financial validation
130
+ self.cost_explorer_mcp = VPCCostExplorerMCP(billing_profile="ams-admin-Billing-ReadOnlyAccess-909135376185")
131
+
124
132
  print_success("RunbooksAdapter initialized with enterprise VPC framework")
125
-
133
+
126
134
  except Exception as e:
127
135
  print_error(f"Runbooks initialization failed: {e}")
128
136
  self.have_runbooks = False
129
-
137
+
130
138
  def dependencies(self, vpc_id: str) -> Dict[str, Any]:
131
139
  """
132
140
  Comprehensive VPC dependency scanning with 12-step analysis.
133
-
141
+
134
142
  Uses existing VPC framework infrastructure for maximum reliability.
135
143
  """
136
144
  if self.have_runbooks and self.vpc_wrapper:
@@ -139,17 +147,17 @@ class RunbooksAdapter:
139
147
  return self.vpc_wrapper.get_vpc_dependencies(vpc_id)
140
148
  except Exception as e:
141
149
  print_warning(f"Enterprise dependency scan failed, using fallback: {e}")
142
-
150
+
143
151
  # Enhanced fallback discovery using boto3
144
152
  return self._fallback_dependency_scan(vpc_id)
145
-
153
+
146
154
  def comprehensive_vpc_analysis_with_mcp(self, vpc_ids: Optional[List[str]] = None) -> Dict[str, Any]:
147
155
  """
148
156
  Enhanced VPC analysis with MCP cross-validation for all discovered VPCs.
149
-
157
+
150
158
  Consolidates notebook logic for complete VPC assessment including:
151
159
  - Dependency discovery (12-step analysis)
152
- - ENI safety validation
160
+ - ENI safety validation
153
161
  - IaC management detection
154
162
  - Cost impact assessment
155
163
  - MCP cross-validation against real AWS APIs
@@ -159,272 +167,343 @@ class RunbooksAdapter:
159
167
  # Use enhanced enterprise framework
160
168
  analysis_results = self.cleanup_cli.analyze_vpc_cleanup_candidates(
161
169
  vpc_ids=vpc_ids,
162
- export_results=True # Generate evidence files
170
+ export_results=True, # Generate evidence files
163
171
  )
164
-
172
+
165
173
  # Results include MCP validation from enhanced cleanup_wrapper
166
174
  return {
167
- 'source': 'enterprise_runbooks_framework',
168
- 'vpc_analysis': analysis_results,
169
- 'mcp_validated': analysis_results.get('cleanup_plan', {}).get('mcp_validation', {}).get('validated', False),
170
- 'accuracy_score': analysis_results.get('cleanup_plan', {}).get('mcp_validation', {}).get('consistency_score', 0.0),
171
- 'three_bucket_classification': analysis_results.get('cleanup_plan', {}).get('metadata', {}).get('three_bucket_classification', {}),
172
- 'timestamp': datetime.now(timezone.utc).isoformat()
175
+ "source": "enterprise_runbooks_framework",
176
+ "vpc_analysis": analysis_results,
177
+ "mcp_validated": analysis_results.get("cleanup_plan", {})
178
+ .get("mcp_validation", {})
179
+ .get("validated", False),
180
+ "accuracy_score": analysis_results.get("cleanup_plan", {})
181
+ .get("mcp_validation", {})
182
+ .get("consistency_score", 0.0),
183
+ "three_bucket_classification": analysis_results.get("cleanup_plan", {})
184
+ .get("metadata", {})
185
+ .get("three_bucket_classification", {}),
186
+ "timestamp": datetime.now(timezone.utc).isoformat(),
173
187
  }
174
188
  except Exception as e:
175
189
  print_error(f"Enterprise VPC analysis failed: {e}")
176
-
190
+
177
191
  # Enhanced fallback with MCP-style validation
178
192
  return self._enhanced_fallback_vpc_analysis(vpc_ids)
179
-
193
+
180
194
  def _enhanced_fallback_vpc_analysis(self, vpc_ids: Optional[List[str]] = None) -> Dict[str, Any]:
181
- """Enhanced fallback VPC analysis with comprehensive dependency scanning."""
195
+ """Enhanced fallback VPC analysis with comprehensive multi-region dependency scanning."""
196
+ # Test mode: Use test data for validation when AWS session unavailable
197
+ if not self.session and self.test_mode:
198
+ print_warning("🧪 Using test data mode for VPC analysis validation")
199
+ return self._test_mode_vpc_analysis(vpc_ids)
200
+
182
201
  if not self.session:
183
- return {'error': 'No AWS session available'}
184
-
202
+ return {"error": "No AWS session available"}
203
+
185
204
  try:
186
- ec2 = self.session.client('ec2')
187
-
188
- # Discover VPCs
189
- if vpc_ids:
190
- vpc_response = ec2.describe_vpcs(VpcIds=vpc_ids)
191
- else:
192
- vpc_response = ec2.describe_vpcs()
193
-
194
- vpcs = vpc_response.get('Vpcs', [])
205
+ # CRITICAL FIX: Multi-region VPC discovery across all AWS regions
206
+ all_regions = self._get_all_aws_regions()
207
+ all_vpcs = []
195
208
  analysis_results = []
196
-
197
- print_warning(f"Analyzing {len(vpcs)} VPCs with comprehensive dependency scanning...")
198
-
199
- for vpc in vpcs:
200
- vpc_id = vpc['VpcId']
201
-
202
- # Comprehensive dependency analysis (extracted from notebook)
203
- deps = self.dependencies(vpc_id)
204
- eni_count = self.eni_count(vpc_id)
205
- iac_info = self.iac_detect(vpc_id)
206
-
207
- # Safety validation
208
- cleanup_ready = eni_count == 0 and len(deps.get('enis', [])) == 0
209
-
210
- # Calculate basic metrics
211
- total_dependencies = sum(len(dep_list) for dep_list in deps.values() if isinstance(dep_list, list))
212
-
213
- vpc_analysis = {
214
- 'vpc_id': vpc_id,
215
- 'vpc_name': self._get_vpc_name(vpc),
216
- 'is_default': vpc.get('IsDefault', False),
217
- 'state': vpc.get('State', 'unknown'),
218
- 'cidr_block': vpc.get('CidrBlock', ''),
219
- 'dependencies': deps,
220
- 'eni_count': eni_count,
221
- 'total_dependencies': total_dependencies,
222
- 'iac_managed': iac_info.get('iac_managed', False),
223
- 'iac_sources': iac_info,
224
- 'cleanup_ready': cleanup_ready,
225
- 'safety_score': 'SAFE' if cleanup_ready else 'UNSAFE',
226
- 'blocking_factors': self._identify_blocking_factors(deps, eni_count, iac_info, vpc)
227
- }
228
-
229
- analysis_results.append(vpc_analysis)
230
-
231
- # Generate three-bucket classification
232
- three_buckets = self._apply_three_bucket_classification(analysis_results)
233
-
209
+ region_summary = {}
210
+
211
+ print_success(f"🌍 Starting multi-region VPC discovery across {len(all_regions)} regions...")
212
+
213
+ for region in all_regions:
214
+ try:
215
+ # Create regional EC2 client
216
+ ec2 = self.session.client("ec2", region_name=region)
217
+
218
+ # Discover VPCs in this region
219
+ if vpc_ids:
220
+ # Filter VPC IDs that might be in this region
221
+ vpc_response = ec2.describe_vpcs()
222
+ region_vpcs = [vpc for vpc in vpc_response.get("Vpcs", []) if vpc["VpcId"] in vpc_ids]
223
+ else:
224
+ vpc_response = ec2.describe_vpcs()
225
+ region_vpcs = vpc_response.get("Vpcs", [])
226
+
227
+ if region_vpcs:
228
+ print_success(f"📍 Region {region}: Found {len(region_vpcs)} VPCs")
229
+ region_summary[region] = len(region_vpcs)
230
+
231
+ # Analyze each VPC in this region
232
+ for vpc in region_vpcs:
233
+ vpc_id = vpc["VpcId"]
234
+
235
+ # Set session region for dependency analysis
236
+ vpc["Region"] = region
237
+
238
+ # Comprehensive dependency analysis (region-aware)
239
+ deps = self._fallback_dependency_scan_regional(vpc_id, region)
240
+ eni_count = self._get_eni_count_regional(vpc_id, region)
241
+ iac_info = self.iac_detect(vpc_id)
242
+
243
+ # ENI Gate Safety Validation (Critical Control)
244
+ eni_gate_passed = eni_count == 0
245
+ cleanup_ready = eni_gate_passed and len(deps.get("enis", [])) == 0
246
+
247
+ # Calculate basic metrics
248
+ total_dependencies = sum(
249
+ len(dep_list) for dep_list in deps.values() if isinstance(dep_list, list)
250
+ )
251
+
252
+ vpc_analysis = {
253
+ "vpc_id": vpc_id,
254
+ "vpc_name": self._get_vpc_name(vpc),
255
+ "region": region,
256
+ "is_default": vpc.get("IsDefault", False),
257
+ "state": vpc.get("State", "unknown"),
258
+ "cidr_block": vpc.get("CidrBlock", ""),
259
+ "dependencies": deps,
260
+ "eni_count": eni_count,
261
+ "eni_gate_passed": eni_gate_passed,
262
+ "total_dependencies": total_dependencies,
263
+ "iac_managed": iac_info.get("iac_managed", False),
264
+ "iac_sources": iac_info,
265
+ "cleanup_ready": cleanup_ready,
266
+ "safety_score": "SAFE" if cleanup_ready else "REQUIRES_ANALYSIS",
267
+ "blocking_factors": self._identify_blocking_factors(deps, eni_count, iac_info, vpc),
268
+ "estimated_monthly_cost": self._estimate_vpc_cost(deps, region),
269
+ }
270
+
271
+ analysis_results.append(vpc_analysis)
272
+ all_vpcs.append(vpc)
273
+
274
+ except Exception as e:
275
+ print_warning(f"⚠️ Region {region} error: {e}")
276
+ continue
277
+
278
+ print_success(
279
+ f"✅ Multi-region discovery complete: {len(all_vpcs)} VPCs found across {len(region_summary)} regions"
280
+ )
281
+ for region, count in region_summary.items():
282
+ print_success(f" 📍 {region}: {count} VPCs")
283
+
284
+ # Generate three-bucket classification with cost analysis
285
+ three_buckets = self._apply_three_bucket_classification_enhanced(analysis_results)
286
+
287
+ # Calculate total potential savings
288
+ total_potential_savings = sum(
289
+ vpc.get("estimated_monthly_cost", 0) for vpc in analysis_results if vpc.get("cleanup_ready", False)
290
+ )
291
+ annual_savings = total_potential_savings * 12
292
+
234
293
  return {
235
- 'source': 'enhanced_fallback_analysis',
236
- 'total_vpcs_analyzed': len(vpcs),
237
- 'vpc_analysis': analysis_results,
238
- 'three_bucket_classification': three_buckets,
239
- 'mcp_validated': False,
240
- 'accuracy_note': 'Fallback analysis - use enterprise framework for MCP validation',
241
- 'timestamp': datetime.now(timezone.utc).isoformat()
294
+ "source": "enhanced_multi_region_analysis",
295
+ "total_vpcs_analyzed": len(all_vpcs),
296
+ "regions_scanned": len(all_regions),
297
+ "regions_with_vpcs": len(region_summary),
298
+ "region_summary": region_summary,
299
+ "vpc_analysis": analysis_results,
300
+ "three_bucket_classification": three_buckets,
301
+ "financial_analysis": {
302
+ "total_monthly_cost_at_risk": total_potential_savings,
303
+ "annual_savings_potential": annual_savings,
304
+ "cleanup_ready_vpcs": len([vpc for vpc in analysis_results if vpc.get("cleanup_ready", False)]),
305
+ "requires_analysis_vpcs": len(
306
+ [vpc for vpc in analysis_results if not vpc.get("cleanup_ready", False)]
307
+ ),
308
+ },
309
+ "mcp_validated": False,
310
+ "accuracy_note": "Multi-region fallback analysis - use enterprise framework for MCP validation",
311
+ "timestamp": datetime.now(timezone.utc).isoformat(),
242
312
  }
243
-
313
+
244
314
  except Exception as e:
245
- return {'error': f'Enhanced fallback analysis failed: {str(e)}'}
246
-
315
+ return {"error": f"Enhanced fallback analysis failed: {str(e)}"}
316
+
247
317
  def _get_vpc_name(self, vpc: Dict[str, Any]) -> str:
248
318
  """Extract VPC name from tags."""
249
- tags = vpc.get('Tags', [])
319
+ tags = vpc.get("Tags", [])
250
320
  for tag in tags:
251
- if tag['Key'] == 'Name':
252
- return tag['Value']
321
+ if tag["Key"] == "Name":
322
+ return tag["Value"]
253
323
  return f"vpc-{vpc['VpcId']}"
254
-
324
+
255
325
  def _identify_blocking_factors(self, deps: Dict, eni_count: int, iac_info: Dict, vpc: Dict) -> List[str]:
256
326
  """Identify factors that block VPC cleanup."""
257
327
  blocking_factors = []
258
-
328
+
259
329
  if eni_count > 0:
260
330
  blocking_factors.append(f"{eni_count} network interfaces attached")
261
-
262
- if deps.get('nat_gateways'):
331
+
332
+ if deps.get("nat_gateways"):
263
333
  blocking_factors.append(f"{len(deps['nat_gateways'])} NAT gateways")
264
-
265
- if deps.get('endpoints'):
334
+
335
+ if deps.get("endpoints"):
266
336
  blocking_factors.append(f"{len(deps['endpoints'])} VPC endpoints")
267
-
268
- if deps.get('tgw_attachments'):
337
+
338
+ if deps.get("tgw_attachments"):
269
339
  blocking_factors.append(f"{len(deps['tgw_attachments'])} transit gateway attachments")
270
-
271
- if iac_info.get('iac_managed'):
340
+
341
+ if iac_info.get("iac_managed"):
272
342
  blocking_factors.append("Infrastructure as Code managed")
273
-
274
- if vpc.get('IsDefault'):
343
+
344
+ if vpc.get("IsDefault"):
275
345
  blocking_factors.append("Default VPC (requires platform approval)")
276
-
346
+
277
347
  if not blocking_factors:
278
348
  blocking_factors.append("None - ready for cleanup")
279
-
349
+
280
350
  return blocking_factors
281
-
351
+
282
352
  def _apply_three_bucket_classification(self, vpc_analyses: List[Dict]) -> Dict[str, Any]:
283
353
  """Apply three-bucket logic to VPC analysis results."""
284
354
  bucket_1_safe = []
285
- bucket_2_analysis = []
355
+ bucket_2_analysis = []
286
356
  bucket_3_complex = []
287
-
357
+
288
358
  for vpc in vpc_analyses:
289
- if (vpc['cleanup_ready'] and
290
- vpc['total_dependencies'] <= 2 and
291
- not vpc['iac_managed'] and
292
- not vpc['is_default']):
293
- bucket_1_safe.append(vpc['vpc_id'])
294
- elif (vpc['total_dependencies'] <= 5 and
295
- vpc['eni_count'] <= 1 and
296
- vpc['safety_score'] != 'UNSAFE'):
297
- bucket_2_analysis.append(vpc['vpc_id'])
359
+ if (
360
+ vpc["cleanup_ready"]
361
+ and vpc["total_dependencies"] <= 2
362
+ and not vpc["iac_managed"]
363
+ and not vpc["is_default"]
364
+ ):
365
+ bucket_1_safe.append(vpc["vpc_id"])
366
+ elif vpc["total_dependencies"] <= 5 and vpc["eni_count"] <= 1 and vpc["safety_score"] != "UNSAFE":
367
+ bucket_2_analysis.append(vpc["vpc_id"])
298
368
  else:
299
- bucket_3_complex.append(vpc['vpc_id'])
300
-
369
+ bucket_3_complex.append(vpc["vpc_id"])
370
+
301
371
  total_vpcs = len(vpc_analyses)
302
372
  return {
303
- 'bucket_1_safe': {
304
- 'count': len(bucket_1_safe),
305
- 'percentage': round((len(bucket_1_safe) / total_vpcs * 100), 1) if total_vpcs > 0 else 0,
306
- 'vpc_ids': bucket_1_safe
373
+ "bucket_1_safe": {
374
+ "count": len(bucket_1_safe),
375
+ "percentage": round((len(bucket_1_safe) / total_vpcs * 100), 1) if total_vpcs > 0 else 0,
376
+ "vpc_ids": bucket_1_safe,
307
377
  },
308
- 'bucket_2_analysis': {
309
- 'count': len(bucket_2_analysis),
310
- 'percentage': round((len(bucket_2_analysis) / total_vpcs * 100), 1) if total_vpcs > 0 else 0,
311
- 'vpc_ids': bucket_2_analysis
378
+ "bucket_2_analysis": {
379
+ "count": len(bucket_2_analysis),
380
+ "percentage": round((len(bucket_2_analysis) / total_vpcs * 100), 1) if total_vpcs > 0 else 0,
381
+ "vpc_ids": bucket_2_analysis,
382
+ },
383
+ "bucket_3_complex": {
384
+ "count": len(bucket_3_complex),
385
+ "percentage": round((len(bucket_3_complex) / total_vpcs * 100), 1) if total_vpcs > 0 else 0,
386
+ "vpc_ids": bucket_3_complex,
312
387
  },
313
- 'bucket_3_complex': {
314
- 'count': len(bucket_3_complex),
315
- 'percentage': round((len(bucket_3_complex) / total_vpcs * 100), 1) if total_vpcs > 0 else 0,
316
- 'vpc_ids': bucket_3_complex
317
- }
318
388
  }
319
-
389
+
320
390
  def _fallback_dependency_scan(self, vpc_id: str) -> Dict[str, Any]:
321
391
  """Fallback dependency scanning using boto3."""
322
392
  if not self.session:
323
- return {'error': 'No AWS session available'}
324
-
325
- ec2 = self.session.client('ec2')
326
- elbv2 = self.session.client('elbv2')
327
-
393
+ return {"error": "No AWS session available"}
394
+
395
+ ec2 = self.session.client("ec2")
396
+ elbv2 = self.session.client("elbv2")
397
+
328
398
  deps = {
329
- 'subnets': [], 'route_tables': [], 'igw': [], 'nat_gateways': [],
330
- 'endpoints': [], 'peerings': [], 'tgw_attachments': [],
331
- 'security_groups': [], 'network_acls': [], 'dhcp_options': [],
332
- 'flow_logs': [], 'enis': [], 'elbs': []
399
+ "subnets": [],
400
+ "route_tables": [],
401
+ "igw": [],
402
+ "nat_gateways": [],
403
+ "endpoints": [],
404
+ "peerings": [],
405
+ "tgw_attachments": [],
406
+ "security_groups": [],
407
+ "network_acls": [],
408
+ "dhcp_options": [],
409
+ "flow_logs": [],
410
+ "enis": [],
411
+ "elbs": [],
333
412
  }
334
-
413
+
335
414
  try:
336
415
  # Consolidated dependency discovery (existing logic from notebook)
337
-
416
+
338
417
  # 1. Subnets
339
- subs = ec2.describe_subnets(Filters=[{'Name':'vpc-id','Values':[vpc_id]}]).get('Subnets',[])
340
- deps['subnets'] = [s['SubnetId'] for s in subs]
341
-
418
+ subs = ec2.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]).get("Subnets", [])
419
+ deps["subnets"] = [s["SubnetId"] for s in subs]
420
+
342
421
  # 2. Route Tables
343
- rts = ec2.describe_route_tables(Filters=[{'Name':'vpc-id','Values':[vpc_id]}]).get('RouteTables',[])
344
- deps['route_tables'] = [r['RouteTableId'] for r in rts]
345
-
422
+ rts = ec2.describe_route_tables(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]).get("RouteTables", [])
423
+ deps["route_tables"] = [r["RouteTableId"] for r in rts]
424
+
346
425
  # 3-12. Additional dependency types (abbreviated for conciseness)
347
426
  # Full implementation includes all 12 dependency types from original notebook
348
-
427
+
349
428
  # 12. ENIs (Network Interfaces) - Critical for safety validation
350
- enis = ec2.describe_network_interfaces(Filters=[{'Name':'vpc-id','Values':[vpc_id]}]).get('NetworkInterfaces',[])
351
- deps['enis'] = [e['NetworkInterfaceId'] for e in enis]
352
-
429
+ enis = ec2.describe_network_interfaces(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]).get(
430
+ "NetworkInterfaces", []
431
+ )
432
+ deps["enis"] = [e["NetworkInterfaceId"] for e in enis]
433
+
353
434
  return deps
354
-
435
+
355
436
  except ClientError as e:
356
- return {'error': str(e)}
357
-
437
+ return {"error": str(e)}
438
+
358
439
  def eni_count(self, vpc_id: str) -> int:
359
440
  """Get ENI count for the VPC - critical for deletion safety."""
360
441
  if self.have_runbooks and self.vpc_wrapper:
361
442
  try:
362
443
  deps = self.vpc_wrapper.get_vpc_dependencies(vpc_id)
363
- return len(deps.get('enis', []))
444
+ return len(deps.get("enis", []))
364
445
  except Exception:
365
446
  pass
366
-
447
+
367
448
  # Fallback using boto3
368
449
  if self.session:
369
450
  try:
370
- ec2 = self.session.client('ec2')
371
- enis = ec2.describe_network_interfaces(
372
- Filters=[{'Name':'vpc-id','Values':[vpc_id]}]
373
- ).get('NetworkInterfaces',[])
451
+ ec2 = self.session.client("ec2")
452
+ enis = ec2.describe_network_interfaces(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]).get(
453
+ "NetworkInterfaces", []
454
+ )
374
455
  return len(enis)
375
456
  except Exception:
376
457
  pass
377
-
458
+
378
459
  return -1
379
-
460
+
380
461
  def iac_detect(self, vpc_id: str) -> Dict[str, Any]:
381
462
  """Detect Infrastructure as Code ownership (CloudFormation/Terraform)."""
382
- result = {'cloudformation': [], 'terraform_tags': [], 'iac_managed': False}
383
-
463
+ result = {"cloudformation": [], "terraform_tags": [], "iac_managed": False}
464
+
384
465
  if not self.session:
385
466
  return result
386
-
467
+
387
468
  try:
388
469
  # CloudFormation detection
389
- cfn = self.session.client('cloudformation')
390
- stacks = cfn.describe_stacks().get('Stacks', [])
470
+ cfn = self.session.client("cloudformation")
471
+ stacks = cfn.describe_stacks().get("Stacks", [])
391
472
  for stack in stacks:
392
- outputs = [o.get('OutputValue','') for o in stack.get('Outputs',[])]
393
- if vpc_id in ''.join(outputs):
394
- result['cloudformation'].append({
395
- 'StackName': stack['StackName'],
396
- 'StackId': stack['StackId']
397
- })
398
- result['iac_managed'] = True
473
+ outputs = [o.get("OutputValue", "") for o in stack.get("Outputs", [])]
474
+ if vpc_id in "".join(outputs):
475
+ result["cloudformation"].append({"StackName": stack["StackName"], "StackId": stack["StackId"]})
476
+ result["iac_managed"] = True
399
477
  except Exception:
400
478
  pass
401
-
479
+
402
480
  try:
403
481
  # Terraform detection via tags
404
- ec2 = self.session.client('ec2')
405
- vpcs = ec2.describe_vpcs(VpcIds=[vpc_id]).get('Vpcs',[])
406
- if vpcs and vpcs[0].get('Tags'):
407
- tags = {t['Key']:t['Value'] for t in vpcs[0]['Tags']}
408
- terraform_indicators = ['tf_module', 'terraform', 'managed-by', 'iac', 'Terraform']
482
+ ec2 = self.session.client("ec2")
483
+ vpcs = ec2.describe_vpcs(VpcIds=[vpc_id]).get("Vpcs", [])
484
+ if vpcs and vpcs[0].get("Tags"):
485
+ tags = {t["Key"]: t["Value"] for t in vpcs[0]["Tags"]}
486
+ terraform_indicators = ["tf_module", "terraform", "managed-by", "iac", "Terraform"]
409
487
  for indicator in terraform_indicators:
410
488
  if indicator in tags:
411
- result['terraform_tags'].append({indicator: tags[indicator]})
412
- result['iac_managed'] = True
489
+ result["terraform_tags"].append({indicator: tags[indicator]})
490
+ result["iac_managed"] = True
413
491
  except Exception:
414
492
  pass
415
-
493
+
416
494
  return result
417
-
418
- def operate_vpc_delete(self, vpc_id: str, plan_only: bool = True, confirm: bool = False,
419
- approval_path: Optional[str] = None) -> Dict[str, Any]:
495
+
496
+ def operate_vpc_delete(
497
+ self, vpc_id: str, plan_only: bool = True, confirm: bool = False, approval_path: Optional[str] = None
498
+ ) -> Dict[str, Any]:
420
499
  """
421
500
  Execute VPC deletion plan or actual deletion.
422
-
501
+
423
502
  Integrates with existing VPC cleanup framework for enterprise safety.
424
503
  """
425
504
  if not plan_only and not confirm:
426
- return {'error': 'Actual deletion requires explicit confirmation'}
427
-
505
+ return {"error": "Actual deletion requires explicit confirmation"}
506
+
428
507
  if self.have_runbooks and self.cleanup_cli:
429
508
  try:
430
509
  # Use enterprise cleanup framework
@@ -434,67 +513,914 @@ class RunbooksAdapter:
434
513
  if candidates:
435
514
  cleanup_plan = self.cleanup_framework.generate_cleanup_plan(candidates)
436
515
  return {
437
- 'plan': cleanup_plan,
438
- 'vpc_id': vpc_id,
439
- 'plan_only': True,
440
- 'command': f'runbooks vpc cleanup --vpc-id {vpc_id} --profile {self.profile}'
516
+ "plan": cleanup_plan,
517
+ "vpc_id": vpc_id,
518
+ "plan_only": True,
519
+ "command": f"runbooks vpc cleanup --vpc-id {vpc_id} --profile {self.profile}",
441
520
  }
442
521
  else:
443
- return {'error': f'VPC {vpc_id} not found or not eligible for cleanup'}
522
+ return {"error": f"VPC {vpc_id} not found or not eligible for cleanup"}
444
523
  else:
445
524
  # Execute actual cleanup (requires enterprise coordination)
446
525
  return {
447
- 'message': 'Actual VPC deletion requires enterprise coordination',
448
- 'command': f'runbooks vpc cleanup --vpc-id {vpc_id} --profile {self.profile} --force',
449
- 'approval_required': True,
450
- 'approval_path': approval_path
526
+ "message": "Actual VPC deletion requires enterprise coordination",
527
+ "command": f"runbooks vpc cleanup --vpc-id {vpc_id} --profile {self.profile} --force",
528
+ "approval_required": True,
529
+ "approval_path": approval_path,
451
530
  }
452
531
  except Exception as e:
453
- return {'error': f'Enterprise cleanup operation failed: {e}'}
454
-
532
+ return {"error": f"Enterprise cleanup operation failed: {e}"}
533
+
455
534
  # Fallback plan generation
456
535
  return {
457
- 'plan': f'Cleanup plan for VPC {vpc_id}',
458
- 'fallback_mode': True,
459
- 'command': f'# Manual cleanup required for VPC {vpc_id}',
460
- 'plan_only': plan_only
536
+ "plan": f"Cleanup plan for VPC {vpc_id}",
537
+ "fallback_mode": True,
538
+ "command": f"# Manual cleanup required for VPC {vpc_id}",
539
+ "plan_only": plan_only,
461
540
  }
462
-
541
+
463
542
  def validate_vpc_cleanup_readiness(self, vpc_id: str) -> Dict[str, Any]:
464
543
  """
465
544
  Validate VPC readiness for cleanup using enterprise framework.
466
-
545
+
467
546
  Provides comprehensive safety validation integrating existing infrastructure.
468
547
  """
469
548
  if self.have_runbooks and self.cleanup_cli:
470
549
  try:
471
550
  # Use enterprise safety validation
472
- return self.cleanup_cli.validate_vpc_cleanup_safety(
473
- vpc_id=vpc_id,
474
- account_profile=self.profile
475
- )
551
+ return self.cleanup_cli.validate_vpc_cleanup_safety(vpc_id=vpc_id, account_profile=self.profile)
476
552
  except Exception as e:
477
553
  print_warning(f"Enterprise validation failed: {e}")
478
-
554
+
479
555
  # Fallback validation
480
556
  try:
481
- ec2 = self.session.client('ec2') if self.session else None
557
+ ec2 = self.session.client("ec2") if self.session else None
482
558
  if not ec2:
483
- return {'error': 'No AWS client available'}
484
-
559
+ return {"error": "No AWS client available"}
560
+
485
561
  # Basic ENI count check (critical safety validation)
486
- eni_response = ec2.describe_network_interfaces(
487
- Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}]
488
- )
489
- eni_count = len(eni_response['NetworkInterfaces'])
490
-
562
+ eni_response = ec2.describe_network_interfaces(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
563
+ eni_count = len(eni_response["NetworkInterfaces"])
564
+
491
565
  return {
492
- 'vpc_id': vpc_id,
493
- 'eni_count': eni_count,
494
- 'cleanup_ready': eni_count == 0,
495
- 'validation_method': 'boto3_fallback',
496
- 'timestamp_utc': datetime.now(timezone.utc).isoformat(),
497
- 'safety_score': 'SAFE' if eni_count == 0 else 'UNSAFE'
566
+ "vpc_id": vpc_id,
567
+ "eni_count": eni_count,
568
+ "cleanup_ready": eni_count == 0,
569
+ "validation_method": "boto3_fallback",
570
+ "timestamp_utc": datetime.now(timezone.utc).isoformat(),
571
+ "safety_score": "SAFE" if eni_count == 0 else "UNSAFE",
572
+ }
573
+ except Exception as e:
574
+ return {"error": f"Validation failed: {str(e)}"}
575
+
576
+ # ==========================================
577
+ # CloudTrail MCP Integration Methods
578
+ # ==========================================
579
+
580
+ def analyze_vpc_deletions_audit_trail(
581
+ self, target_vpcs: Optional[List[str]] = None, days_back: int = 90
582
+ ) -> Dict[str, Any]:
583
+ """
584
+ Analyze VPC deletions using CloudTrail MCP integration for audit trail compliance.
585
+
586
+ Enterprise method for comprehensive deleted resources tracking as requested by user.
587
+
588
+ Args:
589
+ target_vpcs: Specific VPC IDs to audit (optional)
590
+ days_back: Days to look back for audit trail (default: 90)
591
+
592
+ Returns:
593
+ Comprehensive audit results with CloudTrail evidence
594
+ """
595
+ print_success("🔍 CloudTrail MCP Integration: Analyzing VPC deletions audit trail")
596
+
597
+ if self.have_runbooks and hasattr(self, "cloudtrail_audit"):
598
+ try:
599
+ # Use enterprise CloudTrail MCP integration
600
+ audit_results = self.cloudtrail_audit.analyze_deleted_vpc_resources(target_vpc_ids=target_vpcs)
601
+
602
+ return {
603
+ "source": "cloudtrail_mcp_integration",
604
+ "audit_results": audit_results,
605
+ "mcp_validated": audit_results.validation_accuracy >= 99.5,
606
+ "compliance_status": audit_results.compliance_status,
607
+ "deleted_resources_found": audit_results.deleted_resources_found,
608
+ "audit_trail_completeness": audit_results.audit_trail_completeness,
609
+ "enterprise_coordination": "systematic_delegation_active",
610
+ "timestamp": datetime.now(timezone.utc).isoformat(),
611
+ }
612
+
613
+ except Exception as e:
614
+ print_error(f"CloudTrail MCP integration failed: {e}")
615
+ return self._fallback_cloudtrail_analysis(target_vpcs, days_back)
616
+
617
+ # Fallback to basic CloudTrail analysis if MCP unavailable
618
+ return self._fallback_cloudtrail_analysis(target_vpcs, days_back)
619
+
620
+ def validate_user_vpc_cleanup_claims(self, claimed_deletions: List[Dict]) -> Dict[str, Any]:
621
+ """
622
+ Validate user's claimed VPC deletions against CloudTrail audit trail.
623
+
624
+ Specifically for user's case: "validate the 12 deleted VPCs from the user's data"
625
+
626
+ Args:
627
+ claimed_deletions: List of claimed VPC deletions with IDs and dates
628
+
629
+ Returns:
630
+ Validation results with audit trail evidence
631
+ """
632
+ print_success("🔍 CloudTrail Validation: User's VPC deletion claims")
633
+
634
+ if self.have_runbooks and hasattr(self, "cloudtrail_audit"):
635
+ try:
636
+ # Use enterprise CloudTrail MCP validation
637
+ validation_results = self.cloudtrail_audit.validate_user_vpc_deletions(claimed_deletions)
638
+
639
+ return {
640
+ "source": "cloudtrail_mcp_validation",
641
+ "validation_results": validation_results,
642
+ "total_claimed": validation_results["total_claimed_deletions"],
643
+ "validated_count": validation_results["validated_deletions"],
644
+ "validation_accuracy": validation_results["validation_accuracy"],
645
+ "audit_evidence_count": len(validation_results["audit_evidence"]),
646
+ "enterprise_coordination": "devops_security_engineer_validation_complete",
647
+ "timestamp": datetime.now(timezone.utc).isoformat(),
648
+ }
649
+
650
+ except Exception as e:
651
+ print_error(f"CloudTrail MCP validation failed: {e}")
652
+ return self._fallback_user_validation(claimed_deletions)
653
+
654
+ # Fallback validation using basic AWS APIs
655
+ return self._fallback_user_validation(claimed_deletions)
656
+
657
+ def generate_vpc_cleanup_compliance_report(
658
+ self, audit_results: Optional[Dict] = None, compliance_framework: str = "SOC2"
659
+ ) -> Dict[str, Any]:
660
+ """
661
+ Generate enterprise compliance report for VPC cleanup audit trail.
662
+
663
+ Args:
664
+ audit_results: CloudTrail audit results (optional, will run analysis if None)
665
+ compliance_framework: Compliance framework (SOC2, PCI-DSS, HIPAA)
666
+
667
+ Returns:
668
+ Comprehensive compliance report with audit evidence
669
+ """
670
+ print_success(f"📋 Generating {compliance_framework} Compliance Report for VPC cleanup")
671
+
672
+ # Get audit results if not provided
673
+ if not audit_results:
674
+ audit_analysis = self.analyze_vpc_deletions_audit_trail()
675
+ audit_results = audit_analysis.get("audit_results")
676
+
677
+ if self.have_runbooks and hasattr(self, "cloudtrail_audit") and audit_results:
678
+ try:
679
+ # Use enterprise compliance report generation
680
+ compliance_report = self.cloudtrail_audit.generate_compliance_audit_report(
681
+ audit_results, compliance_framework
682
+ )
683
+
684
+ return {
685
+ "source": "enterprise_compliance_framework",
686
+ "framework": compliance_framework,
687
+ "compliance_report": compliance_report,
688
+ "overall_status": compliance_report["compliance_assessment"]["overall_status"],
689
+ "audit_score": compliance_report["compliance_metrics"]["audit_trail_completeness"],
690
+ "validation_score": compliance_report["compliance_metrics"]["validation_accuracy"],
691
+ "enterprise_coordination": "devops_security_engineer_compliance_validated",
692
+ "timestamp": datetime.now(timezone.utc).isoformat(),
693
+ }
694
+
695
+ except Exception as e:
696
+ print_error(f"Enterprise compliance report generation failed: {e}")
697
+
698
+ # Fallback basic compliance summary
699
+ return self._fallback_compliance_summary(compliance_framework)
700
+
701
+ def _fallback_cloudtrail_analysis(self, target_vpcs: Optional[List[str]], days_back: int) -> Dict[str, Any]:
702
+ """Fallback CloudTrail analysis using basic AWS APIs."""
703
+ print_warning("Using fallback CloudTrail analysis - limited functionality")
704
+
705
+ if not self.session:
706
+ return {"error": "No AWS session available for CloudTrail analysis"}
707
+
708
+ try:
709
+ cloudtrail = self.session.client("cloudtrail")
710
+
711
+ # Basic CloudTrail event lookup
712
+ end_time = datetime.now()
713
+ start_time = end_time - timedelta(days=days_back)
714
+
715
+ # Look for VPC deletion events
716
+ vpc_events = []
717
+ event_names = ["DeleteVpc", "DeleteSubnet", "DeleteSecurityGroup", "DeleteNatGateway"]
718
+
719
+ for event_name in event_names:
720
+ try:
721
+ events = cloudtrail.lookup_events(
722
+ LookupAttributes=[{"AttributeKey": "EventName", "AttributeValue": event_name}],
723
+ StartTime=start_time,
724
+ EndTime=end_time,
725
+ MaxItems=50, # AWS CloudTrail limit
726
+ ).get("Events", [])
727
+
728
+ vpc_events.extend(events)
729
+
730
+ except Exception as e:
731
+ print_warning(f"Failed to query {event_name} events: {e}")
732
+
733
+ # Filter for target VPCs if specified
734
+ if target_vpcs:
735
+ filtered_events = []
736
+ for event in vpc_events:
737
+ # Basic filtering - would need more sophisticated parsing in real implementation
738
+ if any(vpc_id in str(event.get("Resources", [])) for vpc_id in target_vpcs):
739
+ filtered_events.append(event)
740
+ vpc_events = filtered_events
741
+
742
+ return {
743
+ "source": "fallback_cloudtrail_analysis",
744
+ "events_found": len(vpc_events),
745
+ "audit_period": f"{start_time.strftime('%Y-%m-%d')} to {end_time.strftime('%Y-%m-%d')}",
746
+ "events": [
747
+ {
748
+ "event_time": event.get("EventTime", "").isoformat()
749
+ if hasattr(event.get("EventTime", ""), "isoformat")
750
+ else str(event.get("EventTime", "")),
751
+ "event_name": event.get("EventName", ""),
752
+ "username": event.get("Username", ""),
753
+ "source_ip": event.get("SourceIPAddress", ""),
754
+ "resources": event.get("Resources", []),
755
+ }
756
+ for event in vpc_events[:20] # Limit for display
757
+ ],
758
+ "limitation": "Basic API - use enterprise MCP integration for comprehensive analysis",
759
+ "mcp_validated": False,
760
+ "timestamp": datetime.now(timezone.utc).isoformat(),
761
+ }
762
+
763
+ except Exception as e:
764
+ return {"error": f"Fallback CloudTrail analysis failed: {str(e)}"}
765
+
766
+ def _fallback_user_validation(self, claimed_deletions: List[Dict]) -> Dict[str, Any]:
767
+ """Fallback validation for user's VPC deletion claims."""
768
+ print_warning("Using fallback validation - limited CloudTrail functionality")
769
+
770
+ return {
771
+ "source": "fallback_validation",
772
+ "total_claimed_deletions": len(claimed_deletions),
773
+ "validation_status": "PARTIAL - MCP integration required for full validation",
774
+ "claimed_deletions": claimed_deletions,
775
+ "limitation": "Use CloudTrail MCP server for comprehensive validation",
776
+ "recommendation": "Enable CloudTrail MCP integration for enterprise audit trail compliance",
777
+ "mcp_validated": False,
778
+ "timestamp": datetime.now(timezone.utc).isoformat(),
779
+ }
780
+
781
+ def _fallback_compliance_summary(self, framework: str) -> Dict[str, Any]:
782
+ """Generate basic compliance summary without full MCP integration."""
783
+ return {
784
+ "source": "fallback_compliance_summary",
785
+ "framework": framework,
786
+ "status": "INCOMPLETE - MCP integration required",
787
+ "audit_trail_status": "PARTIAL",
788
+ "recommendation": "Enable CloudTrail MCP server for complete compliance reporting",
789
+ "limitations": [
790
+ "No real-time CloudTrail event validation",
791
+ "Limited audit trail completeness assessment",
792
+ "No automated compliance scoring",
793
+ ],
794
+ "next_steps": [
795
+ "Configure CloudTrail MCP server",
796
+ "Enable systematic delegation to devops-security-engineer [5]",
797
+ "Implement comprehensive audit trail collection",
798
+ ],
799
+ "timestamp": datetime.now(timezone.utc).isoformat(),
800
+ }
801
+
802
+ def _get_all_aws_regions(self) -> List[str]:
803
+ """Get all available AWS regions for multi-region VPC discovery."""
804
+ try:
805
+ if self.session:
806
+ ec2 = self.session.client("ec2", region_name="us-east-1") # Use us-east-1 to get regions
807
+ regions_response = ec2.describe_regions()
808
+ regions = [region["RegionName"] for region in regions_response["Regions"]]
809
+ return sorted(regions) # Sort for consistent ordering
810
+ else:
811
+ # Fallback to common regions from test data
812
+ return [
813
+ "us-east-1",
814
+ "us-west-2",
815
+ "us-east-2",
816
+ "us-west-1",
817
+ "eu-west-1",
818
+ "eu-west-2",
819
+ "eu-central-1",
820
+ "ap-southeast-1",
821
+ "ap-northeast-1",
822
+ "ca-central-1",
823
+ ]
824
+ except Exception as e:
825
+ print_warning(f"Failed to get regions dynamically: {e}")
826
+ # Fallback to test data regions
827
+ return [
828
+ "us-east-1",
829
+ "us-west-2",
830
+ "us-east-2",
831
+ "us-west-1",
832
+ "eu-west-1",
833
+ "eu-west-2",
834
+ "eu-central-1",
835
+ "ap-southeast-1",
836
+ "ap-northeast-1",
837
+ "ca-central-1",
838
+ ]
839
+
840
+ def _fallback_dependency_scan_regional(self, vpc_id: str, region: str) -> Dict[str, Any]:
841
+ """Enhanced regional dependency scanning for multi-region VPC analysis."""
842
+ if not self.session:
843
+ return {"error": "No AWS session available"}
844
+
845
+ try:
846
+ ec2 = self.session.client("ec2", region_name=region)
847
+
848
+ # Comprehensive dependency discovery
849
+ dependencies = {
850
+ "enis": [],
851
+ "subnets": [],
852
+ "security_groups": [],
853
+ "route_tables": [],
854
+ "nat_gateways": [],
855
+ "internet_gateways": [],
856
+ "vpc_endpoints": [],
857
+ "vpc_peering": [],
858
+ "vpn_gateways": [],
859
+ "vpn_connections": [],
498
860
  }
861
+
862
+ # ENIs (Critical for safety)
863
+ eni_response = ec2.describe_network_interfaces(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
864
+ dependencies["enis"] = eni_response.get("NetworkInterfaces", [])
865
+
866
+ # Subnets
867
+ subnet_response = ec2.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
868
+ dependencies["subnets"] = subnet_response.get("Subnets", [])
869
+
870
+ # Security Groups
871
+ sg_response = ec2.describe_security_groups(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
872
+ dependencies["security_groups"] = sg_response.get("SecurityGroups", [])
873
+
874
+ # Route Tables
875
+ rt_response = ec2.describe_route_tables(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
876
+ dependencies["route_tables"] = rt_response.get("RouteTables", [])
877
+
878
+ # NAT Gateways
879
+ nat_response = ec2.describe_nat_gateways(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
880
+ dependencies["nat_gateways"] = nat_response.get("NatGateways", [])
881
+
882
+ # Internet Gateways
883
+ igw_response = ec2.describe_internet_gateways(Filters=[{"Name": "attachment.vpc-id", "Values": [vpc_id]}])
884
+ dependencies["internet_gateways"] = igw_response.get("InternetGateways", [])
885
+
886
+ # VPC Endpoints
887
+ endpoint_response = ec2.describe_vpc_endpoints(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
888
+ dependencies["vpc_endpoints"] = endpoint_response.get("VpcEndpoints", [])
889
+
890
+ # VPC Peering
891
+ peering_response = ec2.describe_vpc_peering_connections(
892
+ Filters=[
893
+ {"Name": "requester-vpc-info.vpc-id", "Values": [vpc_id]},
894
+ {"Name": "accepter-vpc-info.vpc-id", "Values": [vpc_id]},
895
+ ]
896
+ )
897
+ dependencies["vpc_peering"] = peering_response.get("VpcPeeringConnections", [])
898
+
899
+ return dependencies
900
+
499
901
  except Exception as e:
500
- return {'error': f'Validation failed: {str(e)}'}
902
+ print_warning(f"Regional dependency scan failed for {vpc_id} in {region}: {e}")
903
+ return {"error": str(e)}
904
+
905
+ def _get_eni_count_regional(self, vpc_id: str, region: str) -> int:
906
+ """Get ENI count for a VPC in a specific region (ENI Gate implementation)."""
907
+ try:
908
+ if self.session:
909
+ ec2 = self.session.client("ec2", region_name=region)
910
+ eni_response = ec2.describe_network_interfaces(Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
911
+ return len(eni_response.get("NetworkInterfaces", []))
912
+ else:
913
+ return 0
914
+ except Exception as e:
915
+ print_warning(f"ENI count failed for {vpc_id} in {region}: {e}")
916
+ return -1 # Error indicator
917
+
918
+ def _apply_three_bucket_classification_enhanced(self, analysis_results: List[Dict[str, Any]]) -> Dict[str, Any]:
919
+ """Apply Three-Bucket Cleanup Sequence classification with enhanced cost analysis."""
920
+
921
+ bucket_1_internal = [] # NAT Gateways, VPC Endpoints, Network Firewall
922
+ bucket_2_external = [] # VPC Peering, TGW/VPN, Internet Gateways
923
+ bucket_3_control = [] # Route 53, PHZ, RAM, Subnet Groups, Flow Logs
924
+ immediate_cleanup = [] # Zero ENI VPCs ready for immediate deletion
925
+
926
+ total_cleanup_savings = 0
927
+
928
+ for vpc in analysis_results:
929
+ vpc_id = vpc["vpc_id"]
930
+ deps = vpc.get("dependencies", {})
931
+ eni_count = vpc.get("eni_count", 0)
932
+
933
+ # Immediate cleanup candidates (Zero ENI Gate passed)
934
+ if eni_count == 0 and vpc.get("eni_gate_passed", False):
935
+ immediate_cleanup.append(
936
+ {
937
+ "vpc_id": vpc_id,
938
+ "vpc_name": vpc.get("vpc_name", "Unknown"),
939
+ "region": vpc.get("region", "unknown"),
940
+ "estimated_monthly_savings": vpc.get("estimated_monthly_cost", 0),
941
+ "cleanup_ready": True,
942
+ }
943
+ )
944
+ total_cleanup_savings += vpc.get("estimated_monthly_cost", 0)
945
+ continue
946
+
947
+ # Bucket 1: Internal Data Plane
948
+ if len(deps.get("nat_gateways", [])) > 0 or len(deps.get("vpc_endpoints", [])) > 0:
949
+ bucket_1_internal.append(
950
+ {
951
+ "vpc_id": vpc_id,
952
+ "nat_gateways": len(deps.get("nat_gateways", [])),
953
+ "vpc_endpoints": len(deps.get("vpc_endpoints", [])),
954
+ "estimated_savings": vpc.get("estimated_monthly_cost", 0) * 0.6, # 60% from internal cleanup
955
+ }
956
+ )
957
+
958
+ # Bucket 2: External Interconnects
959
+ if len(deps.get("vpc_peering", [])) > 0 or len(deps.get("internet_gateways", [])) > 0:
960
+ bucket_2_external.append(
961
+ {
962
+ "vpc_id": vpc_id,
963
+ "vpc_peering": len(deps.get("vpc_peering", [])),
964
+ "internet_gateways": len(deps.get("internet_gateways", [])),
965
+ "estimated_savings": vpc.get("estimated_monthly_cost", 0) * 0.3, # 30% from external cleanup
966
+ }
967
+ )
968
+
969
+ # Bucket 3: Control Plane (Route Tables, Security Groups)
970
+ if (
971
+ len(deps.get("route_tables", [])) > 2 # More than default
972
+ or len(deps.get("security_groups", [])) > 1
973
+ ): # More than default
974
+ bucket_3_control.append(
975
+ {
976
+ "vpc_id": vpc_id,
977
+ "route_tables": len(deps.get("route_tables", [])),
978
+ "security_groups": len(deps.get("security_groups", [])),
979
+ "estimated_savings": vpc.get("estimated_monthly_cost", 0) * 0.1, # 10% from control cleanup
980
+ }
981
+ )
982
+
983
+ return {
984
+ "immediate_cleanup": {
985
+ "vpcs": immediate_cleanup,
986
+ "count": len(immediate_cleanup),
987
+ "monthly_savings": total_cleanup_savings,
988
+ "annual_savings": total_cleanup_savings * 12,
989
+ },
990
+ "bucket_1_internal": {
991
+ "vpcs": bucket_1_internal,
992
+ "count": len(bucket_1_internal),
993
+ "focus": "NAT Gateways, VPC Endpoints, Network Firewall",
994
+ },
995
+ "bucket_2_external": {
996
+ "vpcs": bucket_2_external,
997
+ "count": len(bucket_2_external),
998
+ "focus": "VPC Peering, TGW/VPN, Internet Gateways",
999
+ },
1000
+ "bucket_3_control": {
1001
+ "vpcs": bucket_3_control,
1002
+ "count": len(bucket_3_control),
1003
+ "focus": "Route 53, PHZ, RAM, Subnet Groups, Flow Logs",
1004
+ },
1005
+ "summary": {
1006
+ "total_vpcs_analyzed": len(analysis_results),
1007
+ "immediate_cleanup_ready": len(immediate_cleanup),
1008
+ "requires_three_bucket_process": len(bucket_1_internal)
1009
+ + len(bucket_2_external)
1010
+ + len(bucket_3_control),
1011
+ "estimated_annual_savings": total_cleanup_savings * 12,
1012
+ },
1013
+ }
1014
+
1015
+ def _estimate_vpc_cost(self, dependencies: Dict[str, Any], region: str) -> float:
1016
+ """Estimate monthly cost savings from VPC cleanup based on dependencies."""
1017
+
1018
+ # AWS pricing estimates (monthly USD, varies by region)
1019
+ base_pricing = {
1020
+ "nat_gateway": 45.0, # $45/month per NAT Gateway
1021
+ "vpc_endpoint": 7.2, # $7.20/month per endpoint (720 hours)
1022
+ "internet_gateway": 0, # Free, but data transfer costs
1023
+ "elastic_ip": 3.6, # $3.60/month per unused EIP
1024
+ "vpc_base": 0, # VPC itself is free
1025
+ "data_transfer": 0.09, # $0.09/GB estimate for cleanup
1026
+ }
1027
+
1028
+ estimated_cost = 0
1029
+
1030
+ # NAT Gateway costs (major cost driver)
1031
+ nat_count = len(dependencies.get("nat_gateways", []))
1032
+ estimated_cost += nat_count * base_pricing["nat_gateway"]
1033
+
1034
+ # VPC Endpoint costs
1035
+ endpoint_count = len(dependencies.get("vpc_endpoints", []))
1036
+ estimated_cost += endpoint_count * base_pricing["vpc_endpoint"]
1037
+
1038
+ # Estimate unused Elastic IPs (simplified)
1039
+ estimated_cost += len(dependencies.get("enis", [])) * 0.1 * base_pricing["elastic_ip"]
1040
+
1041
+ # Base infrastructure overhead estimate
1042
+ if estimated_cost > 0:
1043
+ estimated_cost += 15.0 # Base infrastructure overhead
1044
+
1045
+ return round(estimated_cost, 2)
1046
+
1047
+ def _test_mode_vpc_analysis(self, vpc_ids: Optional[List[str]] = None) -> Dict[str, Any]:
1048
+ """
1049
+ Test mode VPC analysis using production test data for validation.
1050
+
1051
+ Returns comprehensive analysis using 27 VPCs across 10 regions from test data.
1052
+ """
1053
+ print_success("🧪 Test Mode: Analyzing VPCs using production test data")
1054
+
1055
+ active_vpcs = self.test_data_loader.get_active_vpcs()
1056
+ test_regions = self.test_data_loader.get_test_regions()
1057
+ business_metrics = self.test_data_loader.get_business_metrics()
1058
+
1059
+ # Filter VPCs if specific IDs requested
1060
+ if vpc_ids:
1061
+ active_vpcs = [vpc for vpc in active_vpcs if vpc["vpc_id"] in vpc_ids]
1062
+
1063
+ analysis_results = []
1064
+ region_summary = {}
1065
+
1066
+ print_success(f"🌍 Test mode multi-region analysis across {len(test_regions)} regions...")
1067
+
1068
+ for region in test_regions:
1069
+ region_vpcs = self.test_data_loader.get_vpcs_by_region(region)
1070
+ if not region_vpcs:
1071
+ continue
1072
+
1073
+ print_success(f"📍 Region {region}: Found {len(region_vpcs)} VPCs")
1074
+ region_summary[region] = len(region_vpcs)
1075
+
1076
+ for vpc_data in region_vpcs:
1077
+ # Convert test data to analysis format
1078
+ vpc_analysis = {
1079
+ "vpc_id": vpc_data["vpc_id"],
1080
+ "vpc_name": vpc_data["name"],
1081
+ "region": region,
1082
+ "is_default": vpc_data["name"].startswith("default"),
1083
+ "state": "available",
1084
+ "cidr_block": vpc_data["cidr"],
1085
+ "dependencies": self._simulate_dependencies_from_test_data(vpc_data),
1086
+ "eni_count": vpc_data["enis"],
1087
+ "eni_gate_passed": vpc_data["enis"] == 0,
1088
+ "total_dependencies": self._calculate_dependencies_from_test_data(vpc_data),
1089
+ "iac_managed": False, # Simplified for test mode
1090
+ "iac_sources": {},
1091
+ "cleanup_ready": vpc_data["enis"] == 0 and vpc_data.get("decision") == "DELETE",
1092
+ "safety_score": "SAFE" if vpc_data["enis"] == 0 else "REQUIRES_ANALYSIS",
1093
+ "blocking_factors": self._get_blocking_factors_from_test_data(vpc_data),
1094
+ "estimated_monthly_cost": vpc_data.get("cost_monthly", vpc_data.get("cost_annual", 0) / 12),
1095
+ "test_data_source": True,
1096
+ }
1097
+
1098
+ analysis_results.append(vpc_analysis)
1099
+
1100
+ print_success(f"✅ Test mode analysis complete: {len(analysis_results)} VPCs analyzed")
1101
+
1102
+ # Generate three-bucket classification from test data
1103
+ three_buckets = self._apply_three_bucket_classification_enhanced(analysis_results)
1104
+
1105
+ # Calculate financial metrics from test data
1106
+ total_potential_savings = sum(
1107
+ vpc.get("estimated_monthly_cost", 0) for vpc in analysis_results if vpc.get("cleanup_ready", False)
1108
+ )
1109
+ annual_savings = total_potential_savings * 12
1110
+
1111
+ return {
1112
+ "source": "test_mode_vpc_analysis",
1113
+ "test_data_path": self.test_data_loader.test_data_path,
1114
+ "total_vpcs_analyzed": len(analysis_results),
1115
+ "regions_scanned": len(test_regions),
1116
+ "regions_with_vpcs": len(region_summary),
1117
+ "region_summary": region_summary,
1118
+ "vpc_analysis": analysis_results,
1119
+ "three_bucket_classification": three_buckets,
1120
+ "financial_analysis": {
1121
+ "total_monthly_cost_at_risk": total_potential_savings,
1122
+ "annual_savings_potential": annual_savings,
1123
+ "target_annual_savings": business_metrics.get("annual_savings", 0),
1124
+ "cleanup_ready_vpcs": len([vpc for vpc in analysis_results if vpc.get("cleanup_ready", False)]),
1125
+ "requires_analysis_vpcs": len([vpc for vpc in analysis_results if not vpc.get("cleanup_ready", False)]),
1126
+ },
1127
+ "business_validation": {
1128
+ "exceeds_target": annual_savings >= business_metrics.get("annual_savings", 0),
1129
+ "target_achievement_percentage": round(
1130
+ (annual_savings / business_metrics.get("annual_savings", 1)) * 100, 1
1131
+ )
1132
+ if business_metrics.get("annual_savings", 0) > 0
1133
+ else 0,
1134
+ },
1135
+ "test_metadata": {
1136
+ "total_test_vpcs": business_metrics.get("total_vpcs", 0),
1137
+ "active_vpcs": business_metrics.get("active_vpcs", 0),
1138
+ "deleted_vpcs": business_metrics.get("deleted_vpcs", 0),
1139
+ },
1140
+ "mcp_validated": False,
1141
+ "test_mode": True,
1142
+ "accuracy_note": "Test mode analysis - use real AWS profile for production validation",
1143
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1144
+ }
1145
+
1146
+ def _simulate_dependencies_from_test_data(self, vpc_data: Dict[str, Any]) -> Dict[str, List]:
1147
+ """Simulate VPC dependencies based on test data characteristics."""
1148
+ dependencies = {
1149
+ "enis": [],
1150
+ "subnets": [],
1151
+ "security_groups": [],
1152
+ "route_tables": [],
1153
+ "nat_gateways": [],
1154
+ "internet_gateways": [],
1155
+ "vpc_endpoints": [],
1156
+ "vpc_peering": [],
1157
+ "vpn_gateways": [],
1158
+ "vpn_connections": [],
1159
+ }
1160
+
1161
+ # Simulate ENIs based on count in test data
1162
+ eni_count = vpc_data.get("enis", 0)
1163
+ dependencies["enis"] = [f"eni-{vpc_data['vpc_id'][-8:]}{i:02d}" for i in range(eni_count)]
1164
+
1165
+ # Simulate basic dependencies for active VPCs
1166
+ if vpc_data.get("decision") != "DELETE":
1167
+ dependencies["subnets"] = [f"subnet-{vpc_data['vpc_id'][-8:]}001", f"subnet-{vpc_data['vpc_id'][-8:]}002"]
1168
+ dependencies["security_groups"] = [f"sg-{vpc_data['vpc_id'][-8:]}default"]
1169
+ dependencies["route_tables"] = [f"rtb-{vpc_data['vpc_id'][-8:]}main"]
1170
+
1171
+ # Add NAT gateway for higher cost VPCs
1172
+ if vpc_data.get("cost_monthly", 0) > 100:
1173
+ dependencies["nat_gateways"] = [f"nat-{vpc_data['vpc_id'][-8:]}"]
1174
+
1175
+ # Add internet gateway for non-private VPCs
1176
+ if not vpc_data.get("name", "").endswith("-private"):
1177
+ dependencies["internet_gateways"] = [f"igw-{vpc_data['vpc_id'][-8:]}"]
1178
+
1179
+ return dependencies
1180
+
1181
+ def _calculate_dependencies_from_test_data(self, vpc_data: Dict[str, Any]) -> int:
1182
+ """Calculate total dependency count from test data."""
1183
+ # Base dependencies for any VPC
1184
+ base_deps = 2 # Route table + security group
1185
+
1186
+ # Add ENI count
1187
+ base_deps += vpc_data.get("enis", 0)
1188
+
1189
+ # Add more dependencies for complex VPCs
1190
+ if vpc_data.get("cost_monthly", 0) > 100:
1191
+ base_deps += 3 # NAT gateway + subnets + internet gateway
1192
+
1193
+ return base_deps
1194
+
1195
+ def _get_blocking_factors_from_test_data(self, vpc_data: Dict[str, Any]) -> List[str]:
1196
+ """Get blocking factors from test data characteristics."""
1197
+ blocking_factors = []
1198
+
1199
+ eni_count = vpc_data.get("enis", 0)
1200
+ if eni_count > 0:
1201
+ blocking_factors.append(f"{eni_count} network interfaces attached")
1202
+
1203
+ if vpc_data.get("cost_monthly", 0) > 100:
1204
+ blocking_factors.append("High-cost infrastructure requires analysis")
1205
+
1206
+ if vpc_data.get("name", "").startswith("default"):
1207
+ blocking_factors.append("Default VPC (CIS 2.1 compliance review required)")
1208
+
1209
+ decision = vpc_data.get("decision", "")
1210
+ if decision == "KEEP":
1211
+ blocking_factors.append("Marked for retention (business critical)")
1212
+ elif decision == "OPTIMIZE":
1213
+ blocking_factors.append("Optimization candidate (cost reduction potential)")
1214
+
1215
+ if not blocking_factors:
1216
+ blocking_factors.append("None - ready for cleanup")
1217
+
1218
+ return blocking_factors
1219
+
1220
+ # ==========================================
1221
+ # Cost Explorer MCP Integration Methods
1222
+ # ==========================================
1223
+
1224
+ def validate_vpc_cost_projections_with_mcp(
1225
+ self, vpc_cost_data: Optional[List[Dict[str, Any]]] = None, target_savings: float = 7548
1226
+ ) -> Dict[str, Any]:
1227
+ """
1228
+ Validate VPC cost projections using Cost Explorer MCP integration.
1229
+
1230
+ Phase 2 critical implementation: Validates $7,548+ annual savings target
1231
+ with ≥99.5% accuracy requirement using real AWS Cost Explorer data.
1232
+
1233
+ Args:
1234
+ vpc_cost_data: VPC cost data for validation (uses test data if None)
1235
+ target_savings: Target annual savings (default: $7,548)
1236
+
1237
+ Returns:
1238
+ Comprehensive cost validation with enterprise accuracy requirements
1239
+ """
1240
+ print_success("💰 Phase 2: Cost Explorer MCP Integration - Financial Validation")
1241
+
1242
+ # Use test data if vpc_cost_data not provided
1243
+ if vpc_cost_data is None:
1244
+ if self.test_mode and self.test_data_loader:
1245
+ vpc_cost_data = self._extract_cost_data_from_test_data()
1246
+ else:
1247
+ return {"error": "No VPC cost data available for validation"}
1248
+
1249
+ if self.have_runbooks and hasattr(self, "cost_explorer_mcp"):
1250
+ try:
1251
+ # Phase 2: Cost Explorer MCP validation
1252
+ cost_validation_results = self.cost_explorer_mcp.validate_vpc_cost_projections(
1253
+ vpc_cost_data=vpc_cost_data, validation_period_days=90
1254
+ )
1255
+
1256
+ # Generate executive report
1257
+ executive_report = self.cost_explorer_mcp.generate_cost_validation_report(
1258
+ cost_validation_results, target_savings=target_savings
1259
+ )
1260
+
1261
+ return {
1262
+ "source": "cost_explorer_mcp_integration",
1263
+ "phase": "Phase 2: Cost Explorer MCP Validation",
1264
+ "cost_validation": cost_validation_results,
1265
+ "executive_report": executive_report,
1266
+ "target_savings": target_savings,
1267
+ "accuracy_achieved": cost_validation_results.get("accuracy_score", 0),
1268
+ "accuracy_requirement": 99.5,
1269
+ "validation_passed": cost_validation_results.get("validation_passed", False),
1270
+ "business_readiness": executive_report.get("executive_summary", {}).get(
1271
+ "business_readiness", False
1272
+ ),
1273
+ "enterprise_coordination": "sre_automation_specialist_phase_2_complete",
1274
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1275
+ }
1276
+
1277
+ except Exception as e:
1278
+ print_error(f"Cost Explorer MCP validation failed: {e}")
1279
+ return self._fallback_cost_validation_summary(vpc_cost_data, target_savings)
1280
+
1281
+ # Fallback validation if Cost Explorer MCP unavailable
1282
+ return self._fallback_cost_validation_summary(vpc_cost_data, target_savings)
1283
+
1284
+ def validate_test_data_business_metrics_with_mcp(self) -> Dict[str, Any]:
1285
+ """
1286
+ Validate business metrics from vpc-test-data-production.yaml using Cost Explorer MCP.
1287
+
1288
+ Validates the $11,070 annual savings target against real AWS billing data
1289
+ with ≥99.5% accuracy requirement for Phase 2 completion.
1290
+ """
1291
+ print_success("🧪 Validating test data business metrics with Cost Explorer MCP")
1292
+
1293
+ if not self.test_mode:
1294
+ return {"error": "Test mode not available - no test data loaded"}
1295
+
1296
+ if self.have_runbooks and hasattr(self, "cost_explorer_mcp"):
1297
+ try:
1298
+ # Validate business metrics from test data
1299
+ test_data_path = str(self.test_data_loader.test_data_path)
1300
+ business_validation = self.cost_explorer_mcp.validate_test_data_business_metrics(test_data_path)
1301
+
1302
+ # Check if validation meets Phase 2 requirements
1303
+ accuracy = business_validation.get("test_data_validation", {}).get("validation_accuracy", 0)
1304
+ validation_passed = accuracy >= 99.5
1305
+ exceeds_target = business_validation.get("test_data_validation", {}).get("exceeds_target_7548", False)
1306
+
1307
+ return {
1308
+ "source": "cost_explorer_mcp_business_validation",
1309
+ "phase_2_status": "COMPLETE" if validation_passed and exceeds_target else "REQUIRES_REVIEW",
1310
+ "business_validation": business_validation,
1311
+ "accuracy_achieved": accuracy,
1312
+ "accuracy_requirement": 99.5,
1313
+ "meets_accuracy_requirement": validation_passed,
1314
+ "exceeds_7548_target": exceeds_target,
1315
+ "test_data_source": test_data_path,
1316
+ "enterprise_coordination": "sre_automation_specialist_business_metrics_validated",
1317
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1318
+ }
1319
+
1320
+ except Exception as e:
1321
+ print_error(f"Business metrics validation failed: {e}")
1322
+ return {"error": f"Business metrics MCP validation failed: {str(e)}"}
1323
+
1324
+ # Fallback validation without MCP
1325
+ return self._fallback_business_metrics_validation()
1326
+
1327
+ def _extract_cost_data_from_test_data(self) -> List[Dict[str, Any]]:
1328
+ """Extract cost data from test data for MCP validation."""
1329
+ if not self.test_data_loader or not self.test_mode:
1330
+ return []
1331
+
1332
+ # Get active VPCs from test data
1333
+ active_vpcs = self.test_data_loader.get_active_vpcs()
1334
+
1335
+ # Convert to cost validation format
1336
+ cost_data = []
1337
+ for vpc in active_vpcs:
1338
+ cost_entry = {
1339
+ "vpc_id": vpc.get("vpc_id", "unknown"),
1340
+ "name": vpc.get("name", "Unknown"),
1341
+ "region": vpc.get("region", "unknown"),
1342
+ "cost_monthly": vpc.get("cost_monthly", 0),
1343
+ "cost_annual": vpc.get("cost_annual", vpc.get("cost_monthly", 0) * 12),
1344
+ "decision": vpc.get("decision", "ANALYZE"),
1345
+ "cleanup_priority": vpc.get("cleanup_priority", "MEDIUM"),
1346
+ }
1347
+ cost_data.append(cost_entry)
1348
+
1349
+ return cost_data
1350
+
1351
+ def _fallback_cost_validation_summary(
1352
+ self, vpc_cost_data: List[Dict[str, Any]], target_savings: float
1353
+ ) -> Dict[str, Any]:
1354
+ """Fallback cost validation summary when MCP unavailable."""
1355
+ total_projected_savings = sum(vpc.get("cost_annual", vpc.get("cost_monthly", 0) * 12) for vpc in vpc_cost_data)
1356
+
1357
+ return {
1358
+ "source": "fallback_cost_validation_summary",
1359
+ "total_projected_savings": total_projected_savings,
1360
+ "target_savings": target_savings,
1361
+ "exceeds_target": total_projected_savings >= target_savings,
1362
+ "accuracy_score": 85.0, # Conservative fallback accuracy
1363
+ "validation_passed": False, # Cannot pass without MCP
1364
+ "limitation": "Cost Explorer MCP integration required for ≥99.5% accuracy",
1365
+ "recommendation": "Enable Cost Explorer MCP server for Phase 2 completion",
1366
+ "next_steps": [
1367
+ "Configure Cost Explorer MCP server",
1368
+ "Validate BILLING_PROFILE access",
1369
+ "Retry with MCP integration",
1370
+ ],
1371
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1372
+ }
1373
+
1374
+ def _fallback_business_metrics_validation(self) -> Dict[str, Any]:
1375
+ """Fallback business metrics validation without MCP."""
1376
+ business_metrics = self.test_data_loader.get_business_metrics() if self.test_data_loader else {}
1377
+
1378
+ return {
1379
+ "source": "fallback_business_metrics_validation",
1380
+ "business_metrics": business_metrics,
1381
+ "annual_savings": business_metrics.get("annual_savings", 0),
1382
+ "validation_accuracy": 85.0, # Conservative fallback
1383
+ "meets_accuracy_requirement": False,
1384
+ "limitation": "Cost Explorer MCP integration required",
1385
+ "recommendation": "Enable MCP integration for accurate business validation",
1386
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1387
+ }
1388
+
1389
+ def validate_test_mode_accuracy(self) -> Dict[str, Any]:
1390
+ """Validate test mode implementation accuracy against expected business metrics."""
1391
+ if not self.test_mode:
1392
+ return {"error": "Test mode not available - no test data loaded"}
1393
+
1394
+ print_success("🧪 Validating test mode accuracy against business metrics...")
1395
+
1396
+ # Run test mode analysis
1397
+ test_results = self._test_mode_vpc_analysis()
1398
+
1399
+ business_metrics = self.test_data_loader.get_business_metrics()
1400
+ expected_savings = business_metrics.get("annual_savings", 0)
1401
+ actual_savings = test_results["financial_analysis"]["annual_savings_potential"]
1402
+
1403
+ # Validation metrics
1404
+ savings_accuracy = (actual_savings / expected_savings * 100) if expected_savings > 0 else 0
1405
+ vpc_count_accuracy = test_results["total_vpcs_analyzed"] / business_metrics.get("total_vpcs", 1) * 100
1406
+
1407
+ validation_passed = (
1408
+ abs(savings_accuracy - 100) <= 10 # Within 10% of expected savings
1409
+ and vpc_count_accuracy >= 90 # At least 90% of VPCs analyzed
1410
+ )
1411
+
1412
+ return {
1413
+ "validation_passed": validation_passed,
1414
+ "savings_accuracy_percentage": round(savings_accuracy, 1),
1415
+ "vpc_count_accuracy_percentage": round(vpc_count_accuracy, 1),
1416
+ "expected_annual_savings": expected_savings,
1417
+ "actual_annual_savings": round(actual_savings, 2),
1418
+ "expected_vpc_count": business_metrics.get("total_vpcs", 0),
1419
+ "actual_vpc_count": test_results["total_vpcs_analyzed"],
1420
+ "test_regions": len(test_results["region_summary"]),
1421
+ "cleanup_ready_vpcs": test_results["financial_analysis"]["cleanup_ready_vpcs"],
1422
+ "validation_timestamp": datetime.now(timezone.utc).isoformat(),
1423
+ "recommendation": "Test mode validation complete - ready for real AWS profile testing"
1424
+ if validation_passed
1425
+ else "Test mode needs adjustment - check business metrics alignment",
1426
+ }