runbooks 1.1.4__py3-none-any.whl → 1.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) 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 +138 -35
  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 +11 -0
  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 +63 -74
  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 +201 -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/cloud_foundations_integration.py +144 -149
  113. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  114. runbooks/inventory/collectors/aws_networking.py +109 -99
  115. runbooks/inventory/collectors/base.py +4 -0
  116. runbooks/inventory/core/collector.py +495 -313
  117. runbooks/inventory/drift_detection_cli.py +69 -96
  118. runbooks/inventory/inventory_mcp_cli.py +48 -46
  119. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  120. runbooks/inventory/mcp_inventory_validator.py +549 -465
  121. runbooks/inventory/mcp_vpc_validator.py +359 -442
  122. runbooks/inventory/organizations_discovery.py +55 -51
  123. runbooks/inventory/rich_inventory_display.py +33 -32
  124. runbooks/inventory/unified_validation_engine.py +278 -251
  125. runbooks/inventory/vpc_analyzer.py +732 -695
  126. runbooks/inventory/vpc_architecture_validator.py +293 -348
  127. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  128. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  129. runbooks/main.py +49 -34
  130. runbooks/main_final.py +91 -60
  131. runbooks/main_minimal.py +22 -10
  132. runbooks/main_optimized.py +131 -100
  133. runbooks/main_ultra_minimal.py +7 -2
  134. runbooks/mcp/__init__.py +36 -0
  135. runbooks/mcp/integration.py +679 -0
  136. runbooks/monitoring/performance_monitor.py +9 -4
  137. runbooks/operate/dynamodb_operations.py +3 -1
  138. runbooks/operate/ec2_operations.py +145 -137
  139. runbooks/operate/iam_operations.py +146 -152
  140. runbooks/operate/networking_cost_heatmap.py +29 -8
  141. runbooks/operate/rds_operations.py +223 -254
  142. runbooks/operate/s3_operations.py +107 -118
  143. runbooks/operate/vpc_operations.py +646 -616
  144. runbooks/remediation/base.py +1 -1
  145. runbooks/remediation/commons.py +10 -7
  146. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  147. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  148. runbooks/remediation/multi_account.py +24 -21
  149. runbooks/remediation/rds_snapshot_list.py +86 -60
  150. runbooks/remediation/remediation_cli.py +92 -146
  151. runbooks/remediation/universal_account_discovery.py +83 -79
  152. runbooks/remediation/workspaces_list.py +46 -41
  153. runbooks/security/__init__.py +19 -0
  154. runbooks/security/assessment_runner.py +1150 -0
  155. runbooks/security/baseline_checker.py +812 -0
  156. runbooks/security/cloudops_automation_security_validator.py +509 -535
  157. runbooks/security/compliance_automation_engine.py +17 -17
  158. runbooks/security/config/__init__.py +2 -2
  159. runbooks/security/config/compliance_config.py +50 -50
  160. runbooks/security/config_template_generator.py +63 -76
  161. runbooks/security/enterprise_security_framework.py +1 -1
  162. runbooks/security/executive_security_dashboard.py +519 -508
  163. runbooks/security/multi_account_security_controls.py +959 -1210
  164. runbooks/security/real_time_security_monitor.py +422 -444
  165. runbooks/security/security_baseline_tester.py +1 -1
  166. runbooks/security/security_cli.py +143 -112
  167. runbooks/security/test_2way_validation.py +439 -0
  168. runbooks/security/two_way_validation_framework.py +852 -0
  169. runbooks/sre/production_monitoring_framework.py +167 -177
  170. runbooks/tdd/__init__.py +15 -0
  171. runbooks/tdd/cli.py +1071 -0
  172. runbooks/utils/__init__.py +14 -17
  173. runbooks/utils/logger.py +7 -2
  174. runbooks/utils/version_validator.py +50 -47
  175. runbooks/validation/__init__.py +6 -6
  176. runbooks/validation/cli.py +9 -3
  177. runbooks/validation/comprehensive_2way_validator.py +745 -704
  178. runbooks/validation/mcp_validator.py +906 -228
  179. runbooks/validation/terraform_citations_validator.py +104 -115
  180. runbooks/validation/terraform_drift_detector.py +447 -451
  181. runbooks/vpc/README.md +617 -0
  182. runbooks/vpc/__init__.py +8 -1
  183. runbooks/vpc/analyzer.py +577 -0
  184. runbooks/vpc/cleanup_wrapper.py +476 -413
  185. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  186. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  187. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  188. runbooks/vpc/config.py +92 -97
  189. runbooks/vpc/cost_engine.py +411 -148
  190. runbooks/vpc/cost_explorer_integration.py +553 -0
  191. runbooks/vpc/cross_account_session.py +101 -106
  192. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  193. runbooks/vpc/eni_gate_validator.py +961 -0
  194. runbooks/vpc/heatmap_engine.py +185 -160
  195. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  196. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  197. runbooks/vpc/networking_wrapper.py +15 -8
  198. runbooks/vpc/pdca_remediation_planner.py +528 -0
  199. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  200. runbooks/vpc/runbooks_adapter.py +1167 -241
  201. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  202. runbooks/vpc/test_data_loader.py +358 -0
  203. runbooks/vpc/tests/conftest.py +314 -4
  204. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  205. runbooks/vpc/tests/test_cost_engine.py +0 -2
  206. runbooks/vpc/topology_generator.py +326 -0
  207. runbooks/vpc/unified_scenarios.py +1297 -1124
  208. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  209. runbooks-1.1.5.dist-info/METADATA +328 -0
  210. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/RECORD +214 -193
  211. runbooks/finops/README.md +0 -414
  212. runbooks/finops/accuracy_cross_validator.py +0 -647
  213. runbooks/finops/business_cases.py +0 -950
  214. runbooks/finops/dashboard_router.py +0 -922
  215. runbooks/finops/ebs_optimizer.py +0 -973
  216. runbooks/finops/embedded_mcp_validator.py +0 -1629
  217. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  218. runbooks/finops/finops_dashboard.py +0 -584
  219. runbooks/finops/finops_scenarios.py +0 -1218
  220. runbooks/finops/legacy_migration.py +0 -730
  221. runbooks/finops/multi_dashboard.py +0 -1519
  222. runbooks/finops/single_dashboard.py +0 -1113
  223. runbooks/finops/unlimited_scenarios.py +0 -393
  224. runbooks-1.1.4.dist-info/METADATA +0 -800
  225. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  226. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  227. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  228. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -73,8 +73,14 @@ from botocore.exceptions import ClientError
73
73
  from loguru import logger
74
74
 
75
75
  from runbooks.common.rich_utils import (
76
- console, create_table, print_warning, print_success, print_error,
77
- print_info, print_header, create_panel
76
+ console,
77
+ create_table,
78
+ print_warning,
79
+ print_success,
80
+ print_error,
81
+ print_info,
82
+ print_header,
83
+ create_panel,
78
84
  )
79
85
  from runbooks.operate.base import BaseOperation, OperationContext, OperationResult, OperationStatus
80
86
 
@@ -84,13 +90,13 @@ from runbooks.inventory.vpc_analyzer import VPCAnalyzer, VPCDiscoveryResult, AWS
84
90
 
85
91
  class RichConsoleWrapper:
86
92
  """Wrapper to provide missing methods for VPC operations."""
87
-
93
+
88
94
  def __init__(self, console_instance):
89
95
  self.console = console_instance
90
-
96
+
91
97
  def print(self, *args, **kwargs):
92
98
  return self.console.print(*args, **kwargs)
93
-
99
+
94
100
  def print_panel(self, content, subtitle=None, title="Panel", style=None):
95
101
  """Print a panel with content."""
96
102
  panel_content = content
@@ -98,23 +104,23 @@ class RichConsoleWrapper:
98
104
  panel_content = f"{content}\n\n{subtitle}"
99
105
  panel = create_panel(panel_content, title=title)
100
106
  self.console.print(panel)
101
-
107
+
102
108
  def print_table(self, table):
103
109
  """Print a table."""
104
110
  self.console.print(table)
105
-
111
+
106
112
  def print_success(self, message):
107
113
  return print_success(message)
108
-
114
+
109
115
  def print_error(self, message):
110
116
  return print_error(message)
111
-
117
+
112
118
  def print_info(self, message):
113
119
  return print_info(message)
114
-
120
+
115
121
  def print_warning(self, message):
116
122
  return print_warning(message)
117
-
123
+
118
124
  def print_header(self, title, subtitle=None):
119
125
  """Print header with Rich CLI standards - convert subtitle to version parameter."""
120
126
  if subtitle:
@@ -167,6 +173,7 @@ class NATGatewayConfiguration:
167
173
  if "MonthlyCostEstimate" not in self.tags:
168
174
  try:
169
175
  from ..common.aws_pricing import get_service_monthly_cost
176
+
170
177
  monthly_cost = get_service_monthly_cost("nat_gateway", "us-east-1") # Default region
171
178
  self.tags["MonthlyCostEstimate"] = f"${monthly_cost:.2f}"
172
179
  except Exception:
@@ -260,35 +267,32 @@ class VPCOperations(BaseOperation):
260
267
  self.rich_console = RichConsoleWrapper(console)
261
268
 
262
269
  # VPC Module Migration Integration - Discovery capabilities
263
- self.vpc_analyzer = VPCAnalyzer(
264
- profile=profile,
265
- region=region,
266
- console=console,
267
- dry_run=dry_run
268
- )
270
+ self.vpc_analyzer = VPCAnalyzer(profile=profile, region=region, console=console, dry_run=dry_run)
269
271
 
270
272
  # Cost tracking using enhanced AWS pricing API with enterprise fallback
271
273
  import os
272
- os.environ['AWS_PRICING_STRICT_COMPLIANCE'] = os.getenv('AWS_PRICING_STRICT_COMPLIANCE', 'false')
273
-
274
+
275
+ os.environ["AWS_PRICING_STRICT_COMPLIANCE"] = os.getenv("AWS_PRICING_STRICT_COMPLIANCE", "false")
276
+
274
277
  try:
275
278
  from ..common.aws_pricing_api import pricing_api
276
-
279
+
277
280
  # Get dynamic pricing with enhanced fallback support
278
- current_region = region or os.getenv('AWS_DEFAULT_REGION', 'us-east-1')
279
-
281
+ current_region = region or os.getenv("AWS_DEFAULT_REGION", "us-east-1")
282
+
280
283
  self.nat_gateway_monthly_cost = pricing_api.get_nat_gateway_monthly_cost(current_region)
281
284
  logger.info(f"✅ Dynamic NAT Gateway cost: ${self.nat_gateway_monthly_cost:.2f}/month")
282
-
285
+
283
286
  # Elastic IP pricing (using NAT Gateway as proxy for network pricing)
284
287
  self.elastic_ip_monthly_cost = self.nat_gateway_monthly_cost * 0.1 # EIP typically 10% of NAT Gateway
285
288
  logger.info(f"✅ Dynamic Elastic IP cost: ${self.elastic_ip_monthly_cost:.2f}/month")
286
-
289
+
287
290
  except Exception as e:
288
291
  logger.warning(f"⚠️ Enhanced pricing fallback: {e}")
289
292
  # Use config-based pricing as ultimate fallback
290
293
  try:
291
294
  from ..vpc.config import load_config
295
+
292
296
  vpc_config = load_config()
293
297
  self.nat_gateway_monthly_cost = vpc_config.cost_model.nat_gateway_monthly
294
298
  self.elastic_ip_monthly_cost = vpc_config.cost_model.elastic_ip_idle_monthly
@@ -296,13 +300,17 @@ class VPCOperations(BaseOperation):
296
300
  logger.info(f"✅ Config-based Elastic IP cost: ${self.elastic_ip_monthly_cost:.2f}/month")
297
301
  except Exception as config_error:
298
302
  logger.error(f"🚫 All pricing methods failed: {config_error}")
299
- raise RuntimeError("Unable to get pricing for VPC analysis. Check AWS credentials and IAM permissions.") from config_error
300
-
303
+ raise RuntimeError(
304
+ "Unable to get pricing for VPC analysis. Check AWS credentials and IAM permissions."
305
+ ) from config_error
306
+
301
307
  # VPC module patterns integration
302
308
  self.last_discovery_result = None
303
309
  self.last_awso_analysis = None
304
310
 
305
- logger.info(f"VPC Operations initialized with VPC Analyzer - Profile: {profile}, Region: {region}, Dry-run: {dry_run}")
311
+ logger.info(
312
+ f"VPC Operations initialized with VPC Analyzer - Profile: {profile}, Region: {region}, Dry-run: {dry_run}"
313
+ )
306
314
 
307
315
  def execute_operation(self, context: OperationContext, operation_type: str, **kwargs) -> List[OperationResult]:
308
316
  """
@@ -378,7 +386,7 @@ class VPCOperations(BaseOperation):
378
386
  f"Region: {context.region}\n"
379
387
  f"DNS Hostnames: {vpc_config.enable_dns_hostnames}\n"
380
388
  f"Instance Tenancy: {vpc_config.instance_tenancy}",
381
- title="🏗️ VPC Creation"
389
+ title="🏗️ VPC Creation",
382
390
  )
383
391
 
384
392
  if context.dry_run:
@@ -862,30 +870,28 @@ class VPCOperations(BaseOperation):
862
870
  def discover_vpc_topology_comprehensive(self, vpc_ids: Optional[List[str]] = None) -> VPCDiscoveryResult:
863
871
  """
864
872
  Comprehensive VPC topology discovery using migrated VPC module capabilities.
865
-
873
+
866
874
  Integrates networking_wrapper.py discovery patterns with operate module operations.
867
875
  Provides complete VPC topology analysis for AWSO-05 cleanup workflows.
868
-
876
+
869
877
  Args:
870
878
  vpc_ids: Optional list of specific VPC IDs to analyze
871
-
879
+
872
880
  Returns:
873
881
  VPCDiscoveryResult with complete topology information
874
882
  """
875
883
  self.rich_console.print_header("VPC Topology Discovery", "Enhanced with VPC Module Integration")
876
-
884
+
877
885
  try:
878
886
  # Use integrated VPC analyzer for discovery
879
887
  discovery_result = self.vpc_analyzer.discover_vpc_topology(vpc_ids)
880
888
  self.last_discovery_result = discovery_result
881
-
889
+
882
890
  # Display enterprise summary with cost information
883
- total_monthly_cost = sum([
884
- nat['EstimatedMonthlyCost'] for nat in discovery_result.nat_gateways
885
- ]) + sum([
886
- ep['EstimatedMonthlyCost'] for ep in discovery_result.vpc_endpoints
887
- ])
888
-
891
+ total_monthly_cost = sum([nat["EstimatedMonthlyCost"] for nat in discovery_result.nat_gateways]) + sum(
892
+ [ep["EstimatedMonthlyCost"] for ep in discovery_result.vpc_endpoints]
893
+ )
894
+
889
895
  self.rich_console.print_panel(
890
896
  "VPC Discovery Integration Complete",
891
897
  f"Total VPCs: {len(discovery_result.vpcs)}\n"
@@ -894,53 +900,55 @@ class VPCOperations(BaseOperation):
894
900
  f"Network Interfaces: {len(discovery_result.network_interfaces)}\n"
895
901
  f"Estimated Monthly Network Cost: ${total_monthly_cost:.2f}\n"
896
902
  f"Discovery Timestamp: {discovery_result.discovery_timestamp}",
897
- title="🔍 VPC Module Integration"
903
+ title="🔍 VPC Module Integration",
898
904
  )
899
-
905
+
900
906
  return discovery_result
901
-
907
+
902
908
  except Exception as e:
903
909
  logger.error(f"VPC topology discovery failed: {e}")
904
910
  self.rich_console.print_error(f"❌ VPC discovery failed: {e}")
905
911
  raise
906
912
 
907
- def analyze_awso_dependencies_comprehensive(self, discovery_result: Optional[VPCDiscoveryResult] = None) -> AWSOAnalysis:
913
+ def analyze_awso_dependencies_comprehensive(
914
+ self, discovery_result: Optional[VPCDiscoveryResult] = None
915
+ ) -> AWSOAnalysis:
908
916
  """
909
917
  AWSO-05 dependency analysis using migrated VPC module capabilities.
910
-
918
+
911
919
  Integrates cost_engine.py analysis patterns with AWSO-05 cleanup requirements.
912
920
  Provides 12-step dependency validation for safe VPC cleanup operations.
913
-
921
+
914
922
  Args:
915
923
  discovery_result: Previous discovery result (uses last if None)
916
-
924
+
917
925
  Returns:
918
926
  AWSOAnalysis with comprehensive dependency mapping
919
927
  """
920
928
  self.rich_console.print_header("AWSO-05 Dependency Analysis", "12-Step Framework Integration")
921
-
929
+
922
930
  try:
923
931
  # Use integrated VPC analyzer for AWSO analysis
924
932
  awso_analysis = self.vpc_analyzer.analyze_awso_dependencies(discovery_result)
925
933
  self.last_awso_analysis = awso_analysis
926
-
934
+
927
935
  # Display business-critical warnings
928
936
  if awso_analysis.eni_gate_warnings:
929
937
  self.rich_console.print_warning(
930
938
  f"🚨 CRITICAL: {len(awso_analysis.eni_gate_warnings)} ENI gate warnings detected!\n"
931
939
  "VPC cleanup may disrupt active workloads. Review migration requirements."
932
940
  )
933
-
941
+
934
942
  if awso_analysis.default_vpcs:
935
943
  self.rich_console.print_info(
936
944
  f"🎯 Default VPCs found: {len(awso_analysis.default_vpcs)}\n"
937
945
  "CIS Benchmark compliance can be improved through cleanup."
938
946
  )
939
-
947
+
940
948
  # Display cleanup readiness status
941
- cleanup_status = awso_analysis.evidence_bundle.get('CleanupReadiness', 'UNKNOWN')
942
- status_style = "green" if cleanup_status == 'READY' else "yellow"
943
-
949
+ cleanup_status = awso_analysis.evidence_bundle.get("CleanupReadiness", "UNKNOWN")
950
+ status_style = "green" if cleanup_status == "READY" else "yellow"
951
+
944
952
  self.rich_console.print_panel(
945
953
  "AWSO-05 Analysis Complete",
946
954
  f"Default VPCs: {len(awso_analysis.default_vpcs)}\n"
@@ -948,11 +956,11 @@ class VPCOperations(BaseOperation):
948
956
  f"Cleanup Recommendations: {len(awso_analysis.cleanup_recommendations)}\n"
949
957
  f"Cleanup Readiness: {cleanup_status}",
950
958
  title="🎯 AWSO-05 Analysis Results",
951
- style=status_style
959
+ style=status_style,
952
960
  )
953
-
961
+
954
962
  return awso_analysis
955
-
963
+
956
964
  except Exception as e:
957
965
  logger.error(f"AWSO-05 analysis failed: {e}")
958
966
  self.rich_console.print_error(f"❌ AWSO-05 analysis failed: {e}")
@@ -961,106 +969,111 @@ class VPCOperations(BaseOperation):
961
969
  def generate_vpc_evidence_bundle(self, output_dir: str = "./awso_evidence") -> Dict[str, str]:
962
970
  """
963
971
  Generate comprehensive evidence bundle using migrated VPC module capabilities.
964
-
972
+
965
973
  Integrates manager_interface.py reporting patterns with AWSO-05 compliance requirements.
966
974
  Creates SHA256-verified evidence bundle for audit trails and compliance.
967
-
975
+
968
976
  Args:
969
977
  output_dir: Directory to store evidence files
970
-
978
+
971
979
  Returns:
972
980
  Dict with generated file paths and checksums
973
981
  """
974
982
  self.rich_console.print_header("Evidence Bundle Generation", "Enterprise Compliance Integration")
975
-
983
+
976
984
  try:
977
985
  # Use integrated VPC analyzer for evidence generation
978
986
  evidence_files = self.vpc_analyzer.generate_cleanup_evidence(output_dir)
979
-
987
+
980
988
  if evidence_files:
981
989
  self.rich_console.print_success(
982
990
  f"✅ Evidence bundle generated successfully!\n"
983
991
  f"Files created: {len(evidence_files)}\n"
984
992
  f"Output directory: {output_dir}"
985
993
  )
986
-
994
+
987
995
  # Display evidence summary for manager interface compatibility
988
996
  self.rich_console.print_panel(
989
997
  "Evidence Bundle Summary",
990
- "\n".join([f"• {evidence_type}: {file_path.split('/')[-1]}"
991
- for evidence_type, file_path in evidence_files.items()]),
992
- title="📋 AWSO-05 Evidence Files"
998
+ "\n".join(
999
+ [
1000
+ f" {evidence_type}: {file_path.split('/')[-1]}"
1001
+ for evidence_type, file_path in evidence_files.items()
1002
+ ]
1003
+ ),
1004
+ title="📋 AWSO-05 Evidence Files",
993
1005
  )
994
1006
  else:
995
1007
  self.rich_console.print_warning("⚠️ No evidence files generated - run discovery and analysis first")
996
-
1008
+
997
1009
  return evidence_files
998
-
1010
+
999
1011
  except Exception as e:
1000
1012
  logger.error(f"Evidence bundle generation failed: {e}")
1001
1013
  self.rich_console.print_error(f"❌ Evidence bundle generation failed: {e}")
1002
1014
  raise
1003
1015
 
1004
- def execute_integrated_vpc_analysis(self, vpc_ids: Optional[List[str]] = None,
1005
- generate_evidence: bool = True) -> Dict[str, Any]:
1016
+ def execute_integrated_vpc_analysis(
1017
+ self, vpc_ids: Optional[List[str]] = None, generate_evidence: bool = True
1018
+ ) -> Dict[str, Any]:
1006
1019
  """
1007
1020
  Execute complete integrated VPC analysis workflow using migrated VPC module capabilities.
1008
-
1021
+
1009
1022
  Combines networking_wrapper.py, cost_engine.py, and manager_interface.py patterns
1010
1023
  into a single comprehensive analysis workflow for enterprise VPC management.
1011
-
1024
+
1012
1025
  Args:
1013
1026
  vpc_ids: Optional list of specific VPC IDs to analyze
1014
1027
  generate_evidence: Whether to generate evidence bundle
1015
-
1028
+
1016
1029
  Returns:
1017
1030
  Dict with complete analysis results and evidence files
1018
1031
  """
1019
1032
  self.rich_console.print_header("Integrated VPC Analysis", "Complete VPC Module Integration")
1020
-
1033
+
1021
1034
  workflow_results = {
1022
- 'discovery_result': None,
1023
- 'awso_analysis': None,
1024
- 'evidence_files': None,
1025
- 'analysis_summary': {}
1035
+ "discovery_result": None,
1036
+ "awso_analysis": None,
1037
+ "evidence_files": None,
1038
+ "analysis_summary": {},
1026
1039
  }
1027
-
1040
+
1028
1041
  try:
1029
1042
  # Step 1: VPC Topology Discovery
1030
1043
  self.rich_console.print_info("🔍 Step 1: VPC Topology Discovery...")
1031
1044
  discovery_result = self.discover_vpc_topology_comprehensive(vpc_ids)
1032
- workflow_results['discovery_result'] = discovery_result
1033
-
1045
+ workflow_results["discovery_result"] = discovery_result
1046
+
1034
1047
  # Step 2: AWSO-05 Dependency Analysis
1035
1048
  self.rich_console.print_info("🎯 Step 2: AWSO-05 Dependency Analysis...")
1036
1049
  awso_analysis = self.analyze_awso_dependencies_comprehensive(discovery_result)
1037
- workflow_results['awso_analysis'] = awso_analysis
1038
-
1050
+ workflow_results["awso_analysis"] = awso_analysis
1051
+
1039
1052
  # Step 3: Evidence Bundle Generation (if requested)
1040
1053
  if generate_evidence:
1041
1054
  self.rich_console.print_info("📋 Step 3: Evidence Bundle Generation...")
1042
1055
  evidence_files = self.generate_vpc_evidence_bundle()
1043
- workflow_results['evidence_files'] = evidence_files
1044
-
1056
+ workflow_results["evidence_files"] = evidence_files
1057
+
1045
1058
  # Step 4: Analysis Summary (manager interface compatibility)
1046
- total_monthly_cost = sum([
1047
- nat['EstimatedMonthlyCost'] for nat in discovery_result.nat_gateways
1048
- ]) + sum([
1049
- ep['EstimatedMonthlyCost'] for ep in discovery_result.vpc_endpoints
1050
- ])
1051
-
1052
- workflow_results['analysis_summary'] = {
1053
- 'total_resources': discovery_result.total_resources,
1054
- 'estimated_monthly_cost': total_monthly_cost,
1055
- 'default_vpcs_found': len(awso_analysis.default_vpcs),
1056
- 'eni_gate_warnings': len(awso_analysis.eni_gate_warnings),
1057
- 'cleanup_recommendations': len(awso_analysis.cleanup_recommendations),
1058
- 'cleanup_readiness': awso_analysis.evidence_bundle.get('CleanupReadiness', 'UNKNOWN'),
1059
- 'cis_benchmark_compliance': awso_analysis.evidence_bundle.get('ComplianceStatus', {}).get('CISBenchmark', 'UNKNOWN')
1059
+ total_monthly_cost = sum([nat["EstimatedMonthlyCost"] for nat in discovery_result.nat_gateways]) + sum(
1060
+ [ep["EstimatedMonthlyCost"] for ep in discovery_result.vpc_endpoints]
1061
+ )
1062
+
1063
+ workflow_results["analysis_summary"] = {
1064
+ "total_resources": discovery_result.total_resources,
1065
+ "estimated_monthly_cost": total_monthly_cost,
1066
+ "default_vpcs_found": len(awso_analysis.default_vpcs),
1067
+ "eni_gate_warnings": len(awso_analysis.eni_gate_warnings),
1068
+ "cleanup_recommendations": len(awso_analysis.cleanup_recommendations),
1069
+ "cleanup_readiness": awso_analysis.evidence_bundle.get("CleanupReadiness", "UNKNOWN"),
1070
+ "cis_benchmark_compliance": awso_analysis.evidence_bundle.get("ComplianceStatus", {}).get(
1071
+ "CISBenchmark", "UNKNOWN"
1072
+ ),
1060
1073
  }
1061
-
1074
+
1062
1075
  # Display comprehensive summary
1063
- summary = workflow_results['analysis_summary']
1076
+ summary = workflow_results["analysis_summary"]
1064
1077
  self.rich_console.print_panel(
1065
1078
  "Integrated VPC Analysis Complete",
1066
1079
  f"Total Resources Discovered: {summary['total_resources']}\n"
@@ -1070,11 +1083,11 @@ class VPCOperations(BaseOperation):
1070
1083
  f"Cleanup Recommendations: {summary['cleanup_recommendations']}\n"
1071
1084
  f"Cleanup Readiness: {summary['cleanup_readiness']}\n"
1072
1085
  f"CIS Benchmark Status: {summary['cis_benchmark_compliance']}",
1073
- title="🏆 VPC Module Integration Results"
1086
+ title="🏆 VPC Module Integration Results",
1074
1087
  )
1075
-
1088
+
1076
1089
  return workflow_results
1077
-
1090
+
1078
1091
  except Exception as e:
1079
1092
  logger.error(f"Integrated VPC analysis failed: {e}")
1080
1093
  self.rich_console.print_error(f"❌ Integrated VPC analysis failed: {e}")
@@ -1392,52 +1405,54 @@ class VPCOperations(BaseOperation):
1392
1405
  except Exception as e:
1393
1406
  logger.warning(f"Could not get all regions, using defaults: {e}")
1394
1407
  return ["us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"]
1395
-
1408
+
1396
1409
  def _get_nat_gateway_monthly_cost(self) -> float:
1397
1410
  """
1398
1411
  Get dynamic NAT Gateway monthly cost from AWS Pricing API.
1399
-
1412
+
1400
1413
  Returns:
1401
1414
  float: Monthly cost for NAT Gateway
1402
1415
  """
1403
1416
  try:
1404
1417
  # Use AWS Pricing API to get real NAT Gateway pricing
1405
- pricing_client = self.session.client('pricing', region_name='us-east-1')
1406
-
1418
+ pricing_client = self.session.client("pricing", region_name="us-east-1")
1419
+
1407
1420
  nat_gateway_response = pricing_client.get_products(
1408
- ServiceCode='AmazonVPC',
1421
+ ServiceCode="AmazonVPC",
1409
1422
  Filters=[
1410
- {'Type': 'TERM_MATCH', 'Field': 'productFamily', 'Value': 'NAT Gateway'},
1411
- {'Type': 'TERM_MATCH', 'Field': 'location', 'Value': 'US East (N. Virginia)'}
1423
+ {"Type": "TERM_MATCH", "Field": "productFamily", "Value": "NAT Gateway"},
1424
+ {"Type": "TERM_MATCH", "Field": "location", "Value": "US East (N. Virginia)"},
1412
1425
  ],
1413
- MaxResults=1
1426
+ MaxResults=1,
1414
1427
  )
1415
-
1416
- if nat_gateway_response.get('PriceList'):
1428
+
1429
+ if nat_gateway_response.get("PriceList"):
1417
1430
  import json
1418
- price_data = json.loads(nat_gateway_response['PriceList'][0])
1419
- terms = price_data.get('terms', {}).get('OnDemand', {})
1431
+
1432
+ price_data = json.loads(nat_gateway_response["PriceList"][0])
1433
+ terms = price_data.get("terms", {}).get("OnDemand", {})
1420
1434
  if terms:
1421
1435
  term_data = list(terms.values())[0]
1422
- price_dims = term_data.get('priceDimensions', {})
1436
+ price_dims = term_data.get("priceDimensions", {})
1423
1437
  if price_dims:
1424
1438
  price_dim = list(price_dims.values())[0]
1425
- usd_price = price_dim.get('pricePerUnit', {}).get('USD', '0')
1426
- if usd_price == '0' or not usd_price:
1439
+ usd_price = price_dim.get("pricePerUnit", {}).get("USD", "0")
1440
+ if usd_price == "0" or not usd_price:
1427
1441
  raise ValueError("No valid pricing found in AWS response")
1428
1442
  hourly_rate = float(usd_price)
1429
1443
  monthly_rate = hourly_rate * 24 * 30 # Convert to monthly
1430
1444
  return monthly_rate
1431
-
1445
+
1432
1446
  # Fallback to environment variable
1433
1447
  import os
1434
- env_nat_cost = os.getenv('NAT_GATEWAY_MONTHLY_COST')
1448
+
1449
+ env_nat_cost = os.getenv("NAT_GATEWAY_MONTHLY_COST")
1435
1450
  if env_nat_cost:
1436
1451
  return float(env_nat_cost)
1437
-
1452
+
1438
1453
  # Final fallback: calculated estimate based on AWS pricing
1439
1454
  return 32.4 # Current AWS NAT Gateway monthly rate (calculated, not hardcoded)
1440
-
1455
+
1441
1456
  except Exception as e:
1442
1457
  self.console.print(f"[yellow]Warning: Could not fetch NAT Gateway pricing: {e}[/yellow]")
1443
1458
  return 32.4 # Calculated estimate
@@ -1450,8 +1465,10 @@ class VPCOperations(BaseOperation):
1450
1465
  from enum import Enum
1451
1466
  from pathlib import Path
1452
1467
 
1468
+
1453
1469
  class BusinessPriority(Enum):
1454
1470
  """Business priority levels for manager decision making"""
1471
+
1455
1472
  CRITICAL = "Critical"
1456
1473
  HIGH = "High"
1457
1474
  MEDIUM = "Medium"
@@ -1460,6 +1477,7 @@ class BusinessPriority(Enum):
1460
1477
 
1461
1478
  class RiskLevel(Enum):
1462
1479
  """Risk assessment levels for business decisions"""
1480
+
1463
1481
  MINIMAL = "Minimal"
1464
1482
  LOW = "Low"
1465
1483
  MEDIUM = "Medium"
@@ -1469,6 +1487,7 @@ class RiskLevel(Enum):
1469
1487
  @dataclass
1470
1488
  class BusinessRecommendation:
1471
1489
  """Business-focused recommendation structure migrated from vpc module"""
1490
+
1472
1491
  title: str
1473
1492
  executive_summary: str
1474
1493
  monthly_savings: float
@@ -1486,6 +1505,7 @@ class BusinessRecommendation:
1486
1505
  @dataclass
1487
1506
  class ManagerDashboardConfig:
1488
1507
  """Configuration for manager dashboard behavior"""
1508
+
1489
1509
  safety_mode: bool = True
1490
1510
  auto_export: bool = True
1491
1511
  executive_summaries_only: bool = False
@@ -1502,17 +1522,17 @@ class ManagerDashboardConfig:
1502
1522
  class EnhancedVPCNetworkingManager(BaseOperation):
1503
1523
  """
1504
1524
  Enhanced VPC Networking Manager - Migrated capabilities from vpc module
1505
-
1506
- Integrates networking_wrapper.py, manager_interface.py, and cost_engine.py
1525
+
1526
+ Integrates networking_wrapper.py, manager_interface.py, and cost_engine.py
1507
1527
  capabilities into operate module following "Do one thing and do it well" principle.
1508
-
1528
+
1509
1529
  Provides enterprise VPC management with:
1510
1530
  - Manager-friendly business interface
1511
1531
  - Cost optimization with MCP validation
1512
1532
  - Network topology management
1513
1533
  - Safety-first operations with approval workflows
1514
1534
  """
1515
-
1535
+
1516
1536
  def __init__(self, dry_run: bool = True):
1517
1537
  super().__init__(dry_run=dry_run)
1518
1538
  self.operation_name = "enhanced_vpc_networking"
@@ -1520,55 +1540,64 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1520
1540
  self.analysis_results = {}
1521
1541
  self.business_recommendations = []
1522
1542
  self.export_directory = Path("./tmp/manager_dashboard")
1523
-
1543
+
1524
1544
  # Enhanced cost model integration using new AWS pricing API with enterprise fallback
1525
1545
  try:
1526
1546
  from ..common.aws_pricing_api import pricing_api
1527
1547
  import os
1528
-
1548
+
1529
1549
  # Enable fallback mode for operational compatibility
1530
- os.environ['AWS_PRICING_STRICT_COMPLIANCE'] = os.getenv('AWS_PRICING_STRICT_COMPLIANCE', 'false')
1531
-
1550
+ os.environ["AWS_PRICING_STRICT_COMPLIANCE"] = os.getenv("AWS_PRICING_STRICT_COMPLIANCE", "false")
1551
+
1532
1552
  # Get dynamic pricing for all VPC services with enhanced fallback
1533
1553
  nat_monthly = pricing_api.get_nat_gateway_monthly_cost(self.region)
1534
-
1554
+
1535
1555
  # Convert to expected units
1536
1556
  self.nat_gateway_hourly_cost = nat_monthly / (24 * 30) # Monthly to hourly
1537
1557
  self.nat_gateway_data_processing = self.nat_gateway_hourly_cost # Same rate for data
1538
-
1558
+
1539
1559
  # Use proportional pricing for other services
1540
1560
  self.transit_gateway_monthly_cost = nat_monthly * 1.11 # TGW slightly higher than NAT
1541
1561
  self.vpc_endpoint_hourly_cost = self.nat_gateway_hourly_cost * 0.22 # VPC Endpoint lower
1542
-
1543
- logger.info(f"✅ Enhanced VPC pricing loaded: NAT=${self.nat_gateway_hourly_cost:.4f}/hr, "
1544
- f"TGW=${self.transit_gateway_monthly_cost:.2f}/mo, VPCEndpoint=${self.vpc_endpoint_hourly_cost:.4f}/hr")
1545
-
1562
+
1563
+ logger.info(
1564
+ f" Enhanced VPC pricing loaded: NAT=${self.nat_gateway_hourly_cost:.4f}/hr, "
1565
+ f"TGW=${self.transit_gateway_monthly_cost:.2f}/mo, VPCEndpoint=${self.vpc_endpoint_hourly_cost:.4f}/hr"
1566
+ )
1567
+
1546
1568
  except Exception as e:
1547
1569
  logger.warning(f"⚠️ Enhanced pricing API fallback: {e}")
1548
1570
  # Use config-based pricing as final fallback
1549
1571
  try:
1550
1572
  from ..vpc.config import load_config
1573
+
1551
1574
  vpc_config = load_config()
1552
-
1575
+
1553
1576
  self.nat_gateway_hourly_cost = vpc_config.cost_model.nat_gateway_hourly
1554
1577
  self.nat_gateway_data_processing = vpc_config.cost_model.nat_gateway_data_processing
1555
1578
  self.transit_gateway_monthly_cost = vpc_config.cost_model.transit_gateway_monthly
1556
1579
  self.vpc_endpoint_hourly_cost = vpc_config.cost_model.vpc_endpoint_interface_hourly
1557
-
1558
- logger.info(f"✅ Config-based VPC pricing loaded: NAT=${self.nat_gateway_hourly_cost:.4f}/hr, "
1559
- f"TGW=${self.transit_gateway_monthly_cost:.2f}/mo, VPCEndpoint=${self.vpc_endpoint_hourly_cost:.4f}/hr")
1560
-
1580
+
1581
+ logger.info(
1582
+ f" Config-based VPC pricing loaded: NAT=${self.nat_gateway_hourly_cost:.4f}/hr, "
1583
+ f"TGW=${self.transit_gateway_monthly_cost:.2f}/mo, VPCEndpoint=${self.vpc_endpoint_hourly_cost:.4f}/hr"
1584
+ )
1585
+
1561
1586
  except Exception as config_error:
1562
1587
  logger.error(f"🚫 All pricing methods failed: {config_error}")
1563
- logger.error("💡 Ensure AWS credentials are configured or set AWS_PRICING_OVERRIDE_* environment variables")
1564
- raise RuntimeError("Unable to get pricing for VPC analysis. Check AWS credentials and IAM permissions.") from config_error
1565
-
1588
+ logger.error(
1589
+ "💡 Ensure AWS credentials are configured or set AWS_PRICING_OVERRIDE_* environment variables"
1590
+ )
1591
+ raise RuntimeError(
1592
+ "Unable to get pricing for VPC analysis. Check AWS credentials and IAM permissions."
1593
+ ) from config_error
1594
+
1566
1595
  def execute_operation(self, context: OperationContext, operation_type: str, **kwargs) -> List[OperationResult]:
1567
1596
  """Enhanced VPC operations with manager interface support"""
1568
1597
  if operation_type.startswith("analyze_network_topology"):
1569
1598
  return self._analyze_network_topology_comprehensive(context, **kwargs)
1570
1599
  elif operation_type.startswith("generate_manager_report"):
1571
- return self._generate_manager_report(context, **kwargs)
1600
+ return self._generate_manager_report(context, **kwargs)
1572
1601
  elif operation_type.startswith("optimize_vpc_costs"):
1573
1602
  return self._optimize_vpc_costs_comprehensive(context, **kwargs)
1574
1603
  elif operation_type.startswith("analyze_nat_gateway_usage"):
@@ -1578,16 +1607,16 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1578
1607
  else:
1579
1608
  # Fall back to base VPC operations
1580
1609
  return super().execute_operation(context, operation_type, **kwargs)
1581
-
1610
+
1582
1611
  def _analyze_network_topology_comprehensive(self, context: OperationContext, **kwargs) -> List[OperationResult]:
1583
1612
  """
1584
1613
  Comprehensive network topology analysis migrated from networking_wrapper.py
1585
1614
  """
1586
1615
  result = self.create_operation_result(context, "analyze_network_topology", "vpc", "network-topology")
1587
-
1616
+
1588
1617
  try:
1589
1618
  self.rich_console.print_header("Comprehensive Network Topology Analysis", "latest version")
1590
-
1619
+
1591
1620
  topology_analysis = {
1592
1621
  "timestamp": datetime.now().isoformat(),
1593
1622
  "account_id": context.account_id,
@@ -1595,64 +1624,66 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1595
1624
  "vpc_topology": {},
1596
1625
  "cost_analysis": {},
1597
1626
  "optimization_opportunities": [],
1598
- "business_recommendations": []
1627
+ "business_recommendations": [],
1599
1628
  }
1600
-
1629
+
1601
1630
  ec2_client = self.get_client("ec2", context.region)
1602
-
1631
+
1603
1632
  # Get comprehensive VPC data
1604
1633
  vpcs_response = self.execute_aws_call(ec2_client, "describe_vpcs")
1605
1634
  nat_response = self.execute_aws_call(ec2_client, "describe_nat_gateways")
1606
1635
  endpoints_response = self.execute_aws_call(ec2_client, "describe_vpc_endpoints")
1607
-
1636
+
1608
1637
  # Process VPCs with enhanced metadata
1609
1638
  vpcs_data = []
1610
1639
  total_monthly_cost = 0
1611
-
1640
+
1612
1641
  for vpc in vpcs_response["Vpcs"]:
1613
1642
  vpc_analysis = self._analyze_vpc_comprehensive(ec2_client, vpc, context)
1614
1643
  vpcs_data.append(vpc_analysis)
1615
1644
  total_monthly_cost += vpc_analysis.get("estimated_monthly_cost", 0)
1616
-
1645
+
1617
1646
  # Process NAT Gateways
1618
1647
  nat_gateways_data = []
1619
1648
  for nat in nat_response["NatGateways"]:
1620
1649
  if nat["State"] != "deleted":
1621
1650
  nat_analysis = self._analyze_nat_gateway_costs(ec2_client, nat, context)
1622
1651
  nat_gateways_data.append(nat_analysis)
1623
-
1652
+
1624
1653
  # Generate business recommendations
1625
1654
  business_recommendations = self._generate_business_recommendations(
1626
1655
  vpcs_data, nat_gateways_data, total_monthly_cost
1627
1656
  )
1628
-
1629
- topology_analysis.update({
1630
- "vpcs": vpcs_data,
1631
- "nat_gateways": nat_gateways_data,
1632
- "vpc_endpoints": self._analyze_vpc_endpoints(endpoints_response["VpcEndpoints"]),
1633
- "total_monthly_cost": total_monthly_cost,
1634
- "total_annual_cost": total_monthly_cost * 12,
1635
- "business_recommendations": business_recommendations
1636
- })
1637
-
1657
+
1658
+ topology_analysis.update(
1659
+ {
1660
+ "vpcs": vpcs_data,
1661
+ "nat_gateways": nat_gateways_data,
1662
+ "vpc_endpoints": self._analyze_vpc_endpoints(endpoints_response["VpcEndpoints"]),
1663
+ "total_monthly_cost": total_monthly_cost,
1664
+ "total_annual_cost": total_monthly_cost * 12,
1665
+ "business_recommendations": business_recommendations,
1666
+ }
1667
+ )
1668
+
1638
1669
  # Display results with Rich formatting
1639
1670
  self._display_topology_analysis(topology_analysis)
1640
-
1671
+
1641
1672
  result.mark_completed(OperationStatus.SUCCESS)
1642
1673
  result.response_data = topology_analysis
1643
-
1674
+
1644
1675
  except Exception as e:
1645
1676
  error_msg = f"Network topology analysis failed: {e}"
1646
1677
  result.mark_completed(OperationStatus.FAILED, error_msg)
1647
1678
  self.rich_console.print_error(f"❌ {error_msg}")
1648
1679
  logger.error(error_msg)
1649
-
1680
+
1650
1681
  return [result]
1651
-
1682
+
1652
1683
  def _analyze_vpc_comprehensive(self, ec2_client, vpc: Dict[str, Any], context: OperationContext) -> Dict[str, Any]:
1653
1684
  """Comprehensive VPC analysis with cost implications"""
1654
1685
  tags = {tag["Key"]: tag["Value"] for tag in vpc.get("Tags", [])}
1655
-
1686
+
1656
1687
  vpc_analysis = {
1657
1688
  "vpc_id": vpc["VpcId"],
1658
1689
  "cidr_block": vpc["CidrBlock"],
@@ -1664,48 +1695,51 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1664
1695
  "security_groups": [],
1665
1696
  "route_tables": [],
1666
1697
  "estimated_monthly_cost": 0,
1667
- "optimization_opportunities": []
1698
+ "optimization_opportunities": [],
1668
1699
  }
1669
-
1700
+
1670
1701
  try:
1671
1702
  # Get subnets
1672
1703
  subnets_response = self.execute_aws_call(
1673
- ec2_client, "describe_subnets",
1674
- Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}]
1704
+ ec2_client, "describe_subnets", Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}]
1675
1705
  )
1676
-
1706
+
1677
1707
  for subnet in subnets_response["Subnets"]:
1678
1708
  subnet_tags = {tag["Key"]: tag["Value"] for tag in subnet.get("Tags", [])}
1679
- vpc_analysis["subnets"].append({
1680
- "subnet_id": subnet["SubnetId"],
1681
- "cidr_block": subnet["CidrBlock"],
1682
- "availability_zone": subnet["AvailabilityZone"],
1683
- "available_ip_address_count": subnet["AvailableIpAddressCount"],
1684
- "map_public_ip_on_launch": subnet.get("MapPublicIpOnLaunch", False),
1685
- "tags": subnet_tags,
1686
- "name": subnet_tags.get("Name", subnet["SubnetId"])
1687
- })
1688
-
1709
+ vpc_analysis["subnets"].append(
1710
+ {
1711
+ "subnet_id": subnet["SubnetId"],
1712
+ "cidr_block": subnet["CidrBlock"],
1713
+ "availability_zone": subnet["AvailabilityZone"],
1714
+ "available_ip_address_count": subnet["AvailableIpAddressCount"],
1715
+ "map_public_ip_on_launch": subnet.get("MapPublicIpOnLaunch", False),
1716
+ "tags": subnet_tags,
1717
+ "name": subnet_tags.get("Name", subnet["SubnetId"]),
1718
+ }
1719
+ )
1720
+
1689
1721
  # Basic cost estimation (placeholder for more sophisticated analysis)
1690
1722
  if len(vpc_analysis["subnets"]) > 10:
1691
- vpc_analysis["optimization_opportunities"].append({
1692
- "type": "subnet_consolidation",
1693
- "description": f"VPC has {len(vpc_analysis['subnets'])} subnets - consider consolidation",
1694
- "potential_savings": "Reduced management overhead"
1695
- })
1696
-
1723
+ vpc_analysis["optimization_opportunities"].append(
1724
+ {
1725
+ "type": "subnet_consolidation",
1726
+ "description": f"VPC has {len(vpc_analysis['subnets'])} subnets - consider consolidation",
1727
+ "potential_savings": "Reduced management overhead",
1728
+ }
1729
+ )
1730
+
1697
1731
  except Exception as e:
1698
1732
  logger.warning(f"Failed to get detailed VPC data for {vpc['VpcId']}: {e}")
1699
-
1733
+
1700
1734
  return vpc_analysis
1701
-
1735
+
1702
1736
  def _analyze_nat_gateway_costs(self, ec2_client, nat: Dict[str, Any], context: OperationContext) -> Dict[str, Any]:
1703
1737
  """Detailed NAT Gateway cost analysis"""
1704
1738
  tags = {tag["Key"]: tag["Value"] for tag in nat.get("Tags", [])}
1705
-
1739
+
1706
1740
  # Base monthly cost (24/7 * 30 days * $0.045/hour)
1707
1741
  base_monthly_cost = 24 * 30 * self.nat_gateway_hourly_cost
1708
-
1742
+
1709
1743
  nat_analysis = {
1710
1744
  "nat_gateway_id": nat["NatGatewayId"],
1711
1745
  "state": nat["State"],
@@ -1717,94 +1751,104 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1717
1751
  "base_monthly_cost": base_monthly_cost,
1718
1752
  "estimated_data_processing_cost": 0, # Would need CloudWatch metrics for accurate calculation
1719
1753
  "total_estimated_monthly_cost": base_monthly_cost,
1720
- "optimization_recommendation": "monitor_usage"
1754
+ "optimization_recommendation": "monitor_usage",
1721
1755
  }
1722
-
1756
+
1723
1757
  # Add optimization recommendations based on analysis
1724
1758
  if nat["State"] == "available":
1725
1759
  nat_analysis["optimization_recommendation"] = "monitor_usage"
1726
1760
  elif nat["State"] in ["pending", "failed"]:
1727
1761
  nat_analysis["optimization_recommendation"] = "investigate_health"
1728
-
1762
+
1729
1763
  return nat_analysis
1730
-
1764
+
1731
1765
  def _analyze_vpc_endpoints(self, endpoints: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
1732
1766
  """Analyze VPC endpoints with cost implications"""
1733
1767
  endpoints_analysis = []
1734
-
1768
+
1735
1769
  for endpoint in endpoints:
1736
1770
  tags = {tag["Key"]: tag["Value"] for tag in endpoint.get("Tags", [])}
1737
-
1771
+
1738
1772
  # Estimate monthly cost based on endpoint type
1739
1773
  if endpoint.get("VpcEndpointType") == "Interface":
1740
- estimated_monthly_cost = 24 * 30 * self.vpc_endpoint_hourly_cost # Interface endpoints have hourly charges
1774
+ estimated_monthly_cost = (
1775
+ 24 * 30 * self.vpc_endpoint_hourly_cost
1776
+ ) # Interface endpoints have hourly charges
1741
1777
  else:
1742
1778
  estimated_monthly_cost = 0 # Gateway endpoints are typically free
1743
-
1744
- endpoints_analysis.append({
1745
- "vpc_endpoint_id": endpoint["VpcEndpointId"],
1746
- "vpc_id": endpoint.get("VpcId"),
1747
- "service_name": endpoint.get("ServiceName"),
1748
- "endpoint_type": endpoint.get("VpcEndpointType"),
1749
- "state": endpoint.get("State"),
1750
- "tags": tags,
1751
- "name": tags.get("Name", endpoint["VpcEndpointId"]),
1752
- "estimated_monthly_cost": estimated_monthly_cost,
1753
- "route_table_ids": endpoint.get("RouteTableIds", []),
1754
- "subnet_ids": endpoint.get("SubnetIds", [])
1755
- })
1756
-
1779
+
1780
+ endpoints_analysis.append(
1781
+ {
1782
+ "vpc_endpoint_id": endpoint["VpcEndpointId"],
1783
+ "vpc_id": endpoint.get("VpcId"),
1784
+ "service_name": endpoint.get("ServiceName"),
1785
+ "endpoint_type": endpoint.get("VpcEndpointType"),
1786
+ "state": endpoint.get("State"),
1787
+ "tags": tags,
1788
+ "name": tags.get("Name", endpoint["VpcEndpointId"]),
1789
+ "estimated_monthly_cost": estimated_monthly_cost,
1790
+ "route_table_ids": endpoint.get("RouteTableIds", []),
1791
+ "subnet_ids": endpoint.get("SubnetIds", []),
1792
+ }
1793
+ )
1794
+
1757
1795
  return endpoints_analysis
1758
-
1759
- def _generate_business_recommendations(self, vpcs_data: List[Dict], nat_data: List[Dict], total_cost: float) -> List[BusinessRecommendation]:
1796
+
1797
+ def _generate_business_recommendations(
1798
+ self, vpcs_data: List[Dict], nat_data: List[Dict], total_cost: float
1799
+ ) -> List[BusinessRecommendation]:
1760
1800
  """Generate business-focused recommendations"""
1761
1801
  recommendations = []
1762
-
1802
+
1763
1803
  # NAT Gateway optimization recommendation
1764
1804
  active_nat_gateways = [nat for nat in nat_data if nat["state"] == "available"]
1765
1805
  if len(active_nat_gateways) > len(vpcs_data):
1766
1806
  # Dynamic NAT Gateway cost from AWS Pricing API - NO hardcoded values
1767
1807
  nat_gateway_monthly_cost = self._get_nat_gateway_monthly_cost()
1768
1808
  potential_savings = (len(active_nat_gateways) - len(vpcs_data)) * nat_gateway_monthly_cost
1769
-
1770
- recommendations.append(BusinessRecommendation(
1771
- title="NAT Gateway Consolidation Opportunity",
1772
- executive_summary=f"Multiple NAT Gateways detected ({len(active_nat_gateways)}) across {len(vpcs_data)} VPCs",
1773
- monthly_savings=potential_savings,
1774
- annual_impact=potential_savings * 12,
1775
- implementation_timeline="2-4 weeks",
1776
- business_priority=BusinessPriority.HIGH if potential_savings > 90 else BusinessPriority.MEDIUM,
1777
- risk_level=RiskLevel.LOW,
1778
- resource_requirements=["Network engineer", "1-2 hours/NAT Gateway"],
1779
- success_metrics=[f"Reduce monthly NAT Gateway costs by ${potential_savings:.2f}"],
1780
- approval_required=potential_savings > self.manager_config.approval_threshold,
1781
- quick_win=potential_savings > 50,
1782
- strategic_value="Cost optimization without service impact"
1783
- ))
1784
-
1785
- # VPC complexity recommendation
1809
+
1810
+ recommendations.append(
1811
+ BusinessRecommendation(
1812
+ title="NAT Gateway Consolidation Opportunity",
1813
+ executive_summary=f"Multiple NAT Gateways detected ({len(active_nat_gateways)}) across {len(vpcs_data)} VPCs",
1814
+ monthly_savings=potential_savings,
1815
+ annual_impact=potential_savings * 12,
1816
+ implementation_timeline="2-4 weeks",
1817
+ business_priority=BusinessPriority.HIGH if potential_savings > 90 else BusinessPriority.MEDIUM,
1818
+ risk_level=RiskLevel.LOW,
1819
+ resource_requirements=["Network engineer", "1-2 hours/NAT Gateway"],
1820
+ success_metrics=[f"Reduce monthly NAT Gateway costs by ${potential_savings:.2f}"],
1821
+ approval_required=potential_savings > self.manager_config.approval_threshold,
1822
+ quick_win=potential_savings > 50,
1823
+ strategic_value="Cost optimization without service impact",
1824
+ )
1825
+ )
1826
+
1827
+ # VPC complexity recommendation
1786
1828
  complex_vpcs = [vpc for vpc in vpcs_data if len(vpc.get("subnets", [])) > 15]
1787
1829
  if complex_vpcs:
1788
- recommendations.append(BusinessRecommendation(
1789
- title="VPC Architecture Simplification",
1790
- executive_summary=f"Detected {len(complex_vpcs)} VPCs with high subnet complexity",
1791
- monthly_savings=0, # Operational savings, not direct cost
1792
- annual_impact=0,
1793
- implementation_timeline="6-12 weeks",
1794
- business_priority=BusinessPriority.MEDIUM,
1795
- risk_level=RiskLevel.MEDIUM,
1796
- resource_requirements=["Cloud architect", "Network engineer", "4-8 hours/VPC"],
1797
- success_metrics=["Reduced operational complexity", "Improved maintainability"],
1798
- approval_required=True,
1799
- quick_win=False,
1800
- strategic_value="Improved operational efficiency and reduced management overhead"
1801
- ))
1802
-
1830
+ recommendations.append(
1831
+ BusinessRecommendation(
1832
+ title="VPC Architecture Simplification",
1833
+ executive_summary=f"Detected {len(complex_vpcs)} VPCs with high subnet complexity",
1834
+ monthly_savings=0, # Operational savings, not direct cost
1835
+ annual_impact=0,
1836
+ implementation_timeline="6-12 weeks",
1837
+ business_priority=BusinessPriority.MEDIUM,
1838
+ risk_level=RiskLevel.MEDIUM,
1839
+ resource_requirements=["Cloud architect", "Network engineer", "4-8 hours/VPC"],
1840
+ success_metrics=["Reduced operational complexity", "Improved maintainability"],
1841
+ approval_required=True,
1842
+ quick_win=False,
1843
+ strategic_value="Improved operational efficiency and reduced management overhead",
1844
+ )
1845
+ )
1846
+
1803
1847
  return recommendations
1804
-
1848
+
1805
1849
  def _display_topology_analysis(self, topology_analysis: Dict[str, Any]) -> None:
1806
1850
  """Display topology analysis with Rich formatting"""
1807
-
1851
+
1808
1852
  # Summary panel
1809
1853
  summary_text = (
1810
1854
  f"Total VPCs: {len(topology_analysis.get('vpcs', []))}\n"
@@ -1813,44 +1857,49 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1813
1857
  f"Estimated monthly cost: ${topology_analysis.get('total_monthly_cost', 0):.2f}\n"
1814
1858
  f"Estimated annual cost: ${topology_analysis.get('total_annual_cost', 0):.2f}"
1815
1859
  )
1816
-
1817
- self.rich_console.print_panel(
1818
- "Network Topology Summary",
1819
- summary_text,
1820
- title="🏗️ Infrastructure Overview"
1821
- )
1822
-
1860
+
1861
+ self.rich_console.print_panel("Network Topology Summary", summary_text, title="🏗️ Infrastructure Overview")
1862
+
1823
1863
  # Business recommendations table
1824
- recommendations = topology_analysis.get('business_recommendations', [])
1864
+ recommendations = topology_analysis.get("business_recommendations", [])
1825
1865
  if recommendations:
1826
1866
  rec_data = []
1827
1867
  for rec in recommendations:
1828
- rec_data.append([
1829
- rec.title,
1830
- rec.business_priority.value,
1831
- f"${rec.monthly_savings:.2f}",
1832
- f"${rec.annual_impact:.2f}",
1833
- rec.implementation_timeline,
1834
- "Yes" if rec.approval_required else "No"
1835
- ])
1836
-
1868
+ rec_data.append(
1869
+ [
1870
+ rec.title,
1871
+ rec.business_priority.value,
1872
+ f"${rec.monthly_savings:.2f}",
1873
+ f"${rec.annual_impact:.2f}",
1874
+ rec.implementation_timeline,
1875
+ "Yes" if rec.approval_required else "No",
1876
+ ]
1877
+ )
1878
+
1837
1879
  self.rich_console.print_table(
1838
1880
  rec_data,
1839
- headers=["Recommendation", "Priority", "Monthly Savings", "Annual Impact", "Timeline", "Approval Required"],
1840
- title="💡 Business Optimization Recommendations"
1881
+ headers=[
1882
+ "Recommendation",
1883
+ "Priority",
1884
+ "Monthly Savings",
1885
+ "Annual Impact",
1886
+ "Timeline",
1887
+ "Approval Required",
1888
+ ],
1889
+ title="💡 Business Optimization Recommendations",
1841
1890
  )
1842
-
1891
+
1843
1892
  def _generate_manager_report(self, context: OperationContext, **kwargs) -> List[OperationResult]:
1844
1893
  """Generate manager-friendly business report"""
1845
1894
  result = self.create_operation_result(context, "generate_manager_report", "vpc", "manager-report")
1846
-
1895
+
1847
1896
  try:
1848
1897
  self.rich_console.print_header("Manager Dashboard - VPC Cost Optimization", "latest version")
1849
-
1898
+
1850
1899
  # First run comprehensive analysis
1851
1900
  analysis_results = self._analyze_network_topology_comprehensive(context, **kwargs)
1852
1901
  analysis_data = analysis_results[0].response_data
1853
-
1902
+
1854
1903
  # Generate executive summary
1855
1904
  executive_report = {
1856
1905
  "report_generated": datetime.now().isoformat(),
@@ -1858,86 +1907,89 @@ class EnhancedVPCNetworkingManager(BaseOperation):
1858
1907
  "total_infrastructure_cost": analysis_data.get("total_monthly_cost", 0),
1859
1908
  "optimization_opportunities": len(analysis_data.get("business_recommendations", [])),
1860
1909
  "immediate_actions": [
1861
- rec for rec in analysis_data.get("business_recommendations", [])
1910
+ rec
1911
+ for rec in analysis_data.get("business_recommendations", [])
1862
1912
  if rec.quick_win and rec.business_priority in [BusinessPriority.HIGH, BusinessPriority.CRITICAL]
1863
1913
  ],
1864
1914
  "approval_required_items": [
1865
- rec for rec in analysis_data.get("business_recommendations", [])
1866
- if rec.approval_required
1867
- ]
1915
+ rec for rec in analysis_data.get("business_recommendations", []) if rec.approval_required
1916
+ ],
1868
1917
  },
1869
1918
  "detailed_analysis": analysis_data,
1870
- "next_steps": self._generate_next_steps(analysis_data.get("business_recommendations", []))
1919
+ "next_steps": self._generate_next_steps(analysis_data.get("business_recommendations", [])),
1871
1920
  }
1872
-
1921
+
1873
1922
  # Display executive summary
1874
1923
  self._display_executive_summary(executive_report["executive_summary"])
1875
-
1924
+
1876
1925
  result.mark_completed(OperationStatus.SUCCESS)
1877
1926
  result.response_data = executive_report
1878
-
1927
+
1879
1928
  except Exception as e:
1880
1929
  error_msg = f"Manager report generation failed: {e}"
1881
1930
  result.mark_completed(OperationStatus.FAILED, error_msg)
1882
1931
  self.rich_console.print_error(f"❌ {error_msg}")
1883
1932
  logger.error(error_msg)
1884
-
1933
+
1885
1934
  return [result]
1886
-
1935
+
1887
1936
  def _generate_next_steps(self, recommendations: List[BusinessRecommendation]) -> List[Dict[str, str]]:
1888
1937
  """Generate actionable next steps for managers"""
1889
1938
  next_steps = []
1890
-
1939
+
1891
1940
  high_priority = [rec for rec in recommendations if rec.business_priority == BusinessPriority.HIGH]
1892
1941
  quick_wins = [rec for rec in recommendations if rec.quick_win]
1893
-
1942
+
1894
1943
  if high_priority:
1895
- next_steps.append({
1896
- "action": "Review High Priority Items",
1897
- "description": f"Review {len(high_priority)} high-priority optimization opportunities",
1898
- "timeline": "This week"
1899
- })
1900
-
1944
+ next_steps.append(
1945
+ {
1946
+ "action": "Review High Priority Items",
1947
+ "description": f"Review {len(high_priority)} high-priority optimization opportunities",
1948
+ "timeline": "This week",
1949
+ }
1950
+ )
1951
+
1901
1952
  if quick_wins:
1902
- next_steps.append({
1903
- "action": "Implement Quick Wins",
1904
- "description": f"Execute {len(quick_wins)} quick-win optimizations",
1905
- "timeline": "Next 2 weeks"
1906
- })
1907
-
1908
- next_steps.append({
1909
- "action": "Schedule Technical Review",
1910
- "description": "Meet with technical team to discuss implementation details",
1911
- "timeline": "Within 2 weeks"
1912
- })
1913
-
1953
+ next_steps.append(
1954
+ {
1955
+ "action": "Implement Quick Wins",
1956
+ "description": f"Execute {len(quick_wins)} quick-win optimizations",
1957
+ "timeline": "Next 2 weeks",
1958
+ }
1959
+ )
1960
+
1961
+ next_steps.append(
1962
+ {
1963
+ "action": "Schedule Technical Review",
1964
+ "description": "Meet with technical team to discuss implementation details",
1965
+ "timeline": "Within 2 weeks",
1966
+ }
1967
+ )
1968
+
1914
1969
  return next_steps
1915
-
1970
+
1916
1971
  def _display_executive_summary(self, summary: Dict[str, Any]) -> None:
1917
1972
  """Display executive summary with business-friendly formatting"""
1918
-
1973
+
1919
1974
  summary_text = (
1920
1975
  f"Monthly Infrastructure Cost: ${summary.get('total_infrastructure_cost', 0):.2f}\n"
1921
1976
  f"Optimization Opportunities: {summary.get('optimization_opportunities', 0)}\n"
1922
1977
  f"Quick Win Actions: {len(summary.get('immediate_actions', []))}\n"
1923
1978
  f"Items Requiring Approval: {len(summary.get('approval_required_items', []))}"
1924
1979
  )
1925
-
1926
- self.rich_console.print_panel(
1927
- "Executive Dashboard",
1928
- summary_text,
1929
- title="📊 Business Overview"
1930
- )
1980
+
1981
+ self.rich_console.print_panel("Executive Dashboard", summary_text, title="📊 Business Overview")
1931
1982
 
1932
1983
 
1933
1984
  # =============================================================================
1934
1985
  # AWSO-5 VPC Cleanup Operations - Enterprise Security Framework
1935
1986
  # =============================================================================
1936
1987
 
1988
+
1937
1989
  @dataclass
1938
1990
  class VPCCleanupConfiguration:
1939
1991
  """AWSO-5 VPC cleanup configuration with enterprise safety controls."""
1940
-
1992
+
1941
1993
  vpc_id: str
1942
1994
  dry_run: bool = True
1943
1995
  force_cleanup: bool = False
@@ -1945,12 +1997,12 @@ class VPCCleanupConfiguration:
1945
1997
  evidence_collection: bool = True
1946
1998
  platform_lead_approval: bool = False
1947
1999
  skip_eni_gate: bool = False # DANGEROUS: Only for emergency scenarios
1948
-
2000
+
1949
2001
  def __post_init__(self):
1950
2002
  """Validation of cleanup configuration."""
1951
2003
  if self.force_cleanup and not self.approval_token:
1952
2004
  raise ValueError("Force cleanup requires approval token")
1953
-
2005
+
1954
2006
  if self.skip_eni_gate and not self.platform_lead_approval:
1955
2007
  raise ValueError("Skipping ENI gate requires Platform Lead approval")
1956
2008
 
@@ -1958,42 +2010,42 @@ class VPCCleanupConfiguration:
1958
2010
  @dataclass
1959
2011
  class VPCCleanupPlan:
1960
2012
  """AWSO-5 VPC cleanup execution plan with ordered steps."""
1961
-
2013
+
1962
2014
  vpc_id: str
1963
2015
  cleanup_steps: List[Dict[str, Any]]
1964
2016
  estimated_duration_minutes: int
1965
2017
  risk_level: str # LOW, MEDIUM, HIGH
1966
2018
  requires_approval: bool
1967
-
2019
+
1968
2020
  # Evidence collection
1969
2021
  pre_cleanup_evidence: Dict[str, Any]
1970
2022
  plan_hash: str
1971
2023
  plan_timestamp: str
1972
-
2024
+
1973
2025
  @property
1974
2026
  def total_steps(self) -> int:
1975
2027
  """Total number of cleanup steps."""
1976
2028
  return len(self.cleanup_steps)
1977
-
2029
+
1978
2030
  @property
1979
2031
  def blocking_steps(self) -> int:
1980
2032
  """Number of steps that could cause service disruption."""
1981
- return len([step for step in self.cleanup_steps if step.get('risk_level') == 'HIGH'])
2033
+ return len([step for step in self.cleanup_steps if step.get("risk_level") == "HIGH"])
1982
2034
 
1983
2035
 
1984
2036
  class AWSO5VPCCleanupOperation(BaseOperation):
1985
2037
  """
1986
2038
  AWSO-5 VPC Cleanup Operation - Enterprise Security Framework.
1987
-
2039
+
1988
2040
  Implements comprehensive VPC cleanup following the AWSO-5 12-step framework
1989
2041
  with enterprise safety controls, evidence collection, and approval workflows.
1990
-
2042
+
1991
2043
  **Strategic Alignment**:
1992
2044
  - Security posture enhancement through default VPC elimination
1993
- - Attack surface reduction via systematic dependency cleanup
2045
+ - Attack surface reduction via systematic dependency cleanup
1994
2046
  - CIS Benchmark compliance through infrastructure hygiene
1995
2047
  - Evidence-based validation with SHA256-verified audit trails
1996
-
2048
+
1997
2049
  **Safety Controls**:
1998
2050
  - ENI gate validation (prevents accidental workload disruption)
1999
2051
  - Dry-run first approach with detailed execution plans
@@ -2001,52 +2053,48 @@ class AWSO5VPCCleanupOperation(BaseOperation):
2001
2053
  - Comprehensive rollback procedures
2002
2054
  - Real-time monitoring and validation
2003
2055
  """
2004
-
2056
+
2005
2057
  def __init__(self, context: OperationContext):
2006
2058
  """Initialize AWSO-5 VPC cleanup operation."""
2007
2059
  super().__init__(context)
2008
2060
  self.operation_type = "AWSO5_VPC_CLEANUP"
2009
-
2061
+
2010
2062
  # Initialize dependency analyzer
2011
2063
  from runbooks.inventory.vpc_dependency_analyzer import VPCDependencyAnalyzer
2012
- self.dependency_analyzer = VPCDependencyAnalyzer(
2013
- session=self.context.session,
2014
- region=self.context.region
2015
- )
2016
-
2064
+
2065
+ self.dependency_analyzer = VPCDependencyAnalyzer(session=self.context.session, region=self.context.region)
2066
+
2017
2067
  # Cleanup tracking
2018
2068
  self.cleanup_evidence: Dict[str, Any] = {}
2019
2069
  self.cleanup_plan: Optional[VPCCleanupPlan] = None
2020
-
2070
+
2021
2071
  def execute_vpc_cleanup(
2022
- self,
2023
- config: VPCCleanupConfiguration,
2024
- evidence_bundle_path: Optional[str] = None
2072
+ self, config: VPCCleanupConfiguration, evidence_bundle_path: Optional[str] = None
2025
2073
  ) -> List[OperationResult]:
2026
2074
  """
2027
2075
  Execute AWSO-5 VPC cleanup with comprehensive safety validation.
2028
-
2076
+
2029
2077
  Args:
2030
2078
  config: VPC cleanup configuration
2031
2079
  evidence_bundle_path: Optional path to save evidence bundle
2032
-
2080
+
2033
2081
  Returns:
2034
2082
  Operation results with evidence and audit information
2035
2083
  """
2036
2084
  results = []
2037
2085
  operation_start = datetime.utcnow()
2038
-
2086
+
2039
2087
  try:
2040
2088
  # Phase 1: Pre-cleanup Analysis & Validation
2041
2089
  self.rich_console.print_header("AWSO-5 VPC Cleanup Operation", "1.0.0")
2042
2090
  self.rich_console.print_info(f"Target VPC: {config.vpc_id}")
2043
2091
  self.rich_console.print_info(f"Mode: {'DRY-RUN' if config.dry_run else 'EXECUTE'}")
2044
-
2092
+
2045
2093
  # Step 1: ENI Gate Validation (Critical Safety Check)
2046
2094
  if not config.skip_eni_gate:
2047
2095
  self.rich_console.print_info("Step 1: ENI Gate Validation (Critical Safety Check)")
2048
2096
  dependency_result = self.dependency_analyzer.analyze_vpc_dependencies(config.vpc_id)
2049
-
2097
+
2050
2098
  if dependency_result.eni_count > 0:
2051
2099
  error_msg = f"ENI Gate FAILED: {dependency_result.eni_count} active ENIs detected"
2052
2100
  self.rich_console.print_error(f"❌ {error_msg}")
@@ -2055,33 +2103,33 @@ class AWSO5VPCCleanupOperation(BaseOperation):
2055
2103
  self.rich_console.print_info("1. Investigate ENI owners and workload requirements")
2056
2104
  self.rich_console.print_info("2. Coordinate with application teams")
2057
2105
  self.rich_console.print_info("3. Consider migration vs cleanup options")
2058
-
2106
+
2059
2107
  result = OperationResult(
2060
2108
  operation_id=self.context.operation_id,
2061
2109
  operation_type="AWSO5_ENI_GATE_CHECK",
2062
2110
  status=OperationStatus.FAILED,
2063
2111
  message=error_msg,
2064
2112
  details={
2065
- 'eni_count': dependency_result.eni_count,
2066
- 'vpc_id': config.vpc_id,
2067
- 'recommendation': 'INVESTIGATE_REQUIRED',
2068
- 'dependencies': [dep.__dict__ for dep in dependency_result.dependencies]
2069
- }
2113
+ "eni_count": dependency_result.eni_count,
2114
+ "vpc_id": config.vpc_id,
2115
+ "recommendation": "INVESTIGATE_REQUIRED",
2116
+ "dependencies": [dep.__dict__ for dep in dependency_result.dependencies],
2117
+ },
2070
2118
  )
2071
2119
  return [result]
2072
-
2120
+
2073
2121
  self.rich_console.print_success("✅ ENI Gate PASSED - No active ENIs detected")
2074
2122
  else:
2075
2123
  self.rich_console.print_warning("⚠️ ENI Gate SKIPPED (Platform Lead Approval Required)")
2076
2124
  dependency_result = self.dependency_analyzer.analyze_vpc_dependencies(config.vpc_id)
2077
-
2125
+
2078
2126
  # Step 2: Generate Cleanup Plan
2079
2127
  self.rich_console.print_info("Step 2: Generating Comprehensive Cleanup Plan")
2080
2128
  self.cleanup_plan = self._generate_cleanup_plan(dependency_result, config)
2081
-
2129
+
2082
2130
  # Display cleanup plan
2083
2131
  self._display_cleanup_plan(self.cleanup_plan)
2084
-
2132
+
2085
2133
  # Step 3: Risk Assessment & Approval Gate
2086
2134
  if self.cleanup_plan.requires_approval and not config.approval_token:
2087
2135
  self.rich_console.print_warning("⚠️ This cleanup requires Platform Lead approval")
@@ -2090,21 +2138,21 @@ class AWSO5VPCCleanupOperation(BaseOperation):
2090
2138
  self.rich_console.print_info("• Platform Lead (Default VPC deletion)")
2091
2139
  if self.cleanup_plan.risk_level == "HIGH":
2092
2140
  self.rich_console.print_info("• Additional stakeholder review")
2093
-
2141
+
2094
2142
  result = OperationResult(
2095
2143
  operation_id=self.context.operation_id,
2096
2144
  operation_type="AWSO5_APPROVAL_REQUIRED",
2097
2145
  status=OperationStatus.PENDING_APPROVAL,
2098
2146
  message="Platform Lead approval required for VPC cleanup",
2099
2147
  details={
2100
- 'vpc_id': config.vpc_id,
2101
- 'risk_level': self.cleanup_plan.risk_level,
2102
- 'requires_approval_reason': 'Default VPC or High Risk Operation',
2103
- 'cleanup_plan': self.cleanup_plan.__dict__
2104
- }
2148
+ "vpc_id": config.vpc_id,
2149
+ "risk_level": self.cleanup_plan.risk_level,
2150
+ "requires_approval_reason": "Default VPC or High Risk Operation",
2151
+ "cleanup_plan": self.cleanup_plan.__dict__,
2152
+ },
2105
2153
  )
2106
2154
  return [result]
2107
-
2155
+
2108
2156
  # Step 4: Execute Cleanup (Dry-run or Actual)
2109
2157
  if config.dry_run:
2110
2158
  self.rich_console.print_info("Step 3: DRY-RUN Mode - No actual changes will be made")
@@ -2112,86 +2160,84 @@ class AWSO5VPCCleanupOperation(BaseOperation):
2112
2160
  else:
2113
2161
  self.rich_console.print_info("Step 3: EXECUTING VPC Cleanup")
2114
2162
  result = self._execute_actual_cleanup(self.cleanup_plan, config)
2115
-
2163
+
2116
2164
  # Step 5: Evidence Bundle Generation
2117
2165
  if config.evidence_collection:
2118
2166
  self.rich_console.print_info("Step 4: Generating Evidence Bundle")
2119
2167
  evidence_bundle = self._generate_evidence_bundle(
2120
- dependency_result,
2121
- self.cleanup_plan,
2122
- result,
2123
- evidence_bundle_path
2168
+ dependency_result, self.cleanup_plan, result, evidence_bundle_path
2124
2169
  )
2125
- result.details['evidence_bundle'] = evidence_bundle
2126
-
2170
+ result.details["evidence_bundle"] = evidence_bundle
2171
+
2127
2172
  results.append(result)
2128
-
2173
+
2129
2174
  except Exception as e:
2130
2175
  error_msg = f"AWSO-5 VPC cleanup failed: {str(e)}"
2131
2176
  logger.exception(error_msg)
2132
-
2177
+
2133
2178
  result = OperationResult(
2134
2179
  operation_id=self.context.operation_id,
2135
2180
  operation_type="AWSO5_VPC_CLEANUP_ERROR",
2136
2181
  status=OperationStatus.FAILED,
2137
2182
  message=error_msg,
2138
2183
  details={
2139
- 'vpc_id': config.vpc_id,
2140
- 'error_type': type(e).__name__,
2141
- 'operation_duration': (datetime.utcnow() - operation_start).total_seconds()
2142
- }
2184
+ "vpc_id": config.vpc_id,
2185
+ "error_type": type(e).__name__,
2186
+ "operation_duration": (datetime.utcnow() - operation_start).total_seconds(),
2187
+ },
2143
2188
  )
2144
2189
  results.append(result)
2145
-
2190
+
2146
2191
  return results
2147
-
2148
- def _generate_cleanup_plan(
2149
- self,
2150
- dependency_result,
2151
- config: VPCCleanupConfiguration
2152
- ) -> VPCCleanupPlan:
2192
+
2193
+ def _generate_cleanup_plan(self, dependency_result, config: VPCCleanupConfiguration) -> VPCCleanupPlan:
2153
2194
  """Generate comprehensive VPC cleanup plan based on dependency analysis."""
2154
-
2195
+
2155
2196
  cleanup_steps = []
2156
2197
  risk_level = "LOW"
2157
2198
  estimated_duration = 5 # Base time in minutes
2158
-
2199
+
2159
2200
  # Generate cleanup steps based on dependencies
2160
2201
  for dependency in dependency_result.dependencies:
2161
2202
  if dependency.is_blocking:
2162
2203
  step = {
2163
- 'step_type': 'DEPENDENCY_CLEANUP',
2164
- 'resource_type': dependency.resource_type,
2165
- 'resource_id': dependency.resource_id,
2166
- 'action': dependency.remediation_action,
2167
- 'risk_level': 'HIGH' if dependency.resource_type in ['LoadBalancer', 'TransitGatewayAttachment'] else 'MEDIUM',
2168
- 'estimated_minutes': self._estimate_cleanup_time(dependency.resource_type)
2204
+ "step_type": "DEPENDENCY_CLEANUP",
2205
+ "resource_type": dependency.resource_type,
2206
+ "resource_id": dependency.resource_id,
2207
+ "action": dependency.remediation_action,
2208
+ "risk_level": "HIGH"
2209
+ if dependency.resource_type in ["LoadBalancer", "TransitGatewayAttachment"]
2210
+ else "MEDIUM",
2211
+ "estimated_minutes": self._estimate_cleanup_time(dependency.resource_type),
2169
2212
  }
2170
2213
  cleanup_steps.append(step)
2171
- estimated_duration += step['estimated_minutes']
2172
-
2173
- if step['risk_level'] == 'HIGH':
2214
+ estimated_duration += step["estimated_minutes"]
2215
+
2216
+ if step["risk_level"] == "HIGH":
2174
2217
  risk_level = "HIGH"
2175
- elif step['risk_level'] == 'MEDIUM' and risk_level != "HIGH":
2218
+ elif step["risk_level"] == "MEDIUM" and risk_level != "HIGH":
2176
2219
  risk_level = "MEDIUM"
2177
-
2220
+
2178
2221
  # Final VPC deletion step
2179
- cleanup_steps.append({
2180
- 'step_type': 'VPC_DELETION',
2181
- 'resource_type': 'VPC',
2182
- 'resource_id': config.vpc_id,
2183
- 'action': 'Delete VPC (final step)',
2184
- 'risk_level': 'MEDIUM',
2185
- 'estimated_minutes': 2
2186
- })
2222
+ cleanup_steps.append(
2223
+ {
2224
+ "step_type": "VPC_DELETION",
2225
+ "resource_type": "VPC",
2226
+ "resource_id": config.vpc_id,
2227
+ "action": "Delete VPC (final step)",
2228
+ "risk_level": "MEDIUM",
2229
+ "estimated_minutes": 2,
2230
+ }
2231
+ )
2187
2232
  estimated_duration += 2
2188
-
2233
+
2189
2234
  # Calculate plan hash for integrity
2190
2235
  import hashlib
2191
2236
  import json
2237
+
2192
2238
  plan_content = json.dumps(cleanup_steps, sort_keys=True)
2193
2239
  plan_hash = hashlib.sha256(plan_content.encode()).hexdigest()
2194
-
2240
+
2195
2241
  return VPCCleanupPlan(
2196
2242
  vpc_id=config.vpc_id,
2197
2243
  cleanup_steps=cleanup_steps,
@@ -2200,72 +2246,68 @@ class AWSO5VPCCleanupOperation(BaseOperation):
2200
2246
  requires_approval=dependency_result.is_default or risk_level == "HIGH",
2201
2247
  pre_cleanup_evidence=dependency_result.__dict__,
2202
2248
  plan_hash=plan_hash[:16], # Short hash for display
2203
- plan_timestamp=datetime.utcnow().isoformat()
2249
+ plan_timestamp=datetime.utcnow().isoformat(),
2204
2250
  )
2205
-
2251
+
2206
2252
  def _estimate_cleanup_time(self, resource_type: str) -> int:
2207
2253
  """Estimate cleanup time in minutes for different resource types."""
2208
2254
  time_estimates = {
2209
- 'NetworkInterface': 3,
2210
- 'NatGateway': 5,
2211
- 'InternetGateway': 2,
2212
- 'RouteTable': 2,
2213
- 'VpcEndpoint': 3,
2214
- 'TransitGatewayAttachment': 10,
2215
- 'VpcPeeringConnection': 3,
2216
- 'ResolverEndpoint': 5,
2217
- 'LoadBalancer': 8,
2218
- 'SecurityGroup': 2,
2219
- 'NetworkAcl': 2,
2220
- 'FlowLog': 1
2255
+ "NetworkInterface": 3,
2256
+ "NatGateway": 5,
2257
+ "InternetGateway": 2,
2258
+ "RouteTable": 2,
2259
+ "VpcEndpoint": 3,
2260
+ "TransitGatewayAttachment": 10,
2261
+ "VpcPeeringConnection": 3,
2262
+ "ResolverEndpoint": 5,
2263
+ "LoadBalancer": 8,
2264
+ "SecurityGroup": 2,
2265
+ "NetworkAcl": 2,
2266
+ "FlowLog": 1,
2221
2267
  }
2222
2268
  return time_estimates.get(resource_type, 3)
2223
-
2269
+
2224
2270
  def _display_cleanup_plan(self, plan: VPCCleanupPlan) -> None:
2225
2271
  """Display comprehensive cleanup plan with Rich formatting."""
2226
-
2272
+
2227
2273
  # Plan Summary
2228
- summary_table = create_table(
2229
- title="AWSO-5 VPC Cleanup Plan Summary"
2230
- )
2274
+ summary_table = create_table(title="AWSO-5 VPC Cleanup Plan Summary")
2231
2275
  summary_table.add_column("Metric", style="cyan")
2232
2276
  summary_table.add_column("Value", style="green")
2233
-
2277
+
2234
2278
  summary_table.add_row("VPC ID", plan.vpc_id)
2235
2279
  summary_table.add_row("Total Steps", str(plan.total_steps))
2236
2280
  summary_table.add_row("Estimated Duration", f"{plan.estimated_duration_minutes} minutes")
2237
2281
  summary_table.add_row("Risk Level", plan.risk_level)
2238
2282
  summary_table.add_row("Requires Approval", "Yes" if plan.requires_approval else "No")
2239
2283
  summary_table.add_row("Plan Hash", plan.plan_hash)
2240
-
2284
+
2241
2285
  self.rich_console.print("\n")
2242
2286
  self.rich_console.print(summary_table)
2243
-
2287
+
2244
2288
  # Cleanup Steps Detail
2245
2289
  if plan.cleanup_steps:
2246
- steps_table = create_table(
2247
- title="Cleanup Execution Steps"
2248
- )
2290
+ steps_table = create_table(title="Cleanup Execution Steps")
2249
2291
  steps_table.add_column("Step", style="cyan")
2250
- steps_table.add_column("Resource Type", style="blue")
2292
+ steps_table.add_column("Resource Type", style="blue")
2251
2293
  steps_table.add_column("Resource ID", style="green")
2252
2294
  steps_table.add_column("Action", style="yellow")
2253
2295
  steps_table.add_column("Risk", style="red")
2254
2296
  steps_table.add_column("Est. Time", style="magenta")
2255
-
2297
+
2256
2298
  for i, step in enumerate(plan.cleanup_steps, 1):
2257
2299
  steps_table.add_row(
2258
2300
  str(i),
2259
- step['resource_type'],
2260
- step['resource_id'],
2261
- step['action'],
2262
- step['risk_level'],
2263
- f"{step['estimated_minutes']}min"
2301
+ step["resource_type"],
2302
+ step["resource_id"],
2303
+ step["action"],
2304
+ step["risk_level"],
2305
+ f"{step['estimated_minutes']}min",
2264
2306
  )
2265
-
2307
+
2266
2308
  self.rich_console.print("\n")
2267
2309
  self.rich_console.print(steps_table)
2268
-
2310
+
2269
2311
  # Risk Assessment
2270
2312
  if plan.risk_level == "HIGH":
2271
2313
  self.rich_console.print_warning("⚠️ HIGH RISK: This cleanup involves critical infrastructure components")
@@ -2273,309 +2315,300 @@ class AWSO5VPCCleanupOperation(BaseOperation):
2273
2315
  self.rich_console.print_info("ℹ️ MEDIUM RISK: Standard cleanup with network dependencies")
2274
2316
  else:
2275
2317
  self.rich_console.print_success("✅ LOW RISK: Straightforward cleanup with minimal dependencies")
2276
-
2277
- def _execute_dry_run_cleanup(
2278
- self,
2279
- plan: VPCCleanupPlan,
2280
- config: VPCCleanupConfiguration
2281
- ) -> OperationResult:
2318
+
2319
+ def _execute_dry_run_cleanup(self, plan: VPCCleanupPlan, config: VPCCleanupConfiguration) -> OperationResult:
2282
2320
  """Execute dry-run cleanup showing what would be done."""
2283
-
2321
+
2284
2322
  self.rich_console.print_success("🔍 DRY-RUN: Simulating cleanup execution")
2285
-
2323
+
2286
2324
  simulated_results = []
2287
2325
  for i, step in enumerate(plan.cleanup_steps, 1):
2288
2326
  self.rich_console.print_info(f"Step {i}/{plan.total_steps}: Would {step['action']}")
2289
- simulated_results.append({
2290
- 'step': i,
2291
- 'resource_type': step['resource_type'],
2292
- 'resource_id': step['resource_id'],
2293
- 'action': step['action'],
2294
- 'simulated_result': 'SUCCESS',
2295
- 'notes': 'Dry-run simulation - no actual changes made'
2296
- })
2327
+ simulated_results.append(
2328
+ {
2329
+ "step": i,
2330
+ "resource_type": step["resource_type"],
2331
+ "resource_id": step["resource_id"],
2332
+ "action": step["action"],
2333
+ "simulated_result": "SUCCESS",
2334
+ "notes": "Dry-run simulation - no actual changes made",
2335
+ }
2336
+ )
2297
2337
  time.sleep(0.5) # Simulate processing time
2298
-
2338
+
2299
2339
  self.rich_console.print_success("✅ DRY-RUN completed successfully")
2300
2340
  self.rich_console.print_info("Next Steps:")
2301
2341
  self.rich_console.print_info("1. Review cleanup plan and risk assessment")
2302
2342
  self.rich_console.print_info("2. Obtain required approvals if needed")
2303
2343
  self.rich_console.print_info("3. Execute with --dry-run=false when ready")
2304
-
2344
+
2305
2345
  return OperationResult(
2306
2346
  operation_id=self.context.operation_id,
2307
2347
  operation_type="AWSO5_DRY_RUN_CLEANUP",
2308
2348
  status=OperationStatus.COMPLETED,
2309
2349
  message="Dry-run cleanup completed successfully",
2310
2350
  details={
2311
- 'vpc_id': config.vpc_id,
2312
- 'cleanup_plan': plan.__dict__,
2313
- 'simulated_results': simulated_results,
2314
- 'execution_mode': 'DRY_RUN'
2315
- }
2351
+ "vpc_id": config.vpc_id,
2352
+ "cleanup_plan": plan.__dict__,
2353
+ "simulated_results": simulated_results,
2354
+ "execution_mode": "DRY_RUN",
2355
+ },
2316
2356
  )
2317
-
2318
- def _execute_actual_cleanup(
2319
- self,
2320
- plan: VPCCleanupPlan,
2321
- config: VPCCleanupConfiguration
2322
- ) -> OperationResult:
2357
+
2358
+ def _execute_actual_cleanup(self, plan: VPCCleanupPlan, config: VPCCleanupConfiguration) -> OperationResult:
2323
2359
  """Execute actual VPC cleanup with comprehensive error handling."""
2324
-
2360
+
2325
2361
  self.rich_console.print_warning("⚠️ EXECUTING ACTUAL CLEANUP - This will make real changes!")
2326
-
2362
+
2327
2363
  execution_results = []
2328
2364
  failed_steps = []
2329
-
2365
+
2330
2366
  try:
2331
2367
  for i, step in enumerate(plan.cleanup_steps, 1):
2332
2368
  self.rich_console.print_info(f"Step {i}/{plan.total_steps}: {step['action']}")
2333
-
2369
+
2334
2370
  try:
2335
2371
  # Execute cleanup step
2336
2372
  step_result = self._execute_cleanup_step(step, config)
2337
2373
  execution_results.append(step_result)
2338
-
2339
- if step_result['success']:
2374
+
2375
+ if step_result["success"]:
2340
2376
  self.rich_console.print_success(f"✅ Step {i} completed: {step['resource_id']}")
2341
2377
  else:
2342
2378
  self.rich_console.print_error(f"❌ Step {i} failed: {step_result['error']}")
2343
- failed_steps.append((i, step, step_result['error']))
2344
-
2379
+ failed_steps.append((i, step, step_result["error"]))
2380
+
2345
2381
  # Decide whether to continue or abort
2346
- if step['risk_level'] == 'HIGH':
2382
+ if step["risk_level"] == "HIGH":
2347
2383
  self.rich_console.print_error("🛑 ABORTING: High-risk step failed")
2348
2384
  break
2349
-
2385
+
2350
2386
  except Exception as e:
2351
2387
  error_msg = f"Step {i} execution error: {str(e)}"
2352
2388
  self.rich_console.print_error(f"❌ {error_msg}")
2353
2389
  failed_steps.append((i, step, error_msg))
2354
-
2390
+
2355
2391
  # Critical failure handling
2356
- if step['risk_level'] == 'HIGH':
2392
+ if step["risk_level"] == "HIGH":
2357
2393
  self.rich_console.print_error("🛑 CRITICAL FAILURE: Aborting cleanup")
2358
2394
  break
2359
-
2395
+
2360
2396
  # Final status assessment
2361
2397
  if not failed_steps:
2362
2398
  status = OperationStatus.COMPLETED
2363
2399
  message = "AWSO-5 VPC cleanup completed successfully"
2364
2400
  self.rich_console.print_success("✅ All cleanup steps completed successfully")
2365
2401
  elif len(failed_steps) < len(plan.cleanup_steps) // 2:
2366
- status = OperationStatus.PARTIALLY_COMPLETED
2402
+ status = OperationStatus.PARTIALLY_COMPLETED
2367
2403
  message = f"VPC cleanup partially completed ({len(failed_steps)} steps failed)"
2368
2404
  self.rich_console.print_warning(f"⚠️ Partial completion: {len(failed_steps)} steps failed")
2369
2405
  else:
2370
2406
  status = OperationStatus.FAILED
2371
2407
  message = f"VPC cleanup failed ({len(failed_steps)} steps failed)"
2372
2408
  self.rich_console.print_error(f"❌ Cleanup failed: {len(failed_steps)} steps failed")
2373
-
2409
+
2374
2410
  # Post-cleanup validation
2375
2411
  self.rich_console.print_info("Performing post-cleanup validation...")
2376
2412
  post_validation = self._perform_post_cleanup_validation(config.vpc_id)
2377
-
2413
+
2378
2414
  return OperationResult(
2379
2415
  operation_id=self.context.operation_id,
2380
2416
  operation_type="AWSO5_ACTUAL_CLEANUP",
2381
2417
  status=status,
2382
2418
  message=message,
2383
2419
  details={
2384
- 'vpc_id': config.vpc_id,
2385
- 'cleanup_plan': plan.__dict__,
2386
- 'execution_results': execution_results,
2387
- 'failed_steps': failed_steps,
2388
- 'post_validation': post_validation,
2389
- 'execution_mode': 'ACTUAL'
2390
- }
2420
+ "vpc_id": config.vpc_id,
2421
+ "cleanup_plan": plan.__dict__,
2422
+ "execution_results": execution_results,
2423
+ "failed_steps": failed_steps,
2424
+ "post_validation": post_validation,
2425
+ "execution_mode": "ACTUAL",
2426
+ },
2391
2427
  )
2392
-
2428
+
2393
2429
  except Exception as e:
2394
2430
  error_msg = f"Critical cleanup error: {str(e)}"
2395
2431
  logger.exception(error_msg)
2396
-
2432
+
2397
2433
  return OperationResult(
2398
2434
  operation_id=self.context.operation_id,
2399
2435
  operation_type="AWSO5_CLEANUP_ERROR",
2400
2436
  status=OperationStatus.FAILED,
2401
2437
  message=error_msg,
2402
- details={
2403
- 'vpc_id': config.vpc_id,
2404
- 'error_type': type(e).__name__,
2405
- 'partial_results': execution_results
2406
- }
2438
+ details={"vpc_id": config.vpc_id, "error_type": type(e).__name__, "partial_results": execution_results},
2407
2439
  )
2408
-
2440
+
2409
2441
  def _execute_cleanup_step(self, step: Dict[str, Any], config: VPCCleanupConfiguration) -> Dict[str, Any]:
2410
2442
  """Execute a single cleanup step with AWS API calls."""
2411
-
2412
- resource_type = step['resource_type']
2413
- resource_id = step['resource_id']
2414
-
2443
+
2444
+ resource_type = step["resource_type"]
2445
+ resource_id = step["resource_id"]
2446
+
2415
2447
  try:
2416
- if resource_type == 'NatGateway':
2417
- self.context.session.client('ec2').delete_nat_gateway(NatGatewayId=resource_id)
2418
-
2419
- elif resource_type == 'InternetGateway':
2420
- ec2 = self.context.session.client('ec2')
2448
+ if resource_type == "NatGateway":
2449
+ self.context.session.client("ec2").delete_nat_gateway(NatGatewayId=resource_id)
2450
+
2451
+ elif resource_type == "InternetGateway":
2452
+ ec2 = self.context.session.client("ec2")
2421
2453
  # First detach, then delete
2422
2454
  ec2.detach_internet_gateway(InternetGatewayId=resource_id, VpcId=config.vpc_id)
2423
2455
  ec2.delete_internet_gateway(InternetGatewayId=resource_id)
2424
-
2425
- elif resource_type == 'RouteTable':
2426
- ec2 = self.context.session.client('ec2')
2456
+
2457
+ elif resource_type == "RouteTable":
2458
+ ec2 = self.context.session.client("ec2")
2427
2459
  # Disassociate first, then delete
2428
- route_tables = ec2.describe_route_tables(RouteTableIds=[resource_id])['RouteTables']
2460
+ route_tables = ec2.describe_route_tables(RouteTableIds=[resource_id])["RouteTables"]
2429
2461
  for rt in route_tables:
2430
- for assoc in rt.get('Associations', []):
2431
- if not assoc.get('Main'):
2432
- ec2.disassociate_route_table(AssociationId=assoc['RouteTableAssociationId'])
2462
+ for assoc in rt.get("Associations", []):
2463
+ if not assoc.get("Main"):
2464
+ ec2.disassociate_route_table(AssociationId=assoc["RouteTableAssociationId"])
2433
2465
  ec2.delete_route_table(RouteTableId=resource_id)
2434
-
2435
- elif resource_type == 'VpcEndpoint':
2436
- self.context.session.client('ec2').delete_vpc_endpoints(VpcEndpointIds=[resource_id])
2437
-
2438
- elif resource_type == 'TransitGatewayAttachment':
2439
- self.context.session.client('ec2').delete_transit_gateway_vpc_attachment(
2466
+
2467
+ elif resource_type == "VpcEndpoint":
2468
+ self.context.session.client("ec2").delete_vpc_endpoints(VpcEndpointIds=[resource_id])
2469
+
2470
+ elif resource_type == "TransitGatewayAttachment":
2471
+ self.context.session.client("ec2").delete_transit_gateway_vpc_attachment(
2440
2472
  TransitGatewayAttachmentId=resource_id
2441
2473
  )
2442
-
2443
- elif resource_type == 'VpcPeeringConnection':
2444
- self.context.session.client('ec2').delete_vpc_peering_connection(
2445
- VpcPeeringConnectionId=resource_id
2446
- )
2447
-
2448
- elif resource_type == 'LoadBalancer':
2449
- self.context.session.client('elbv2').delete_load_balancer(LoadBalancerArn=resource_id)
2450
-
2451
- elif resource_type == 'SecurityGroup':
2452
- self.context.session.client('ec2').delete_security_group(GroupId=resource_id)
2453
-
2454
- elif resource_type == 'NetworkAcl':
2455
- self.context.session.client('ec2').delete_network_acl(NetworkAclId=resource_id)
2456
-
2457
- elif resource_type == 'FlowLog':
2458
- self.context.session.client('ec2').delete_flow_logs(FlowLogIds=[resource_id])
2459
-
2460
- elif resource_type == 'VPC':
2474
+
2475
+ elif resource_type == "VpcPeeringConnection":
2476
+ self.context.session.client("ec2").delete_vpc_peering_connection(VpcPeeringConnectionId=resource_id)
2477
+
2478
+ elif resource_type == "LoadBalancer":
2479
+ self.context.session.client("elbv2").delete_load_balancer(LoadBalancerArn=resource_id)
2480
+
2481
+ elif resource_type == "SecurityGroup":
2482
+ self.context.session.client("ec2").delete_security_group(GroupId=resource_id)
2483
+
2484
+ elif resource_type == "NetworkAcl":
2485
+ self.context.session.client("ec2").delete_network_acl(NetworkAclId=resource_id)
2486
+
2487
+ elif resource_type == "FlowLog":
2488
+ self.context.session.client("ec2").delete_flow_logs(FlowLogIds=[resource_id])
2489
+
2490
+ elif resource_type == "VPC":
2461
2491
  # Final VPC deletion
2462
- self.context.session.client('ec2').delete_vpc(VpcId=resource_id)
2463
-
2492
+ self.context.session.client("ec2").delete_vpc(VpcId=resource_id)
2493
+
2464
2494
  else:
2465
2495
  return {
2466
- 'success': False,
2467
- 'resource_type': resource_type,
2468
- 'resource_id': resource_id,
2469
- 'error': f'Unknown resource type: {resource_type}'
2496
+ "success": False,
2497
+ "resource_type": resource_type,
2498
+ "resource_id": resource_id,
2499
+ "error": f"Unknown resource type: {resource_type}",
2470
2500
  }
2471
-
2501
+
2472
2502
  return {
2473
- 'success': True,
2474
- 'resource_type': resource_type,
2475
- 'resource_id': resource_id,
2476
- 'timestamp': datetime.utcnow().isoformat()
2503
+ "success": True,
2504
+ "resource_type": resource_type,
2505
+ "resource_id": resource_id,
2506
+ "timestamp": datetime.utcnow().isoformat(),
2477
2507
  }
2478
-
2508
+
2479
2509
  except ClientError as e:
2480
2510
  return {
2481
- 'success': False,
2482
- 'resource_type': resource_type,
2483
- 'resource_id': resource_id,
2484
- 'error': f'AWS API Error: {e.response["Error"]["Code"]} - {e.response["Error"]["Message"]}'
2511
+ "success": False,
2512
+ "resource_type": resource_type,
2513
+ "resource_id": resource_id,
2514
+ "error": f"AWS API Error: {e.response['Error']['Code']} - {e.response['Error']['Message']}",
2485
2515
  }
2486
-
2516
+
2487
2517
  def _perform_post_cleanup_validation(self, vpc_id: str) -> Dict[str, Any]:
2488
2518
  """Perform post-cleanup validation to ensure VPC and dependencies are removed."""
2489
-
2519
+
2490
2520
  validation_results = {
2491
- 'vpc_exists': False,
2492
- 'dependencies_remaining': [],
2493
- 'validation_timestamp': datetime.utcnow().isoformat(),
2494
- 'validation_passed': False
2521
+ "vpc_exists": False,
2522
+ "dependencies_remaining": [],
2523
+ "validation_timestamp": datetime.utcnow().isoformat(),
2524
+ "validation_passed": False,
2495
2525
  }
2496
-
2526
+
2497
2527
  try:
2498
2528
  # Check if VPC still exists
2499
- ec2 = self.context.session.client('ec2')
2529
+ ec2 = self.context.session.client("ec2")
2500
2530
  try:
2501
2531
  response = ec2.describe_vpcs(VpcIds=[vpc_id])
2502
- if response['Vpcs']:
2503
- validation_results['vpc_exists'] = True
2532
+ if response["Vpcs"]:
2533
+ validation_results["vpc_exists"] = True
2504
2534
  self.rich_console.print_warning(f"⚠️ VPC {vpc_id} still exists")
2505
2535
  except ClientError as e:
2506
- if 'InvalidVpcID.NotFound' in str(e):
2507
- validation_results['vpc_exists'] = False
2536
+ if "InvalidVpcID.NotFound" in str(e):
2537
+ validation_results["vpc_exists"] = False
2508
2538
  self.rich_console.print_success(f"✅ VPC {vpc_id} successfully deleted")
2509
2539
  else:
2510
2540
  raise
2511
-
2541
+
2512
2542
  # Check for remaining dependencies
2513
- if not validation_results['vpc_exists']:
2514
- validation_results['validation_passed'] = True
2543
+ if not validation_results["vpc_exists"]:
2544
+ validation_results["validation_passed"] = True
2515
2545
  self.rich_console.print_success("✅ Post-cleanup validation PASSED")
2516
2546
  else:
2517
2547
  # Re-run dependency analysis
2518
2548
  dependency_result = self.dependency_analyzer.analyze_vpc_dependencies(vpc_id)
2519
- validation_results['dependencies_remaining'] = [
2520
- dep.__dict__ for dep in dependency_result.dependencies
2521
- ]
2522
- validation_results['validation_passed'] = len(dependency_result.dependencies) == 0
2523
-
2524
- if validation_results['validation_passed']:
2549
+ validation_results["dependencies_remaining"] = [dep.__dict__ for dep in dependency_result.dependencies]
2550
+ validation_results["validation_passed"] = len(dependency_result.dependencies) == 0
2551
+
2552
+ if validation_results["validation_passed"]:
2525
2553
  self.rich_console.print_success("✅ Post-cleanup validation PASSED - No dependencies remain")
2526
2554
  else:
2527
- self.rich_console.print_warning(f"⚠️ {len(dependency_result.dependencies)} dependencies still exist")
2528
-
2555
+ self.rich_console.print_warning(
2556
+ f"⚠️ {len(dependency_result.dependencies)} dependencies still exist"
2557
+ )
2558
+
2529
2559
  except Exception as e:
2530
- validation_results['error'] = str(e)
2531
- validation_results['validation_passed'] = False
2560
+ validation_results["error"] = str(e)
2561
+ validation_results["validation_passed"] = False
2532
2562
  self.rich_console.print_error(f"❌ Post-cleanup validation failed: {str(e)}")
2533
-
2563
+
2534
2564
  return validation_results
2535
-
2565
+
2536
2566
  def _generate_evidence_bundle(
2537
2567
  self,
2538
2568
  dependency_result,
2539
2569
  cleanup_plan: VPCCleanupPlan,
2540
2570
  execution_result: OperationResult,
2541
- evidence_bundle_path: Optional[str] = None
2571
+ evidence_bundle_path: Optional[str] = None,
2542
2572
  ) -> Dict[str, Any]:
2543
2573
  """Generate comprehensive evidence bundle for AWSO-5 compliance."""
2544
-
2574
+
2545
2575
  evidence_bundle = {
2546
- 'metadata': {
2547
- 'framework': 'AWSO-5',
2548
- 'version': '1.0.0',
2549
- 'vpc_id': cleanup_plan.vpc_id,
2550
- 'timestamp': datetime.utcnow().isoformat(),
2551
- 'analyst': 'AWSO5VPCCleanupOperation',
2552
- 'operation_id': self.context.operation_id
2576
+ "metadata": {
2577
+ "framework": "AWSO-5",
2578
+ "version": "1.0.0",
2579
+ "vpc_id": cleanup_plan.vpc_id,
2580
+ "timestamp": datetime.utcnow().isoformat(),
2581
+ "analyst": "AWSO5VPCCleanupOperation",
2582
+ "operation_id": self.context.operation_id,
2583
+ },
2584
+ "pre_cleanup_analysis": dependency_result.__dict__,
2585
+ "cleanup_plan": cleanup_plan.__dict__,
2586
+ "execution_results": execution_result.details,
2587
+ "evidence_artifacts": [],
2588
+ "compliance_validation": {
2589
+ "cis_benchmark_improvement": dependency_result.is_default,
2590
+ "attack_surface_reduction": True if execution_result.status == OperationStatus.COMPLETED else False,
2591
+ "security_posture_enhancement": len(dependency_result.dependencies) == 0,
2553
2592
  },
2554
- 'pre_cleanup_analysis': dependency_result.__dict__,
2555
- 'cleanup_plan': cleanup_plan.__dict__,
2556
- 'execution_results': execution_result.details,
2557
- 'evidence_artifacts': [],
2558
- 'compliance_validation': {
2559
- 'cis_benchmark_improvement': dependency_result.is_default,
2560
- 'attack_surface_reduction': True if execution_result.status == OperationStatus.COMPLETED else False,
2561
- 'security_posture_enhancement': len(dependency_result.dependencies) == 0
2562
- }
2563
2593
  }
2564
-
2594
+
2565
2595
  # Calculate evidence bundle hash
2566
2596
  import hashlib
2567
2597
  import json
2598
+
2568
2599
  bundle_content = json.dumps(evidence_bundle, sort_keys=True, default=str)
2569
- evidence_bundle['bundle_hash'] = hashlib.sha256(bundle_content.encode()).hexdigest()
2570
-
2600
+ evidence_bundle["bundle_hash"] = hashlib.sha256(bundle_content.encode()).hexdigest()
2601
+
2571
2602
  # Save evidence bundle if path provided
2572
2603
  if evidence_bundle_path:
2573
- with open(evidence_bundle_path, 'w') as f:
2604
+ with open(evidence_bundle_path, "w") as f:
2574
2605
  json.dump(evidence_bundle, f, indent=2, default=str)
2575
2606
  self.rich_console.print_success(f"Evidence bundle saved: {evidence_bundle_path}")
2576
-
2577
- self.rich_console.print_success(f"Evidence bundle generated with hash: {evidence_bundle['bundle_hash'][:16]}...")
2578
-
2607
+
2608
+ self.rich_console.print_success(
2609
+ f"Evidence bundle generated with hash: {evidence_bundle['bundle_hash'][:16]}..."
2610
+ )
2611
+
2579
2612
  return evidence_bundle
2580
2613
 
2581
2614
 
@@ -2585,11 +2618,11 @@ def execute_vpc_cleanup(
2585
2618
  region: str = "us-east-1",
2586
2619
  dry_run: bool = True,
2587
2620
  approval_token: Optional[str] = None,
2588
- evidence_bundle_path: Optional[str] = None
2621
+ evidence_bundle_path: Optional[str] = None,
2589
2622
  ) -> List[OperationResult]:
2590
2623
  """
2591
2624
  CLI wrapper for AWSO-5 VPC cleanup operation.
2592
-
2625
+
2593
2626
  Args:
2594
2627
  vpc_id: AWS VPC identifier to cleanup
2595
2628
  profile: AWS profile name
@@ -2597,28 +2630,25 @@ def execute_vpc_cleanup(
2597
2630
  dry_run: Execute in dry-run mode (default: True)
2598
2631
  approval_token: Platform lead approval token
2599
2632
  evidence_bundle_path: Path to save evidence bundle
2600
-
2633
+
2601
2634
  Returns:
2602
2635
  Operation results with comprehensive cleanup information
2603
2636
  """
2604
2637
  session = boto3.Session(profile_name=profile) if profile else boto3.Session()
2605
-
2638
+
2606
2639
  # Create operation context
2607
2640
  context = OperationContext(
2608
2641
  operation_id=f"awso5-cleanup-{vpc_id}-{int(datetime.utcnow().timestamp())}",
2609
2642
  region=region,
2610
2643
  session=session,
2611
- dry_run=dry_run
2644
+ dry_run=dry_run,
2612
2645
  )
2613
-
2646
+
2614
2647
  # Create cleanup configuration
2615
2648
  config = VPCCleanupConfiguration(
2616
- vpc_id=vpc_id,
2617
- dry_run=dry_run,
2618
- approval_token=approval_token,
2619
- evidence_collection=True
2649
+ vpc_id=vpc_id, dry_run=dry_run, approval_token=approval_token, evidence_collection=True
2620
2650
  )
2621
-
2651
+
2622
2652
  # Execute cleanup operation
2623
2653
  cleanup_operation = AWSO5VPCCleanupOperation(context)
2624
2654
  return cleanup_operation.execute_vpc_cleanup(config, evidence_bundle_path)