runbooks 0.9.5__py3-none-any.whl → 0.9.7__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.
- runbooks/__init__.py +1 -1
- runbooks/_platform/__init__.py +19 -0
- runbooks/_platform/core/runbooks_wrapper.py +478 -0
- runbooks/cloudops/cost_optimizer.py +330 -0
- runbooks/cloudops/interfaces.py +3 -3
- runbooks/finops/README.md +1 -1
- runbooks/finops/automation_core.py +643 -0
- runbooks/finops/business_cases.py +414 -16
- runbooks/finops/cli.py +23 -0
- runbooks/finops/compute_cost_optimizer.py +865 -0
- runbooks/finops/ebs_cost_optimizer.py +718 -0
- runbooks/finops/ebs_optimizer.py +909 -0
- runbooks/finops/elastic_ip_optimizer.py +675 -0
- runbooks/finops/embedded_mcp_validator.py +330 -14
- runbooks/finops/enterprise_wrappers.py +827 -0
- runbooks/finops/legacy_migration.py +730 -0
- runbooks/finops/nat_gateway_optimizer.py +1160 -0
- runbooks/finops/network_cost_optimizer.py +1387 -0
- runbooks/finops/notebook_utils.py +596 -0
- runbooks/finops/reservation_optimizer.py +956 -0
- runbooks/finops/validation_framework.py +753 -0
- runbooks/finops/workspaces_analyzer.py +593 -0
- runbooks/inventory/__init__.py +7 -0
- runbooks/inventory/collectors/aws_networking.py +357 -6
- runbooks/inventory/mcp_vpc_validator.py +1091 -0
- runbooks/inventory/vpc_analyzer.py +1107 -0
- runbooks/inventory/vpc_architecture_validator.py +939 -0
- runbooks/inventory/vpc_dependency_analyzer.py +845 -0
- runbooks/main.py +425 -39
- runbooks/operate/vpc_operations.py +1479 -16
- runbooks/remediation/commvault_ec2_analysis.py +5 -4
- runbooks/remediation/dynamodb_optimize.py +2 -2
- runbooks/remediation/rds_instance_list.py +1 -1
- runbooks/remediation/rds_snapshot_list.py +5 -4
- runbooks/remediation/workspaces_list.py +2 -2
- runbooks/security/compliance_automation.py +2 -2
- runbooks/vpc/tests/test_config.py +2 -2
- {runbooks-0.9.5.dist-info → runbooks-0.9.7.dist-info}/METADATA +1 -1
- {runbooks-0.9.5.dist-info → runbooks-0.9.7.dist-info}/RECORD +43 -24
- {runbooks-0.9.5.dist-info → runbooks-0.9.7.dist-info}/WHEEL +0 -0
- {runbooks-0.9.5.dist-info → runbooks-0.9.7.dist-info}/entry_points.txt +0 -0
- {runbooks-0.9.5.dist-info → runbooks-0.9.7.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.9.5.dist-info → runbooks-0.9.7.dist-info}/top_level.txt +0 -0
@@ -1,22 +1,66 @@
|
|
1
1
|
"""
|
2
|
-
VPC Operations Module -
|
2
|
+
AWSO-5 Enhanced VPC Operations Module - Enterprise VPC Cleanup Framework
|
3
|
+
|
4
|
+
Strategic Migration: VPC operations enhanced with discovery capabilities from migrated vpc module
|
5
|
+
following FAANG SDLC "Do one thing and do it well" principle with enterprise safety validation.
|
6
|
+
|
7
|
+
**VPC Module Migration Integration**:
|
8
|
+
- Discovery logic migrated to inventory/vpc_analyzer.py for "Do one thing and do it well"
|
9
|
+
- Operations logic enhanced with vpc module capabilities (networking_wrapper.py patterns)
|
10
|
+
- Manager interface integration (manager_interface.py business logic)
|
11
|
+
- Network cost engine integration (cost_engine.py patterns)
|
12
|
+
- Rich formatting integration (rich_formatters.py standards)
|
13
|
+
|
14
|
+
**AWSO-5 Integration**: Complete 12-step VPC cleanup framework with:
|
15
|
+
- ENI gate validation (critical blocking check)
|
16
|
+
- Comprehensive dependency cleanup (NAT, IGW, RT, Endpoints, TGW, Peering)
|
17
|
+
- Evidence bundle generation with SHA256 verification
|
18
|
+
- Default VPC elimination for CIS Benchmark compliance
|
19
|
+
- Security posture enhancement across 60+1 AWS Landing Zone
|
3
20
|
|
4
21
|
Enterprise-grade VPC and NAT Gateway operations for multi-account AWS environments.
|
5
22
|
Addresses manager-raised VPC infrastructure automation requirements with cost optimization focus.
|
6
23
|
|
7
|
-
|
8
|
-
-
|
9
|
-
- VPC
|
24
|
+
**AWSO-5 CAPABILITIES** (Critical security-focused cleanup):
|
25
|
+
- 12-step dependency analysis and cleanup framework
|
26
|
+
- Default VPC elimination (8 identified VPCs for security compliance)
|
27
|
+
- ENI gate validation preventing accidental workload disruption
|
28
|
+
- Comprehensive evidence collection with audit trails
|
29
|
+
- IaC detection and integration for managed infrastructure
|
30
|
+
- Enterprise approval workflows with platform lead oversight
|
31
|
+
|
32
|
+
**ENHANCED CAPABILITIES** (integrated from vpc module):
|
33
|
+
- VPC creation/deletion with enterprise best practices
|
34
|
+
- Advanced NAT Gateway operations with cost analysis ($45/month optimization)
|
35
|
+
- VPC endpoint management and optimization
|
36
|
+
- Transit Gateway attachment operations
|
10
37
|
- VPC peering and cross-account connectivity
|
11
|
-
- Network security optimization
|
12
|
-
-
|
38
|
+
- Network security optimization and attack surface reduction
|
39
|
+
- Business-friendly manager interface for non-technical users
|
40
|
+
|
41
|
+
This module provides comprehensive VPC lifecycle management including:
|
42
|
+
- AWSO-5 VPC cleanup with 12-step dependency resolution
|
43
|
+
- NAT Gateway operations with cost optimization focus
|
44
|
+
- VPC infrastructure management with enterprise best practices
|
45
|
+
- Cross-account connectivity and network security
|
46
|
+
- Manager dashboard interface for business users
|
47
|
+
- Cost analysis and recommendations with enterprise MCP validation
|
48
|
+
|
49
|
+
**Security Focus Features**:
|
50
|
+
- Default VPC identification and elimination
|
51
|
+
- Attack surface reduction through systematic cleanup
|
52
|
+
- CIS Benchmark compliance enhancement
|
53
|
+
- Evidence-based validation with ≥99.5% MCP accuracy
|
13
54
|
|
14
55
|
Features:
|
15
|
-
- Multi-account support (1-200+ accounts)
|
56
|
+
- Multi-account support (1-200+ accounts) across validated Landing Zone
|
16
57
|
- Rich CLI integration with beautiful terminal output
|
17
|
-
- Enterprise safety (dry-run, confirmation, rollback)
|
18
|
-
-
|
19
|
-
-
|
58
|
+
- Enterprise safety (dry-run, confirmation, rollback, approval workflows)
|
59
|
+
- AWSO-5 framework integration with comprehensive dependency analysis
|
60
|
+
- Cost optimization integration with existing finops patterns
|
61
|
+
- Manager dashboard interface for executive decision making
|
62
|
+
- Comprehensive error handling and logging with audit trails
|
63
|
+
- SHA256-verified evidence bundle generation for compliance
|
20
64
|
"""
|
21
65
|
|
22
66
|
import time
|
@@ -28,9 +72,52 @@ import boto3
|
|
28
72
|
from botocore.exceptions import ClientError
|
29
73
|
from loguru import logger
|
30
74
|
|
31
|
-
from runbooks.common.rich_utils import
|
75
|
+
from runbooks.common.rich_utils import (
|
76
|
+
console, create_table, print_warning, print_success, print_error,
|
77
|
+
print_info, print_header, create_panel
|
78
|
+
)
|
32
79
|
from runbooks.operate.base import BaseOperation, OperationContext, OperationResult, OperationStatus
|
33
80
|
|
81
|
+
# VPC Module Migration Integration - Discovery capabilities
|
82
|
+
from runbooks.inventory.vpc_analyzer import VPCAnalyzer, VPCDiscoveryResult, AWSOAnalysis
|
83
|
+
|
84
|
+
|
85
|
+
class RichConsoleWrapper:
|
86
|
+
"""Wrapper to provide missing methods for VPC operations."""
|
87
|
+
|
88
|
+
def __init__(self, console_instance):
|
89
|
+
self.console = console_instance
|
90
|
+
|
91
|
+
def print(self, *args, **kwargs):
|
92
|
+
return self.console.print(*args, **kwargs)
|
93
|
+
|
94
|
+
def print_panel(self, content, subtitle=None, title="Panel"):
|
95
|
+
"""Print a panel with content."""
|
96
|
+
panel_content = content
|
97
|
+
if subtitle:
|
98
|
+
panel_content = f"{content}\n\n{subtitle}"
|
99
|
+
panel = create_panel(panel_content, title=title)
|
100
|
+
self.console.print(panel)
|
101
|
+
|
102
|
+
def print_table(self, table):
|
103
|
+
"""Print a table."""
|
104
|
+
self.console.print(table)
|
105
|
+
|
106
|
+
def print_success(self, message):
|
107
|
+
return print_success(message)
|
108
|
+
|
109
|
+
def print_error(self, message):
|
110
|
+
return print_error(message)
|
111
|
+
|
112
|
+
def print_info(self, message):
|
113
|
+
return print_info(message)
|
114
|
+
|
115
|
+
def print_warning(self, message):
|
116
|
+
return print_warning(message)
|
117
|
+
|
118
|
+
def print_header(self, title, subtitle=None):
|
119
|
+
return print_header(title, subtitle)
|
120
|
+
|
34
121
|
|
35
122
|
@dataclass
|
36
123
|
class VPCConfiguration:
|
@@ -151,7 +238,7 @@ class VPCOperations(BaseOperation):
|
|
151
238
|
|
152
239
|
def __init__(self, profile: Optional[str] = None, region: Optional[str] = None, dry_run: bool = False):
|
153
240
|
"""
|
154
|
-
Initialize VPC Operations with Enterprise safety features.
|
241
|
+
Initialize VPC Operations with Enterprise safety features and VPC module migration integration.
|
155
242
|
|
156
243
|
Args:
|
157
244
|
profile: AWS profile for authentication
|
@@ -159,15 +246,27 @@ class VPCOperations(BaseOperation):
|
|
159
246
|
dry_run: Enable dry-run mode for safe testing
|
160
247
|
"""
|
161
248
|
super().__init__(profile, region, dry_run)
|
162
|
-
self.rich_console =
|
249
|
+
self.rich_console = RichConsoleWrapper(console)
|
250
|
+
|
251
|
+
# VPC Module Migration Integration - Discovery capabilities
|
252
|
+
self.vpc_analyzer = VPCAnalyzer(
|
253
|
+
profile=profile,
|
254
|
+
region=region,
|
255
|
+
console=console,
|
256
|
+
dry_run=dry_run
|
257
|
+
)
|
163
258
|
|
164
259
|
# Cost tracking for NAT Gateways ($45/month awareness)
|
165
260
|
self.nat_gateway_monthly_cost = 45.0
|
166
261
|
|
167
262
|
# Cost tracking for Elastic IPs ($3.60/month awareness)
|
168
263
|
self.elastic_ip_monthly_cost = 3.60
|
264
|
+
|
265
|
+
# VPC module patterns integration
|
266
|
+
self.last_discovery_result = None
|
267
|
+
self.last_awso_analysis = None
|
169
268
|
|
170
|
-
logger.info(f"VPC Operations initialized - Profile: {profile}, Region: {region}, Dry-run: {dry_run}")
|
269
|
+
logger.info(f"VPC Operations initialized with VPC Analyzer - Profile: {profile}, Region: {region}, Dry-run: {dry_run}")
|
171
270
|
|
172
271
|
def execute_operation(self, context: OperationContext, operation_type: str, **kwargs) -> List[OperationResult]:
|
173
272
|
"""
|
@@ -239,12 +338,11 @@ class VPCOperations(BaseOperation):
|
|
239
338
|
|
240
339
|
# Display cost and configuration info
|
241
340
|
self.rich_console.print_panel(
|
242
|
-
f"Creating VPC: {vpc_config.name}",
|
243
341
|
f"CIDR Block: {vpc_config.cidr_block}\n"
|
244
342
|
f"Region: {context.region}\n"
|
245
343
|
f"DNS Hostnames: {vpc_config.enable_dns_hostnames}\n"
|
246
344
|
f"Instance Tenancy: {vpc_config.instance_tenancy}",
|
247
|
-
title="🏗️ VPC Creation"
|
345
|
+
title="🏗️ VPC Creation"
|
248
346
|
)
|
249
347
|
|
250
348
|
if context.dry_run:
|
@@ -724,6 +822,228 @@ class VPCOperations(BaseOperation):
|
|
724
822
|
|
725
823
|
return [result]
|
726
824
|
|
825
|
+
# VPC Module Migration Integration Methods
|
826
|
+
def discover_vpc_topology_comprehensive(self, vpc_ids: Optional[List[str]] = None) -> VPCDiscoveryResult:
|
827
|
+
"""
|
828
|
+
Comprehensive VPC topology discovery using migrated VPC module capabilities.
|
829
|
+
|
830
|
+
Integrates networking_wrapper.py discovery patterns with operate module operations.
|
831
|
+
Provides complete VPC topology analysis for AWSO-05 cleanup workflows.
|
832
|
+
|
833
|
+
Args:
|
834
|
+
vpc_ids: Optional list of specific VPC IDs to analyze
|
835
|
+
|
836
|
+
Returns:
|
837
|
+
VPCDiscoveryResult with complete topology information
|
838
|
+
"""
|
839
|
+
self.rich_console.print_header("VPC Topology Discovery", "Enhanced with VPC Module Integration")
|
840
|
+
|
841
|
+
try:
|
842
|
+
# Use integrated VPC analyzer for discovery
|
843
|
+
discovery_result = self.vpc_analyzer.discover_vpc_topology(vpc_ids)
|
844
|
+
self.last_discovery_result = discovery_result
|
845
|
+
|
846
|
+
# Display enterprise summary with cost information
|
847
|
+
total_monthly_cost = sum([
|
848
|
+
nat['EstimatedMonthlyCost'] for nat in discovery_result.nat_gateways
|
849
|
+
]) + sum([
|
850
|
+
ep['EstimatedMonthlyCost'] for ep in discovery_result.vpc_endpoints
|
851
|
+
])
|
852
|
+
|
853
|
+
self.rich_console.print_panel(
|
854
|
+
"VPC Discovery Integration Complete",
|
855
|
+
f"Total VPCs: {len(discovery_result.vpcs)}\n"
|
856
|
+
f"NAT Gateways: {len(discovery_result.nat_gateways)}\n"
|
857
|
+
f"VPC Endpoints: {len(discovery_result.vpc_endpoints)}\n"
|
858
|
+
f"Network Interfaces: {len(discovery_result.network_interfaces)}\n"
|
859
|
+
f"Estimated Monthly Network Cost: ${total_monthly_cost:.2f}\n"
|
860
|
+
f"Discovery Timestamp: {discovery_result.discovery_timestamp}",
|
861
|
+
title="🔍 VPC Module Integration"
|
862
|
+
)
|
863
|
+
|
864
|
+
return discovery_result
|
865
|
+
|
866
|
+
except Exception as e:
|
867
|
+
logger.error(f"VPC topology discovery failed: {e}")
|
868
|
+
self.rich_console.print_error(f"❌ VPC discovery failed: {e}")
|
869
|
+
raise
|
870
|
+
|
871
|
+
def analyze_awso_dependencies_comprehensive(self, discovery_result: Optional[VPCDiscoveryResult] = None) -> AWSOAnalysis:
|
872
|
+
"""
|
873
|
+
AWSO-05 dependency analysis using migrated VPC module capabilities.
|
874
|
+
|
875
|
+
Integrates cost_engine.py analysis patterns with AWSO-05 cleanup requirements.
|
876
|
+
Provides 12-step dependency validation for safe VPC cleanup operations.
|
877
|
+
|
878
|
+
Args:
|
879
|
+
discovery_result: Previous discovery result (uses last if None)
|
880
|
+
|
881
|
+
Returns:
|
882
|
+
AWSOAnalysis with comprehensive dependency mapping
|
883
|
+
"""
|
884
|
+
self.rich_console.print_header("AWSO-05 Dependency Analysis", "12-Step Framework Integration")
|
885
|
+
|
886
|
+
try:
|
887
|
+
# Use integrated VPC analyzer for AWSO analysis
|
888
|
+
awso_analysis = self.vpc_analyzer.analyze_awso_dependencies(discovery_result)
|
889
|
+
self.last_awso_analysis = awso_analysis
|
890
|
+
|
891
|
+
# Display business-critical warnings
|
892
|
+
if awso_analysis.eni_gate_warnings:
|
893
|
+
self.rich_console.print_warning(
|
894
|
+
f"🚨 CRITICAL: {len(awso_analysis.eni_gate_warnings)} ENI gate warnings detected!\n"
|
895
|
+
"VPC cleanup may disrupt active workloads. Review migration requirements."
|
896
|
+
)
|
897
|
+
|
898
|
+
if awso_analysis.default_vpcs:
|
899
|
+
self.rich_console.print_info(
|
900
|
+
f"🎯 Default VPCs found: {len(awso_analysis.default_vpcs)}\n"
|
901
|
+
"CIS Benchmark compliance can be improved through cleanup."
|
902
|
+
)
|
903
|
+
|
904
|
+
# Display cleanup readiness status
|
905
|
+
cleanup_status = awso_analysis.evidence_bundle.get('CleanupReadiness', 'UNKNOWN')
|
906
|
+
status_style = "green" if cleanup_status == 'READY' else "yellow"
|
907
|
+
|
908
|
+
self.rich_console.print_panel(
|
909
|
+
"AWSO-05 Analysis Complete",
|
910
|
+
f"Default VPCs: {len(awso_analysis.default_vpcs)}\n"
|
911
|
+
f"ENI Warnings: {len(awso_analysis.eni_gate_warnings)}\n"
|
912
|
+
f"Cleanup Recommendations: {len(awso_analysis.cleanup_recommendations)}\n"
|
913
|
+
f"Cleanup Readiness: {cleanup_status}",
|
914
|
+
title="🎯 AWSO-05 Analysis Results",
|
915
|
+
style=status_style
|
916
|
+
)
|
917
|
+
|
918
|
+
return awso_analysis
|
919
|
+
|
920
|
+
except Exception as e:
|
921
|
+
logger.error(f"AWSO-05 analysis failed: {e}")
|
922
|
+
self.rich_console.print_error(f"❌ AWSO-05 analysis failed: {e}")
|
923
|
+
raise
|
924
|
+
|
925
|
+
def generate_vpc_evidence_bundle(self, output_dir: str = "./awso_evidence") -> Dict[str, str]:
|
926
|
+
"""
|
927
|
+
Generate comprehensive evidence bundle using migrated VPC module capabilities.
|
928
|
+
|
929
|
+
Integrates manager_interface.py reporting patterns with AWSO-05 compliance requirements.
|
930
|
+
Creates SHA256-verified evidence bundle for audit trails and compliance.
|
931
|
+
|
932
|
+
Args:
|
933
|
+
output_dir: Directory to store evidence files
|
934
|
+
|
935
|
+
Returns:
|
936
|
+
Dict with generated file paths and checksums
|
937
|
+
"""
|
938
|
+
self.rich_console.print_header("Evidence Bundle Generation", "Enterprise Compliance Integration")
|
939
|
+
|
940
|
+
try:
|
941
|
+
# Use integrated VPC analyzer for evidence generation
|
942
|
+
evidence_files = self.vpc_analyzer.generate_cleanup_evidence(output_dir)
|
943
|
+
|
944
|
+
if evidence_files:
|
945
|
+
self.rich_console.print_success(
|
946
|
+
f"✅ Evidence bundle generated successfully!\n"
|
947
|
+
f"Files created: {len(evidence_files)}\n"
|
948
|
+
f"Output directory: {output_dir}"
|
949
|
+
)
|
950
|
+
|
951
|
+
# Display evidence summary for manager interface compatibility
|
952
|
+
self.rich_console.print_panel(
|
953
|
+
"Evidence Bundle Summary",
|
954
|
+
"\n".join([f"• {evidence_type}: {file_path.split('/')[-1]}"
|
955
|
+
for evidence_type, file_path in evidence_files.items()]),
|
956
|
+
title="📋 AWSO-05 Evidence Files"
|
957
|
+
)
|
958
|
+
else:
|
959
|
+
self.rich_console.print_warning("⚠️ No evidence files generated - run discovery and analysis first")
|
960
|
+
|
961
|
+
return evidence_files
|
962
|
+
|
963
|
+
except Exception as e:
|
964
|
+
logger.error(f"Evidence bundle generation failed: {e}")
|
965
|
+
self.rich_console.print_error(f"❌ Evidence bundle generation failed: {e}")
|
966
|
+
raise
|
967
|
+
|
968
|
+
def execute_integrated_vpc_analysis(self, vpc_ids: Optional[List[str]] = None,
|
969
|
+
generate_evidence: bool = True) -> Dict[str, Any]:
|
970
|
+
"""
|
971
|
+
Execute complete integrated VPC analysis workflow using migrated VPC module capabilities.
|
972
|
+
|
973
|
+
Combines networking_wrapper.py, cost_engine.py, and manager_interface.py patterns
|
974
|
+
into a single comprehensive analysis workflow for enterprise VPC management.
|
975
|
+
|
976
|
+
Args:
|
977
|
+
vpc_ids: Optional list of specific VPC IDs to analyze
|
978
|
+
generate_evidence: Whether to generate evidence bundle
|
979
|
+
|
980
|
+
Returns:
|
981
|
+
Dict with complete analysis results and evidence files
|
982
|
+
"""
|
983
|
+
self.rich_console.print_header("Integrated VPC Analysis", "Complete VPC Module Integration")
|
984
|
+
|
985
|
+
workflow_results = {
|
986
|
+
'discovery_result': None,
|
987
|
+
'awso_analysis': None,
|
988
|
+
'evidence_files': None,
|
989
|
+
'analysis_summary': {}
|
990
|
+
}
|
991
|
+
|
992
|
+
try:
|
993
|
+
# Step 1: VPC Topology Discovery
|
994
|
+
self.rich_console.print_info("🔍 Step 1: VPC Topology Discovery...")
|
995
|
+
discovery_result = self.discover_vpc_topology_comprehensive(vpc_ids)
|
996
|
+
workflow_results['discovery_result'] = discovery_result
|
997
|
+
|
998
|
+
# Step 2: AWSO-05 Dependency Analysis
|
999
|
+
self.rich_console.print_info("🎯 Step 2: AWSO-05 Dependency Analysis...")
|
1000
|
+
awso_analysis = self.analyze_awso_dependencies_comprehensive(discovery_result)
|
1001
|
+
workflow_results['awso_analysis'] = awso_analysis
|
1002
|
+
|
1003
|
+
# Step 3: Evidence Bundle Generation (if requested)
|
1004
|
+
if generate_evidence:
|
1005
|
+
self.rich_console.print_info("📋 Step 3: Evidence Bundle Generation...")
|
1006
|
+
evidence_files = self.generate_vpc_evidence_bundle()
|
1007
|
+
workflow_results['evidence_files'] = evidence_files
|
1008
|
+
|
1009
|
+
# Step 4: Analysis Summary (manager interface compatibility)
|
1010
|
+
total_monthly_cost = sum([
|
1011
|
+
nat['EstimatedMonthlyCost'] for nat in discovery_result.nat_gateways
|
1012
|
+
]) + sum([
|
1013
|
+
ep['EstimatedMonthlyCost'] for ep in discovery_result.vpc_endpoints
|
1014
|
+
])
|
1015
|
+
|
1016
|
+
workflow_results['analysis_summary'] = {
|
1017
|
+
'total_resources': discovery_result.total_resources,
|
1018
|
+
'estimated_monthly_cost': total_monthly_cost,
|
1019
|
+
'default_vpcs_found': len(awso_analysis.default_vpcs),
|
1020
|
+
'eni_gate_warnings': len(awso_analysis.eni_gate_warnings),
|
1021
|
+
'cleanup_recommendations': len(awso_analysis.cleanup_recommendations),
|
1022
|
+
'cleanup_readiness': awso_analysis.evidence_bundle.get('CleanupReadiness', 'UNKNOWN'),
|
1023
|
+
'cis_benchmark_compliance': awso_analysis.evidence_bundle.get('ComplianceStatus', {}).get('CISBenchmark', 'UNKNOWN')
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
# Display comprehensive summary
|
1027
|
+
summary = workflow_results['analysis_summary']
|
1028
|
+
self.rich_console.print_panel(
|
1029
|
+
"Integrated VPC Analysis Complete",
|
1030
|
+
f"Total Resources Discovered: {summary['total_resources']}\n"
|
1031
|
+
f"Estimated Monthly Network Cost: ${summary['estimated_monthly_cost']:.2f}\n"
|
1032
|
+
f"Default VPCs (Security Risk): {summary['default_vpcs_found']}\n"
|
1033
|
+
f"ENI Gate Warnings: {summary['eni_gate_warnings']}\n"
|
1034
|
+
f"Cleanup Recommendations: {summary['cleanup_recommendations']}\n"
|
1035
|
+
f"Cleanup Readiness: {summary['cleanup_readiness']}\n"
|
1036
|
+
f"CIS Benchmark Status: {summary['cis_benchmark_compliance']}",
|
1037
|
+
title="🏆 VPC Module Integration Results"
|
1038
|
+
)
|
1039
|
+
|
1040
|
+
return workflow_results
|
1041
|
+
|
1042
|
+
except Exception as e:
|
1043
|
+
logger.error(f"Integrated VPC analysis failed: {e}")
|
1044
|
+
self.rich_console.print_error(f"❌ Integrated VPC analysis failed: {e}")
|
1045
|
+
raise
|
1046
|
+
|
727
1047
|
def _discover_unused_eips(
|
728
1048
|
self, context: OperationContext, target_region: Optional[str] = None
|
729
1049
|
) -> List[OperationResult]:
|
@@ -1036,3 +1356,1146 @@ class VPCOperations(BaseOperation):
|
|
1036
1356
|
except Exception as e:
|
1037
1357
|
logger.warning(f"Could not get all regions, using defaults: {e}")
|
1038
1358
|
return ["us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"]
|
1359
|
+
|
1360
|
+
|
1361
|
+
# ============================================================================
|
1362
|
+
# ENHANCED VPC MANAGEMENT - VPC Module Migration Integration
|
1363
|
+
# ============================================================================
|
1364
|
+
|
1365
|
+
from enum import Enum
|
1366
|
+
from pathlib import Path
|
1367
|
+
|
1368
|
+
class BusinessPriority(Enum):
|
1369
|
+
"""Business priority levels for manager decision making"""
|
1370
|
+
CRITICAL = "Critical"
|
1371
|
+
HIGH = "High"
|
1372
|
+
MEDIUM = "Medium"
|
1373
|
+
LOW = "Low"
|
1374
|
+
|
1375
|
+
|
1376
|
+
class RiskLevel(Enum):
|
1377
|
+
"""Risk assessment levels for business decisions"""
|
1378
|
+
MINIMAL = "Minimal"
|
1379
|
+
LOW = "Low"
|
1380
|
+
MEDIUM = "Medium"
|
1381
|
+
HIGH = "High"
|
1382
|
+
|
1383
|
+
|
1384
|
+
@dataclass
|
1385
|
+
class BusinessRecommendation:
|
1386
|
+
"""Business-focused recommendation structure migrated from vpc module"""
|
1387
|
+
title: str
|
1388
|
+
executive_summary: str
|
1389
|
+
monthly_savings: float
|
1390
|
+
annual_impact: float
|
1391
|
+
implementation_timeline: str
|
1392
|
+
business_priority: BusinessPriority
|
1393
|
+
risk_level: RiskLevel
|
1394
|
+
resource_requirements: List[str]
|
1395
|
+
success_metrics: List[str]
|
1396
|
+
approval_required: bool
|
1397
|
+
quick_win: bool
|
1398
|
+
strategic_value: str
|
1399
|
+
|
1400
|
+
|
1401
|
+
@dataclass
|
1402
|
+
class ManagerDashboardConfig:
|
1403
|
+
"""Configuration for manager dashboard behavior"""
|
1404
|
+
safety_mode: bool = True
|
1405
|
+
auto_export: bool = True
|
1406
|
+
executive_summaries_only: bool = False
|
1407
|
+
approval_threshold: float = 1000.0
|
1408
|
+
target_savings_percentage: float = 30.0
|
1409
|
+
max_implementation_weeks: int = 12
|
1410
|
+
preferred_export_formats: List[str] = None
|
1411
|
+
|
1412
|
+
def __post_init__(self):
|
1413
|
+
if self.preferred_export_formats is None:
|
1414
|
+
self.preferred_export_formats = ["json", "csv", "excel"]
|
1415
|
+
|
1416
|
+
|
1417
|
+
class EnhancedVPCNetworkingManager(BaseOperation):
|
1418
|
+
"""
|
1419
|
+
Enhanced VPC Networking Manager - Migrated capabilities from vpc module
|
1420
|
+
|
1421
|
+
Integrates networking_wrapper.py, manager_interface.py, and cost_engine.py
|
1422
|
+
capabilities into operate module following "Do one thing and do it well" principle.
|
1423
|
+
|
1424
|
+
Provides enterprise VPC management with:
|
1425
|
+
- Manager-friendly business interface
|
1426
|
+
- Cost optimization with MCP validation
|
1427
|
+
- Network topology management
|
1428
|
+
- Safety-first operations with approval workflows
|
1429
|
+
"""
|
1430
|
+
|
1431
|
+
def __init__(self, dry_run: bool = True):
|
1432
|
+
super().__init__(dry_run=dry_run)
|
1433
|
+
self.operation_name = "enhanced_vpc_networking"
|
1434
|
+
self.manager_config = ManagerDashboardConfig()
|
1435
|
+
self.analysis_results = {}
|
1436
|
+
self.business_recommendations = []
|
1437
|
+
self.export_directory = Path("./tmp/manager_dashboard")
|
1438
|
+
|
1439
|
+
# Cost model integration from vpc cost_engine
|
1440
|
+
self.nat_gateway_hourly_cost = 0.045 # $0.045/hour
|
1441
|
+
self.nat_gateway_data_processing = 0.045 # $0.045/GB
|
1442
|
+
self.transit_gateway_monthly_cost = 36.50
|
1443
|
+
self.vpc_endpoint_hourly_cost = 0.01
|
1444
|
+
|
1445
|
+
def execute_operation(self, context: OperationContext, operation_type: str, **kwargs) -> List[OperationResult]:
|
1446
|
+
"""Enhanced VPC operations with manager interface support"""
|
1447
|
+
if operation_type.startswith("analyze_network_topology"):
|
1448
|
+
return self._analyze_network_topology_comprehensive(context, **kwargs)
|
1449
|
+
elif operation_type.startswith("generate_manager_report"):
|
1450
|
+
return self._generate_manager_report(context, **kwargs)
|
1451
|
+
elif operation_type.startswith("optimize_vpc_costs"):
|
1452
|
+
return self._optimize_vpc_costs_comprehensive(context, **kwargs)
|
1453
|
+
elif operation_type.startswith("analyze_nat_gateway_usage"):
|
1454
|
+
return self._analyze_nat_gateway_usage_detailed(context, **kwargs)
|
1455
|
+
elif operation_type.startswith("manage_vpc_endpoints"):
|
1456
|
+
return self._manage_vpc_endpoints(context, **kwargs)
|
1457
|
+
else:
|
1458
|
+
# Fall back to base VPC operations
|
1459
|
+
return super().execute_operation(context, operation_type, **kwargs)
|
1460
|
+
|
1461
|
+
def _analyze_network_topology_comprehensive(self, context: OperationContext, **kwargs) -> List[OperationResult]:
|
1462
|
+
"""
|
1463
|
+
Comprehensive network topology analysis migrated from networking_wrapper.py
|
1464
|
+
"""
|
1465
|
+
result = self.create_operation_result(context, "analyze_network_topology", "vpc", "network-topology")
|
1466
|
+
|
1467
|
+
try:
|
1468
|
+
self.rich_console.print_header("Comprehensive Network Topology Analysis", "v0.9.1")
|
1469
|
+
|
1470
|
+
topology_analysis = {
|
1471
|
+
"timestamp": datetime.now().isoformat(),
|
1472
|
+
"account_id": context.account_id,
|
1473
|
+
"region": context.region,
|
1474
|
+
"vpc_topology": {},
|
1475
|
+
"cost_analysis": {},
|
1476
|
+
"optimization_opportunities": [],
|
1477
|
+
"business_recommendations": []
|
1478
|
+
}
|
1479
|
+
|
1480
|
+
ec2_client = self.get_client("ec2", context.region)
|
1481
|
+
|
1482
|
+
# Get comprehensive VPC data
|
1483
|
+
vpcs_response = self.execute_aws_call(ec2_client, "describe_vpcs")
|
1484
|
+
nat_response = self.execute_aws_call(ec2_client, "describe_nat_gateways")
|
1485
|
+
endpoints_response = self.execute_aws_call(ec2_client, "describe_vpc_endpoints")
|
1486
|
+
|
1487
|
+
# Process VPCs with enhanced metadata
|
1488
|
+
vpcs_data = []
|
1489
|
+
total_monthly_cost = 0
|
1490
|
+
|
1491
|
+
for vpc in vpcs_response["Vpcs"]:
|
1492
|
+
vpc_analysis = self._analyze_vpc_comprehensive(ec2_client, vpc, context)
|
1493
|
+
vpcs_data.append(vpc_analysis)
|
1494
|
+
total_monthly_cost += vpc_analysis.get("estimated_monthly_cost", 0)
|
1495
|
+
|
1496
|
+
# Process NAT Gateways
|
1497
|
+
nat_gateways_data = []
|
1498
|
+
for nat in nat_response["NatGateways"]:
|
1499
|
+
if nat["State"] != "deleted":
|
1500
|
+
nat_analysis = self._analyze_nat_gateway_costs(ec2_client, nat, context)
|
1501
|
+
nat_gateways_data.append(nat_analysis)
|
1502
|
+
|
1503
|
+
# Generate business recommendations
|
1504
|
+
business_recommendations = self._generate_business_recommendations(
|
1505
|
+
vpcs_data, nat_gateways_data, total_monthly_cost
|
1506
|
+
)
|
1507
|
+
|
1508
|
+
topology_analysis.update({
|
1509
|
+
"vpcs": vpcs_data,
|
1510
|
+
"nat_gateways": nat_gateways_data,
|
1511
|
+
"vpc_endpoints": self._analyze_vpc_endpoints(endpoints_response["VpcEndpoints"]),
|
1512
|
+
"total_monthly_cost": total_monthly_cost,
|
1513
|
+
"total_annual_cost": total_monthly_cost * 12,
|
1514
|
+
"business_recommendations": business_recommendations
|
1515
|
+
})
|
1516
|
+
|
1517
|
+
# Display results with Rich formatting
|
1518
|
+
self._display_topology_analysis(topology_analysis)
|
1519
|
+
|
1520
|
+
result.mark_completed(OperationStatus.SUCCESS)
|
1521
|
+
result.response_data = topology_analysis
|
1522
|
+
|
1523
|
+
except Exception as e:
|
1524
|
+
error_msg = f"Network topology analysis failed: {e}"
|
1525
|
+
result.mark_completed(OperationStatus.FAILED, error_msg)
|
1526
|
+
self.rich_console.print_error(f"❌ {error_msg}")
|
1527
|
+
logger.error(error_msg)
|
1528
|
+
|
1529
|
+
return [result]
|
1530
|
+
|
1531
|
+
def _analyze_vpc_comprehensive(self, ec2_client, vpc: Dict[str, Any], context: OperationContext) -> Dict[str, Any]:
|
1532
|
+
"""Comprehensive VPC analysis with cost implications"""
|
1533
|
+
tags = {tag["Key"]: tag["Value"] for tag in vpc.get("Tags", [])}
|
1534
|
+
|
1535
|
+
vpc_analysis = {
|
1536
|
+
"vpc_id": vpc["VpcId"],
|
1537
|
+
"cidr_block": vpc["CidrBlock"],
|
1538
|
+
"state": vpc["State"],
|
1539
|
+
"name": tags.get("Name", vpc["VpcId"]),
|
1540
|
+
"tags": tags,
|
1541
|
+
"is_default": vpc.get("IsDefault", False),
|
1542
|
+
"subnets": [],
|
1543
|
+
"security_groups": [],
|
1544
|
+
"route_tables": [],
|
1545
|
+
"estimated_monthly_cost": 0,
|
1546
|
+
"optimization_opportunities": []
|
1547
|
+
}
|
1548
|
+
|
1549
|
+
try:
|
1550
|
+
# Get subnets
|
1551
|
+
subnets_response = self.execute_aws_call(
|
1552
|
+
ec2_client, "describe_subnets",
|
1553
|
+
Filters=[{"Name": "vpc-id", "Values": [vpc["VpcId"]]}]
|
1554
|
+
)
|
1555
|
+
|
1556
|
+
for subnet in subnets_response["Subnets"]:
|
1557
|
+
subnet_tags = {tag["Key"]: tag["Value"] for tag in subnet.get("Tags", [])}
|
1558
|
+
vpc_analysis["subnets"].append({
|
1559
|
+
"subnet_id": subnet["SubnetId"],
|
1560
|
+
"cidr_block": subnet["CidrBlock"],
|
1561
|
+
"availability_zone": subnet["AvailabilityZone"],
|
1562
|
+
"available_ip_address_count": subnet["AvailableIpAddressCount"],
|
1563
|
+
"map_public_ip_on_launch": subnet.get("MapPublicIpOnLaunch", False),
|
1564
|
+
"tags": subnet_tags,
|
1565
|
+
"name": subnet_tags.get("Name", subnet["SubnetId"])
|
1566
|
+
})
|
1567
|
+
|
1568
|
+
# Basic cost estimation (placeholder for more sophisticated analysis)
|
1569
|
+
if len(vpc_analysis["subnets"]) > 10:
|
1570
|
+
vpc_analysis["optimization_opportunities"].append({
|
1571
|
+
"type": "subnet_consolidation",
|
1572
|
+
"description": f"VPC has {len(vpc_analysis['subnets'])} subnets - consider consolidation",
|
1573
|
+
"potential_savings": "Reduced management overhead"
|
1574
|
+
})
|
1575
|
+
|
1576
|
+
except Exception as e:
|
1577
|
+
logger.warning(f"Failed to get detailed VPC data for {vpc['VpcId']}: {e}")
|
1578
|
+
|
1579
|
+
return vpc_analysis
|
1580
|
+
|
1581
|
+
def _analyze_nat_gateway_costs(self, ec2_client, nat: Dict[str, Any], context: OperationContext) -> Dict[str, Any]:
|
1582
|
+
"""Detailed NAT Gateway cost analysis"""
|
1583
|
+
tags = {tag["Key"]: tag["Value"] for tag in nat.get("Tags", [])}
|
1584
|
+
|
1585
|
+
# Base monthly cost (24/7 * 30 days * $0.045/hour)
|
1586
|
+
base_monthly_cost = 24 * 30 * self.nat_gateway_hourly_cost
|
1587
|
+
|
1588
|
+
nat_analysis = {
|
1589
|
+
"nat_gateway_id": nat["NatGatewayId"],
|
1590
|
+
"state": nat["State"],
|
1591
|
+
"vpc_id": nat.get("VpcId"),
|
1592
|
+
"subnet_id": nat.get("SubnetId"),
|
1593
|
+
"connectivity_type": nat.get("ConnectivityType", "public"),
|
1594
|
+
"tags": tags,
|
1595
|
+
"name": tags.get("Name", nat["NatGatewayId"]),
|
1596
|
+
"base_monthly_cost": base_monthly_cost,
|
1597
|
+
"estimated_data_processing_cost": 0, # Would need CloudWatch metrics for accurate calculation
|
1598
|
+
"total_estimated_monthly_cost": base_monthly_cost,
|
1599
|
+
"optimization_recommendation": "monitor_usage"
|
1600
|
+
}
|
1601
|
+
|
1602
|
+
# Add optimization recommendations based on analysis
|
1603
|
+
if nat["State"] == "available":
|
1604
|
+
nat_analysis["optimization_recommendation"] = "monitor_usage"
|
1605
|
+
elif nat["State"] in ["pending", "failed"]:
|
1606
|
+
nat_analysis["optimization_recommendation"] = "investigate_health"
|
1607
|
+
|
1608
|
+
return nat_analysis
|
1609
|
+
|
1610
|
+
def _analyze_vpc_endpoints(self, endpoints: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
1611
|
+
"""Analyze VPC endpoints with cost implications"""
|
1612
|
+
endpoints_analysis = []
|
1613
|
+
|
1614
|
+
for endpoint in endpoints:
|
1615
|
+
tags = {tag["Key"]: tag["Value"] for tag in endpoint.get("Tags", [])}
|
1616
|
+
|
1617
|
+
# Estimate monthly cost based on endpoint type
|
1618
|
+
if endpoint.get("VpcEndpointType") == "Interface":
|
1619
|
+
estimated_monthly_cost = 24 * 30 * self.vpc_endpoint_hourly_cost # Interface endpoints have hourly charges
|
1620
|
+
else:
|
1621
|
+
estimated_monthly_cost = 0 # Gateway endpoints are typically free
|
1622
|
+
|
1623
|
+
endpoints_analysis.append({
|
1624
|
+
"vpc_endpoint_id": endpoint["VpcEndpointId"],
|
1625
|
+
"vpc_id": endpoint.get("VpcId"),
|
1626
|
+
"service_name": endpoint.get("ServiceName"),
|
1627
|
+
"endpoint_type": endpoint.get("VpcEndpointType"),
|
1628
|
+
"state": endpoint.get("State"),
|
1629
|
+
"tags": tags,
|
1630
|
+
"name": tags.get("Name", endpoint["VpcEndpointId"]),
|
1631
|
+
"estimated_monthly_cost": estimated_monthly_cost,
|
1632
|
+
"route_table_ids": endpoint.get("RouteTableIds", []),
|
1633
|
+
"subnet_ids": endpoint.get("SubnetIds", [])
|
1634
|
+
})
|
1635
|
+
|
1636
|
+
return endpoints_analysis
|
1637
|
+
|
1638
|
+
def _generate_business_recommendations(self, vpcs_data: List[Dict], nat_data: List[Dict], total_cost: float) -> List[BusinessRecommendation]:
|
1639
|
+
"""Generate business-focused recommendations"""
|
1640
|
+
recommendations = []
|
1641
|
+
|
1642
|
+
# NAT Gateway optimization recommendation
|
1643
|
+
active_nat_gateways = [nat for nat in nat_data if nat["state"] == "available"]
|
1644
|
+
if len(active_nat_gateways) > len(vpcs_data):
|
1645
|
+
potential_savings = (len(active_nat_gateways) - len(vpcs_data)) * 45.0 # $45/month per NAT Gateway
|
1646
|
+
|
1647
|
+
recommendations.append(BusinessRecommendation(
|
1648
|
+
title="NAT Gateway Consolidation Opportunity",
|
1649
|
+
executive_summary=f"Multiple NAT Gateways detected ({len(active_nat_gateways)}) across {len(vpcs_data)} VPCs",
|
1650
|
+
monthly_savings=potential_savings,
|
1651
|
+
annual_impact=potential_savings * 12,
|
1652
|
+
implementation_timeline="2-4 weeks",
|
1653
|
+
business_priority=BusinessPriority.HIGH if potential_savings > 90 else BusinessPriority.MEDIUM,
|
1654
|
+
risk_level=RiskLevel.LOW,
|
1655
|
+
resource_requirements=["Network engineer", "1-2 hours/NAT Gateway"],
|
1656
|
+
success_metrics=[f"Reduce monthly NAT Gateway costs by ${potential_savings:.2f}"],
|
1657
|
+
approval_required=potential_savings > self.manager_config.approval_threshold,
|
1658
|
+
quick_win=potential_savings > 50,
|
1659
|
+
strategic_value="Cost optimization without service impact"
|
1660
|
+
))
|
1661
|
+
|
1662
|
+
# VPC complexity recommendation
|
1663
|
+
complex_vpcs = [vpc for vpc in vpcs_data if len(vpc.get("subnets", [])) > 15]
|
1664
|
+
if complex_vpcs:
|
1665
|
+
recommendations.append(BusinessRecommendation(
|
1666
|
+
title="VPC Architecture Simplification",
|
1667
|
+
executive_summary=f"Detected {len(complex_vpcs)} VPCs with high subnet complexity",
|
1668
|
+
monthly_savings=0, # Operational savings, not direct cost
|
1669
|
+
annual_impact=0,
|
1670
|
+
implementation_timeline="6-12 weeks",
|
1671
|
+
business_priority=BusinessPriority.MEDIUM,
|
1672
|
+
risk_level=RiskLevel.MEDIUM,
|
1673
|
+
resource_requirements=["Cloud architect", "Network engineer", "4-8 hours/VPC"],
|
1674
|
+
success_metrics=["Reduced operational complexity", "Improved maintainability"],
|
1675
|
+
approval_required=True,
|
1676
|
+
quick_win=False,
|
1677
|
+
strategic_value="Improved operational efficiency and reduced management overhead"
|
1678
|
+
))
|
1679
|
+
|
1680
|
+
return recommendations
|
1681
|
+
|
1682
|
+
def _display_topology_analysis(self, topology_analysis: Dict[str, Any]) -> None:
|
1683
|
+
"""Display topology analysis with Rich formatting"""
|
1684
|
+
|
1685
|
+
# Summary panel
|
1686
|
+
summary_text = (
|
1687
|
+
f"Total VPCs: {len(topology_analysis.get('vpcs', []))}\n"
|
1688
|
+
f"Total NAT Gateways: {len(topology_analysis.get('nat_gateways', []))}\n"
|
1689
|
+
f"Total VPC Endpoints: {len(topology_analysis.get('vpc_endpoints', []))}\n"
|
1690
|
+
f"Estimated monthly cost: ${topology_analysis.get('total_monthly_cost', 0):.2f}\n"
|
1691
|
+
f"Estimated annual cost: ${topology_analysis.get('total_annual_cost', 0):.2f}"
|
1692
|
+
)
|
1693
|
+
|
1694
|
+
self.rich_console.print_panel(
|
1695
|
+
"Network Topology Summary",
|
1696
|
+
summary_text,
|
1697
|
+
title="🏗️ Infrastructure Overview"
|
1698
|
+
)
|
1699
|
+
|
1700
|
+
# Business recommendations table
|
1701
|
+
recommendations = topology_analysis.get('business_recommendations', [])
|
1702
|
+
if recommendations:
|
1703
|
+
rec_data = []
|
1704
|
+
for rec in recommendations:
|
1705
|
+
rec_data.append([
|
1706
|
+
rec.title,
|
1707
|
+
rec.business_priority.value,
|
1708
|
+
f"${rec.monthly_savings:.2f}",
|
1709
|
+
f"${rec.annual_impact:.2f}",
|
1710
|
+
rec.implementation_timeline,
|
1711
|
+
"Yes" if rec.approval_required else "No"
|
1712
|
+
])
|
1713
|
+
|
1714
|
+
self.rich_console.print_table(
|
1715
|
+
rec_data,
|
1716
|
+
headers=["Recommendation", "Priority", "Monthly Savings", "Annual Impact", "Timeline", "Approval Required"],
|
1717
|
+
title="💡 Business Optimization Recommendations"
|
1718
|
+
)
|
1719
|
+
|
1720
|
+
def _generate_manager_report(self, context: OperationContext, **kwargs) -> List[OperationResult]:
|
1721
|
+
"""Generate manager-friendly business report"""
|
1722
|
+
result = self.create_operation_result(context, "generate_manager_report", "vpc", "manager-report")
|
1723
|
+
|
1724
|
+
try:
|
1725
|
+
self.rich_console.print_header("Manager Dashboard - VPC Cost Optimization", "v0.9.1")
|
1726
|
+
|
1727
|
+
# First run comprehensive analysis
|
1728
|
+
analysis_results = self._analyze_network_topology_comprehensive(context, **kwargs)
|
1729
|
+
analysis_data = analysis_results[0].response_data
|
1730
|
+
|
1731
|
+
# Generate executive summary
|
1732
|
+
executive_report = {
|
1733
|
+
"report_generated": datetime.now().isoformat(),
|
1734
|
+
"executive_summary": {
|
1735
|
+
"total_infrastructure_cost": analysis_data.get("total_monthly_cost", 0),
|
1736
|
+
"optimization_opportunities": len(analysis_data.get("business_recommendations", [])),
|
1737
|
+
"immediate_actions": [
|
1738
|
+
rec for rec in analysis_data.get("business_recommendations", [])
|
1739
|
+
if rec.quick_win and rec.business_priority in [BusinessPriority.HIGH, BusinessPriority.CRITICAL]
|
1740
|
+
],
|
1741
|
+
"approval_required_items": [
|
1742
|
+
rec for rec in analysis_data.get("business_recommendations", [])
|
1743
|
+
if rec.approval_required
|
1744
|
+
]
|
1745
|
+
},
|
1746
|
+
"detailed_analysis": analysis_data,
|
1747
|
+
"next_steps": self._generate_next_steps(analysis_data.get("business_recommendations", []))
|
1748
|
+
}
|
1749
|
+
|
1750
|
+
# Display executive summary
|
1751
|
+
self._display_executive_summary(executive_report["executive_summary"])
|
1752
|
+
|
1753
|
+
result.mark_completed(OperationStatus.SUCCESS)
|
1754
|
+
result.response_data = executive_report
|
1755
|
+
|
1756
|
+
except Exception as e:
|
1757
|
+
error_msg = f"Manager report generation failed: {e}"
|
1758
|
+
result.mark_completed(OperationStatus.FAILED, error_msg)
|
1759
|
+
self.rich_console.print_error(f"❌ {error_msg}")
|
1760
|
+
logger.error(error_msg)
|
1761
|
+
|
1762
|
+
return [result]
|
1763
|
+
|
1764
|
+
def _generate_next_steps(self, recommendations: List[BusinessRecommendation]) -> List[Dict[str, str]]:
|
1765
|
+
"""Generate actionable next steps for managers"""
|
1766
|
+
next_steps = []
|
1767
|
+
|
1768
|
+
high_priority = [rec for rec in recommendations if rec.business_priority == BusinessPriority.HIGH]
|
1769
|
+
quick_wins = [rec for rec in recommendations if rec.quick_win]
|
1770
|
+
|
1771
|
+
if high_priority:
|
1772
|
+
next_steps.append({
|
1773
|
+
"action": "Review High Priority Items",
|
1774
|
+
"description": f"Review {len(high_priority)} high-priority optimization opportunities",
|
1775
|
+
"timeline": "This week"
|
1776
|
+
})
|
1777
|
+
|
1778
|
+
if quick_wins:
|
1779
|
+
next_steps.append({
|
1780
|
+
"action": "Implement Quick Wins",
|
1781
|
+
"description": f"Execute {len(quick_wins)} quick-win optimizations",
|
1782
|
+
"timeline": "Next 2 weeks"
|
1783
|
+
})
|
1784
|
+
|
1785
|
+
next_steps.append({
|
1786
|
+
"action": "Schedule Technical Review",
|
1787
|
+
"description": "Meet with technical team to discuss implementation details",
|
1788
|
+
"timeline": "Within 2 weeks"
|
1789
|
+
})
|
1790
|
+
|
1791
|
+
return next_steps
|
1792
|
+
|
1793
|
+
def _display_executive_summary(self, summary: Dict[str, Any]) -> None:
|
1794
|
+
"""Display executive summary with business-friendly formatting"""
|
1795
|
+
|
1796
|
+
summary_text = (
|
1797
|
+
f"Monthly Infrastructure Cost: ${summary.get('total_infrastructure_cost', 0):.2f}\n"
|
1798
|
+
f"Optimization Opportunities: {summary.get('optimization_opportunities', 0)}\n"
|
1799
|
+
f"Quick Win Actions: {len(summary.get('immediate_actions', []))}\n"
|
1800
|
+
f"Items Requiring Approval: {len(summary.get('approval_required_items', []))}"
|
1801
|
+
)
|
1802
|
+
|
1803
|
+
self.rich_console.print_panel(
|
1804
|
+
"Executive Dashboard",
|
1805
|
+
summary_text,
|
1806
|
+
title="📊 Business Overview"
|
1807
|
+
)
|
1808
|
+
|
1809
|
+
|
1810
|
+
# =============================================================================
|
1811
|
+
# AWSO-5 VPC Cleanup Operations - Enterprise Security Framework
|
1812
|
+
# =============================================================================
|
1813
|
+
|
1814
|
+
@dataclass
|
1815
|
+
class VPCCleanupConfiguration:
|
1816
|
+
"""AWSO-5 VPC cleanup configuration with enterprise safety controls."""
|
1817
|
+
|
1818
|
+
vpc_id: str
|
1819
|
+
dry_run: bool = True
|
1820
|
+
force_cleanup: bool = False
|
1821
|
+
approval_token: Optional[str] = None
|
1822
|
+
evidence_collection: bool = True
|
1823
|
+
platform_lead_approval: bool = False
|
1824
|
+
skip_eni_gate: bool = False # DANGEROUS: Only for emergency scenarios
|
1825
|
+
|
1826
|
+
def __post_init__(self):
|
1827
|
+
"""Validation of cleanup configuration."""
|
1828
|
+
if self.force_cleanup and not self.approval_token:
|
1829
|
+
raise ValueError("Force cleanup requires approval token")
|
1830
|
+
|
1831
|
+
if self.skip_eni_gate and not self.platform_lead_approval:
|
1832
|
+
raise ValueError("Skipping ENI gate requires Platform Lead approval")
|
1833
|
+
|
1834
|
+
|
1835
|
+
@dataclass
|
1836
|
+
class VPCCleanupPlan:
|
1837
|
+
"""AWSO-5 VPC cleanup execution plan with ordered steps."""
|
1838
|
+
|
1839
|
+
vpc_id: str
|
1840
|
+
cleanup_steps: List[Dict[str, Any]]
|
1841
|
+
estimated_duration_minutes: int
|
1842
|
+
risk_level: str # LOW, MEDIUM, HIGH
|
1843
|
+
requires_approval: bool
|
1844
|
+
|
1845
|
+
# Evidence collection
|
1846
|
+
pre_cleanup_evidence: Dict[str, Any]
|
1847
|
+
plan_hash: str
|
1848
|
+
plan_timestamp: str
|
1849
|
+
|
1850
|
+
@property
|
1851
|
+
def total_steps(self) -> int:
|
1852
|
+
"""Total number of cleanup steps."""
|
1853
|
+
return len(self.cleanup_steps)
|
1854
|
+
|
1855
|
+
@property
|
1856
|
+
def blocking_steps(self) -> int:
|
1857
|
+
"""Number of steps that could cause service disruption."""
|
1858
|
+
return len([step for step in self.cleanup_steps if step.get('risk_level') == 'HIGH'])
|
1859
|
+
|
1860
|
+
|
1861
|
+
class AWSO5VPCCleanupOperation(BaseOperation):
|
1862
|
+
"""
|
1863
|
+
AWSO-5 VPC Cleanup Operation - Enterprise Security Framework.
|
1864
|
+
|
1865
|
+
Implements comprehensive VPC cleanup following the AWSO-5 12-step framework
|
1866
|
+
with enterprise safety controls, evidence collection, and approval workflows.
|
1867
|
+
|
1868
|
+
**Strategic Alignment**:
|
1869
|
+
- Security posture enhancement through default VPC elimination
|
1870
|
+
- Attack surface reduction via systematic dependency cleanup
|
1871
|
+
- CIS Benchmark compliance through infrastructure hygiene
|
1872
|
+
- Evidence-based validation with SHA256-verified audit trails
|
1873
|
+
|
1874
|
+
**Safety Controls**:
|
1875
|
+
- ENI gate validation (prevents accidental workload disruption)
|
1876
|
+
- Dry-run first approach with detailed execution plans
|
1877
|
+
- Platform Lead approval for high-risk operations
|
1878
|
+
- Comprehensive rollback procedures
|
1879
|
+
- Real-time monitoring and validation
|
1880
|
+
"""
|
1881
|
+
|
1882
|
+
def __init__(self, context: OperationContext):
|
1883
|
+
"""Initialize AWSO-5 VPC cleanup operation."""
|
1884
|
+
super().__init__(context)
|
1885
|
+
self.operation_type = "AWSO5_VPC_CLEANUP"
|
1886
|
+
|
1887
|
+
# Initialize dependency analyzer
|
1888
|
+
from runbooks.inventory.vpc_dependency_analyzer import VPCDependencyAnalyzer
|
1889
|
+
self.dependency_analyzer = VPCDependencyAnalyzer(
|
1890
|
+
session=self.context.session,
|
1891
|
+
region=self.context.region
|
1892
|
+
)
|
1893
|
+
|
1894
|
+
# Cleanup tracking
|
1895
|
+
self.cleanup_evidence: Dict[str, Any] = {}
|
1896
|
+
self.cleanup_plan: Optional[VPCCleanupPlan] = None
|
1897
|
+
|
1898
|
+
def execute_vpc_cleanup(
|
1899
|
+
self,
|
1900
|
+
config: VPCCleanupConfiguration,
|
1901
|
+
evidence_bundle_path: Optional[str] = None
|
1902
|
+
) -> List[OperationResult]:
|
1903
|
+
"""
|
1904
|
+
Execute AWSO-5 VPC cleanup with comprehensive safety validation.
|
1905
|
+
|
1906
|
+
Args:
|
1907
|
+
config: VPC cleanup configuration
|
1908
|
+
evidence_bundle_path: Optional path to save evidence bundle
|
1909
|
+
|
1910
|
+
Returns:
|
1911
|
+
Operation results with evidence and audit information
|
1912
|
+
"""
|
1913
|
+
results = []
|
1914
|
+
operation_start = datetime.utcnow()
|
1915
|
+
|
1916
|
+
try:
|
1917
|
+
# Phase 1: Pre-cleanup Analysis & Validation
|
1918
|
+
self.rich_console.print_header("AWSO-5 VPC Cleanup Operation", "1.0.0")
|
1919
|
+
self.rich_console.print_info(f"Target VPC: {config.vpc_id}")
|
1920
|
+
self.rich_console.print_info(f"Mode: {'DRY-RUN' if config.dry_run else 'EXECUTE'}")
|
1921
|
+
|
1922
|
+
# Step 1: ENI Gate Validation (Critical Safety Check)
|
1923
|
+
if not config.skip_eni_gate:
|
1924
|
+
self.rich_console.print_info("Step 1: ENI Gate Validation (Critical Safety Check)")
|
1925
|
+
dependency_result = self.dependency_analyzer.analyze_vpc_dependencies(config.vpc_id)
|
1926
|
+
|
1927
|
+
if dependency_result.eni_count > 0:
|
1928
|
+
error_msg = f"ENI Gate FAILED: {dependency_result.eni_count} active ENIs detected"
|
1929
|
+
self.rich_console.print_error(f"❌ {error_msg}")
|
1930
|
+
self.rich_console.print_warning("⚠️ Active ENIs indicate running workloads!")
|
1931
|
+
self.rich_console.print_info("Next Steps:")
|
1932
|
+
self.rich_console.print_info("1. Investigate ENI owners and workload requirements")
|
1933
|
+
self.rich_console.print_info("2. Coordinate with application teams")
|
1934
|
+
self.rich_console.print_info("3. Consider migration vs cleanup options")
|
1935
|
+
|
1936
|
+
result = OperationResult(
|
1937
|
+
operation_id=self.context.operation_id,
|
1938
|
+
operation_type="AWSO5_ENI_GATE_CHECK",
|
1939
|
+
status=OperationStatus.FAILED,
|
1940
|
+
message=error_msg,
|
1941
|
+
details={
|
1942
|
+
'eni_count': dependency_result.eni_count,
|
1943
|
+
'vpc_id': config.vpc_id,
|
1944
|
+
'recommendation': 'INVESTIGATE_REQUIRED',
|
1945
|
+
'dependencies': [dep.__dict__ for dep in dependency_result.dependencies]
|
1946
|
+
}
|
1947
|
+
)
|
1948
|
+
return [result]
|
1949
|
+
|
1950
|
+
self.rich_console.print_success("✅ ENI Gate PASSED - No active ENIs detected")
|
1951
|
+
else:
|
1952
|
+
self.rich_console.print_warning("⚠️ ENI Gate SKIPPED (Platform Lead Approval Required)")
|
1953
|
+
dependency_result = self.dependency_analyzer.analyze_vpc_dependencies(config.vpc_id)
|
1954
|
+
|
1955
|
+
# Step 2: Generate Cleanup Plan
|
1956
|
+
self.rich_console.print_info("Step 2: Generating Comprehensive Cleanup Plan")
|
1957
|
+
self.cleanup_plan = self._generate_cleanup_plan(dependency_result, config)
|
1958
|
+
|
1959
|
+
# Display cleanup plan
|
1960
|
+
self._display_cleanup_plan(self.cleanup_plan)
|
1961
|
+
|
1962
|
+
# Step 3: Risk Assessment & Approval Gate
|
1963
|
+
if self.cleanup_plan.requires_approval and not config.approval_token:
|
1964
|
+
self.rich_console.print_warning("⚠️ This cleanup requires Platform Lead approval")
|
1965
|
+
self.rich_console.print_info("Required approvals:")
|
1966
|
+
if dependency_result.is_default:
|
1967
|
+
self.rich_console.print_info("• Platform Lead (Default VPC deletion)")
|
1968
|
+
if self.cleanup_plan.risk_level == "HIGH":
|
1969
|
+
self.rich_console.print_info("• Additional stakeholder review")
|
1970
|
+
|
1971
|
+
result = OperationResult(
|
1972
|
+
operation_id=self.context.operation_id,
|
1973
|
+
operation_type="AWSO5_APPROVAL_REQUIRED",
|
1974
|
+
status=OperationStatus.PENDING_APPROVAL,
|
1975
|
+
message="Platform Lead approval required for VPC cleanup",
|
1976
|
+
details={
|
1977
|
+
'vpc_id': config.vpc_id,
|
1978
|
+
'risk_level': self.cleanup_plan.risk_level,
|
1979
|
+
'requires_approval_reason': 'Default VPC or High Risk Operation',
|
1980
|
+
'cleanup_plan': self.cleanup_plan.__dict__
|
1981
|
+
}
|
1982
|
+
)
|
1983
|
+
return [result]
|
1984
|
+
|
1985
|
+
# Step 4: Execute Cleanup (Dry-run or Actual)
|
1986
|
+
if config.dry_run:
|
1987
|
+
self.rich_console.print_info("Step 3: DRY-RUN Mode - No actual changes will be made")
|
1988
|
+
result = self._execute_dry_run_cleanup(self.cleanup_plan, config)
|
1989
|
+
else:
|
1990
|
+
self.rich_console.print_info("Step 3: EXECUTING VPC Cleanup")
|
1991
|
+
result = self._execute_actual_cleanup(self.cleanup_plan, config)
|
1992
|
+
|
1993
|
+
# Step 5: Evidence Bundle Generation
|
1994
|
+
if config.evidence_collection:
|
1995
|
+
self.rich_console.print_info("Step 4: Generating Evidence Bundle")
|
1996
|
+
evidence_bundle = self._generate_evidence_bundle(
|
1997
|
+
dependency_result,
|
1998
|
+
self.cleanup_plan,
|
1999
|
+
result,
|
2000
|
+
evidence_bundle_path
|
2001
|
+
)
|
2002
|
+
result.details['evidence_bundle'] = evidence_bundle
|
2003
|
+
|
2004
|
+
results.append(result)
|
2005
|
+
|
2006
|
+
except Exception as e:
|
2007
|
+
error_msg = f"AWSO-5 VPC cleanup failed: {str(e)}"
|
2008
|
+
logger.exception(error_msg)
|
2009
|
+
|
2010
|
+
result = OperationResult(
|
2011
|
+
operation_id=self.context.operation_id,
|
2012
|
+
operation_type="AWSO5_VPC_CLEANUP_ERROR",
|
2013
|
+
status=OperationStatus.FAILED,
|
2014
|
+
message=error_msg,
|
2015
|
+
details={
|
2016
|
+
'vpc_id': config.vpc_id,
|
2017
|
+
'error_type': type(e).__name__,
|
2018
|
+
'operation_duration': (datetime.utcnow() - operation_start).total_seconds()
|
2019
|
+
}
|
2020
|
+
)
|
2021
|
+
results.append(result)
|
2022
|
+
|
2023
|
+
return results
|
2024
|
+
|
2025
|
+
def _generate_cleanup_plan(
|
2026
|
+
self,
|
2027
|
+
dependency_result,
|
2028
|
+
config: VPCCleanupConfiguration
|
2029
|
+
) -> VPCCleanupPlan:
|
2030
|
+
"""Generate comprehensive VPC cleanup plan based on dependency analysis."""
|
2031
|
+
|
2032
|
+
cleanup_steps = []
|
2033
|
+
risk_level = "LOW"
|
2034
|
+
estimated_duration = 5 # Base time in minutes
|
2035
|
+
|
2036
|
+
# Generate cleanup steps based on dependencies
|
2037
|
+
for dependency in dependency_result.dependencies:
|
2038
|
+
if dependency.is_blocking:
|
2039
|
+
step = {
|
2040
|
+
'step_type': 'DEPENDENCY_CLEANUP',
|
2041
|
+
'resource_type': dependency.resource_type,
|
2042
|
+
'resource_id': dependency.resource_id,
|
2043
|
+
'action': dependency.remediation_action,
|
2044
|
+
'risk_level': 'HIGH' if dependency.resource_type in ['LoadBalancer', 'TransitGatewayAttachment'] else 'MEDIUM',
|
2045
|
+
'estimated_minutes': self._estimate_cleanup_time(dependency.resource_type)
|
2046
|
+
}
|
2047
|
+
cleanup_steps.append(step)
|
2048
|
+
estimated_duration += step['estimated_minutes']
|
2049
|
+
|
2050
|
+
if step['risk_level'] == 'HIGH':
|
2051
|
+
risk_level = "HIGH"
|
2052
|
+
elif step['risk_level'] == 'MEDIUM' and risk_level != "HIGH":
|
2053
|
+
risk_level = "MEDIUM"
|
2054
|
+
|
2055
|
+
# Final VPC deletion step
|
2056
|
+
cleanup_steps.append({
|
2057
|
+
'step_type': 'VPC_DELETION',
|
2058
|
+
'resource_type': 'VPC',
|
2059
|
+
'resource_id': config.vpc_id,
|
2060
|
+
'action': 'Delete VPC (final step)',
|
2061
|
+
'risk_level': 'MEDIUM',
|
2062
|
+
'estimated_minutes': 2
|
2063
|
+
})
|
2064
|
+
estimated_duration += 2
|
2065
|
+
|
2066
|
+
# Calculate plan hash for integrity
|
2067
|
+
import hashlib
|
2068
|
+
import json
|
2069
|
+
plan_content = json.dumps(cleanup_steps, sort_keys=True)
|
2070
|
+
plan_hash = hashlib.sha256(plan_content.encode()).hexdigest()
|
2071
|
+
|
2072
|
+
return VPCCleanupPlan(
|
2073
|
+
vpc_id=config.vpc_id,
|
2074
|
+
cleanup_steps=cleanup_steps,
|
2075
|
+
estimated_duration_minutes=estimated_duration,
|
2076
|
+
risk_level=risk_level,
|
2077
|
+
requires_approval=dependency_result.is_default or risk_level == "HIGH",
|
2078
|
+
pre_cleanup_evidence=dependency_result.__dict__,
|
2079
|
+
plan_hash=plan_hash[:16], # Short hash for display
|
2080
|
+
plan_timestamp=datetime.utcnow().isoformat()
|
2081
|
+
)
|
2082
|
+
|
2083
|
+
def _estimate_cleanup_time(self, resource_type: str) -> int:
|
2084
|
+
"""Estimate cleanup time in minutes for different resource types."""
|
2085
|
+
time_estimates = {
|
2086
|
+
'NetworkInterface': 3,
|
2087
|
+
'NatGateway': 5,
|
2088
|
+
'InternetGateway': 2,
|
2089
|
+
'RouteTable': 2,
|
2090
|
+
'VpcEndpoint': 3,
|
2091
|
+
'TransitGatewayAttachment': 10,
|
2092
|
+
'VpcPeeringConnection': 3,
|
2093
|
+
'ResolverEndpoint': 5,
|
2094
|
+
'LoadBalancer': 8,
|
2095
|
+
'SecurityGroup': 2,
|
2096
|
+
'NetworkAcl': 2,
|
2097
|
+
'FlowLog': 1
|
2098
|
+
}
|
2099
|
+
return time_estimates.get(resource_type, 3)
|
2100
|
+
|
2101
|
+
def _display_cleanup_plan(self, plan: VPCCleanupPlan) -> None:
|
2102
|
+
"""Display comprehensive cleanup plan with Rich formatting."""
|
2103
|
+
|
2104
|
+
# Plan Summary
|
2105
|
+
summary_table = create_table(
|
2106
|
+
title="AWSO-5 VPC Cleanup Plan Summary"
|
2107
|
+
)
|
2108
|
+
summary_table.add_column("Metric", style="cyan")
|
2109
|
+
summary_table.add_column("Value", style="green")
|
2110
|
+
|
2111
|
+
summary_table.add_row("VPC ID", plan.vpc_id)
|
2112
|
+
summary_table.add_row("Total Steps", str(plan.total_steps))
|
2113
|
+
summary_table.add_row("Estimated Duration", f"{plan.estimated_duration_minutes} minutes")
|
2114
|
+
summary_table.add_row("Risk Level", plan.risk_level)
|
2115
|
+
summary_table.add_row("Requires Approval", "Yes" if plan.requires_approval else "No")
|
2116
|
+
summary_table.add_row("Plan Hash", plan.plan_hash)
|
2117
|
+
|
2118
|
+
self.rich_console.print("\n")
|
2119
|
+
self.rich_console.print(summary_table)
|
2120
|
+
|
2121
|
+
# Cleanup Steps Detail
|
2122
|
+
if plan.cleanup_steps:
|
2123
|
+
steps_table = create_table(
|
2124
|
+
title="Cleanup Execution Steps"
|
2125
|
+
)
|
2126
|
+
steps_table.add_column("Step", style="cyan")
|
2127
|
+
steps_table.add_column("Resource Type", style="blue")
|
2128
|
+
steps_table.add_column("Resource ID", style="green")
|
2129
|
+
steps_table.add_column("Action", style="yellow")
|
2130
|
+
steps_table.add_column("Risk", style="red")
|
2131
|
+
steps_table.add_column("Est. Time", style="magenta")
|
2132
|
+
|
2133
|
+
for i, step in enumerate(plan.cleanup_steps, 1):
|
2134
|
+
steps_table.add_row(
|
2135
|
+
str(i),
|
2136
|
+
step['resource_type'],
|
2137
|
+
step['resource_id'],
|
2138
|
+
step['action'],
|
2139
|
+
step['risk_level'],
|
2140
|
+
f"{step['estimated_minutes']}min"
|
2141
|
+
)
|
2142
|
+
|
2143
|
+
self.rich_console.print("\n")
|
2144
|
+
self.rich_console.print(steps_table)
|
2145
|
+
|
2146
|
+
# Risk Assessment
|
2147
|
+
if plan.risk_level == "HIGH":
|
2148
|
+
self.rich_console.print_warning("⚠️ HIGH RISK: This cleanup involves critical infrastructure components")
|
2149
|
+
elif plan.risk_level == "MEDIUM":
|
2150
|
+
self.rich_console.print_info("ℹ️ MEDIUM RISK: Standard cleanup with network dependencies")
|
2151
|
+
else:
|
2152
|
+
self.rich_console.print_success("✅ LOW RISK: Straightforward cleanup with minimal dependencies")
|
2153
|
+
|
2154
|
+
def _execute_dry_run_cleanup(
|
2155
|
+
self,
|
2156
|
+
plan: VPCCleanupPlan,
|
2157
|
+
config: VPCCleanupConfiguration
|
2158
|
+
) -> OperationResult:
|
2159
|
+
"""Execute dry-run cleanup showing what would be done."""
|
2160
|
+
|
2161
|
+
self.rich_console.print_success("🔍 DRY-RUN: Simulating cleanup execution")
|
2162
|
+
|
2163
|
+
simulated_results = []
|
2164
|
+
for i, step in enumerate(plan.cleanup_steps, 1):
|
2165
|
+
self.rich_console.print_info(f"Step {i}/{plan.total_steps}: Would {step['action']}")
|
2166
|
+
simulated_results.append({
|
2167
|
+
'step': i,
|
2168
|
+
'resource_type': step['resource_type'],
|
2169
|
+
'resource_id': step['resource_id'],
|
2170
|
+
'action': step['action'],
|
2171
|
+
'simulated_result': 'SUCCESS',
|
2172
|
+
'notes': 'Dry-run simulation - no actual changes made'
|
2173
|
+
})
|
2174
|
+
time.sleep(0.5) # Simulate processing time
|
2175
|
+
|
2176
|
+
self.rich_console.print_success("✅ DRY-RUN completed successfully")
|
2177
|
+
self.rich_console.print_info("Next Steps:")
|
2178
|
+
self.rich_console.print_info("1. Review cleanup plan and risk assessment")
|
2179
|
+
self.rich_console.print_info("2. Obtain required approvals if needed")
|
2180
|
+
self.rich_console.print_info("3. Execute with --dry-run=false when ready")
|
2181
|
+
|
2182
|
+
return OperationResult(
|
2183
|
+
operation_id=self.context.operation_id,
|
2184
|
+
operation_type="AWSO5_DRY_RUN_CLEANUP",
|
2185
|
+
status=OperationStatus.COMPLETED,
|
2186
|
+
message="Dry-run cleanup completed successfully",
|
2187
|
+
details={
|
2188
|
+
'vpc_id': config.vpc_id,
|
2189
|
+
'cleanup_plan': plan.__dict__,
|
2190
|
+
'simulated_results': simulated_results,
|
2191
|
+
'execution_mode': 'DRY_RUN'
|
2192
|
+
}
|
2193
|
+
)
|
2194
|
+
|
2195
|
+
def _execute_actual_cleanup(
|
2196
|
+
self,
|
2197
|
+
plan: VPCCleanupPlan,
|
2198
|
+
config: VPCCleanupConfiguration
|
2199
|
+
) -> OperationResult:
|
2200
|
+
"""Execute actual VPC cleanup with comprehensive error handling."""
|
2201
|
+
|
2202
|
+
self.rich_console.print_warning("⚠️ EXECUTING ACTUAL CLEANUP - This will make real changes!")
|
2203
|
+
|
2204
|
+
execution_results = []
|
2205
|
+
failed_steps = []
|
2206
|
+
|
2207
|
+
try:
|
2208
|
+
for i, step in enumerate(plan.cleanup_steps, 1):
|
2209
|
+
self.rich_console.print_info(f"Step {i}/{plan.total_steps}: {step['action']}")
|
2210
|
+
|
2211
|
+
try:
|
2212
|
+
# Execute cleanup step
|
2213
|
+
step_result = self._execute_cleanup_step(step, config)
|
2214
|
+
execution_results.append(step_result)
|
2215
|
+
|
2216
|
+
if step_result['success']:
|
2217
|
+
self.rich_console.print_success(f"✅ Step {i} completed: {step['resource_id']}")
|
2218
|
+
else:
|
2219
|
+
self.rich_console.print_error(f"❌ Step {i} failed: {step_result['error']}")
|
2220
|
+
failed_steps.append((i, step, step_result['error']))
|
2221
|
+
|
2222
|
+
# Decide whether to continue or abort
|
2223
|
+
if step['risk_level'] == 'HIGH':
|
2224
|
+
self.rich_console.print_error("🛑 ABORTING: High-risk step failed")
|
2225
|
+
break
|
2226
|
+
|
2227
|
+
except Exception as e:
|
2228
|
+
error_msg = f"Step {i} execution error: {str(e)}"
|
2229
|
+
self.rich_console.print_error(f"❌ {error_msg}")
|
2230
|
+
failed_steps.append((i, step, error_msg))
|
2231
|
+
|
2232
|
+
# Critical failure handling
|
2233
|
+
if step['risk_level'] == 'HIGH':
|
2234
|
+
self.rich_console.print_error("🛑 CRITICAL FAILURE: Aborting cleanup")
|
2235
|
+
break
|
2236
|
+
|
2237
|
+
# Final status assessment
|
2238
|
+
if not failed_steps:
|
2239
|
+
status = OperationStatus.COMPLETED
|
2240
|
+
message = "AWSO-5 VPC cleanup completed successfully"
|
2241
|
+
self.rich_console.print_success("✅ All cleanup steps completed successfully")
|
2242
|
+
elif len(failed_steps) < len(plan.cleanup_steps) // 2:
|
2243
|
+
status = OperationStatus.PARTIALLY_COMPLETED
|
2244
|
+
message = f"VPC cleanup partially completed ({len(failed_steps)} steps failed)"
|
2245
|
+
self.rich_console.print_warning(f"⚠️ Partial completion: {len(failed_steps)} steps failed")
|
2246
|
+
else:
|
2247
|
+
status = OperationStatus.FAILED
|
2248
|
+
message = f"VPC cleanup failed ({len(failed_steps)} steps failed)"
|
2249
|
+
self.rich_console.print_error(f"❌ Cleanup failed: {len(failed_steps)} steps failed")
|
2250
|
+
|
2251
|
+
# Post-cleanup validation
|
2252
|
+
self.rich_console.print_info("Performing post-cleanup validation...")
|
2253
|
+
post_validation = self._perform_post_cleanup_validation(config.vpc_id)
|
2254
|
+
|
2255
|
+
return OperationResult(
|
2256
|
+
operation_id=self.context.operation_id,
|
2257
|
+
operation_type="AWSO5_ACTUAL_CLEANUP",
|
2258
|
+
status=status,
|
2259
|
+
message=message,
|
2260
|
+
details={
|
2261
|
+
'vpc_id': config.vpc_id,
|
2262
|
+
'cleanup_plan': plan.__dict__,
|
2263
|
+
'execution_results': execution_results,
|
2264
|
+
'failed_steps': failed_steps,
|
2265
|
+
'post_validation': post_validation,
|
2266
|
+
'execution_mode': 'ACTUAL'
|
2267
|
+
}
|
2268
|
+
)
|
2269
|
+
|
2270
|
+
except Exception as e:
|
2271
|
+
error_msg = f"Critical cleanup error: {str(e)}"
|
2272
|
+
logger.exception(error_msg)
|
2273
|
+
|
2274
|
+
return OperationResult(
|
2275
|
+
operation_id=self.context.operation_id,
|
2276
|
+
operation_type="AWSO5_CLEANUP_ERROR",
|
2277
|
+
status=OperationStatus.FAILED,
|
2278
|
+
message=error_msg,
|
2279
|
+
details={
|
2280
|
+
'vpc_id': config.vpc_id,
|
2281
|
+
'error_type': type(e).__name__,
|
2282
|
+
'partial_results': execution_results
|
2283
|
+
}
|
2284
|
+
)
|
2285
|
+
|
2286
|
+
def _execute_cleanup_step(self, step: Dict[str, Any], config: VPCCleanupConfiguration) -> Dict[str, Any]:
|
2287
|
+
"""Execute a single cleanup step with AWS API calls."""
|
2288
|
+
|
2289
|
+
resource_type = step['resource_type']
|
2290
|
+
resource_id = step['resource_id']
|
2291
|
+
|
2292
|
+
try:
|
2293
|
+
if resource_type == 'NatGateway':
|
2294
|
+
self.context.session.client('ec2').delete_nat_gateway(NatGatewayId=resource_id)
|
2295
|
+
|
2296
|
+
elif resource_type == 'InternetGateway':
|
2297
|
+
ec2 = self.context.session.client('ec2')
|
2298
|
+
# First detach, then delete
|
2299
|
+
ec2.detach_internet_gateway(InternetGatewayId=resource_id, VpcId=config.vpc_id)
|
2300
|
+
ec2.delete_internet_gateway(InternetGatewayId=resource_id)
|
2301
|
+
|
2302
|
+
elif resource_type == 'RouteTable':
|
2303
|
+
ec2 = self.context.session.client('ec2')
|
2304
|
+
# Disassociate first, then delete
|
2305
|
+
route_tables = ec2.describe_route_tables(RouteTableIds=[resource_id])['RouteTables']
|
2306
|
+
for rt in route_tables:
|
2307
|
+
for assoc in rt.get('Associations', []):
|
2308
|
+
if not assoc.get('Main'):
|
2309
|
+
ec2.disassociate_route_table(AssociationId=assoc['RouteTableAssociationId'])
|
2310
|
+
ec2.delete_route_table(RouteTableId=resource_id)
|
2311
|
+
|
2312
|
+
elif resource_type == 'VpcEndpoint':
|
2313
|
+
self.context.session.client('ec2').delete_vpc_endpoints(VpcEndpointIds=[resource_id])
|
2314
|
+
|
2315
|
+
elif resource_type == 'TransitGatewayAttachment':
|
2316
|
+
self.context.session.client('ec2').delete_transit_gateway_vpc_attachment(
|
2317
|
+
TransitGatewayAttachmentId=resource_id
|
2318
|
+
)
|
2319
|
+
|
2320
|
+
elif resource_type == 'VpcPeeringConnection':
|
2321
|
+
self.context.session.client('ec2').delete_vpc_peering_connection(
|
2322
|
+
VpcPeeringConnectionId=resource_id
|
2323
|
+
)
|
2324
|
+
|
2325
|
+
elif resource_type == 'LoadBalancer':
|
2326
|
+
self.context.session.client('elbv2').delete_load_balancer(LoadBalancerArn=resource_id)
|
2327
|
+
|
2328
|
+
elif resource_type == 'SecurityGroup':
|
2329
|
+
self.context.session.client('ec2').delete_security_group(GroupId=resource_id)
|
2330
|
+
|
2331
|
+
elif resource_type == 'NetworkAcl':
|
2332
|
+
self.context.session.client('ec2').delete_network_acl(NetworkAclId=resource_id)
|
2333
|
+
|
2334
|
+
elif resource_type == 'FlowLog':
|
2335
|
+
self.context.session.client('ec2').delete_flow_logs(FlowLogIds=[resource_id])
|
2336
|
+
|
2337
|
+
elif resource_type == 'VPC':
|
2338
|
+
# Final VPC deletion
|
2339
|
+
self.context.session.client('ec2').delete_vpc(VpcId=resource_id)
|
2340
|
+
|
2341
|
+
else:
|
2342
|
+
return {
|
2343
|
+
'success': False,
|
2344
|
+
'resource_type': resource_type,
|
2345
|
+
'resource_id': resource_id,
|
2346
|
+
'error': f'Unknown resource type: {resource_type}'
|
2347
|
+
}
|
2348
|
+
|
2349
|
+
return {
|
2350
|
+
'success': True,
|
2351
|
+
'resource_type': resource_type,
|
2352
|
+
'resource_id': resource_id,
|
2353
|
+
'timestamp': datetime.utcnow().isoformat()
|
2354
|
+
}
|
2355
|
+
|
2356
|
+
except ClientError as e:
|
2357
|
+
return {
|
2358
|
+
'success': False,
|
2359
|
+
'resource_type': resource_type,
|
2360
|
+
'resource_id': resource_id,
|
2361
|
+
'error': f'AWS API Error: {e.response["Error"]["Code"]} - {e.response["Error"]["Message"]}'
|
2362
|
+
}
|
2363
|
+
|
2364
|
+
def _perform_post_cleanup_validation(self, vpc_id: str) -> Dict[str, Any]:
|
2365
|
+
"""Perform post-cleanup validation to ensure VPC and dependencies are removed."""
|
2366
|
+
|
2367
|
+
validation_results = {
|
2368
|
+
'vpc_exists': False,
|
2369
|
+
'dependencies_remaining': [],
|
2370
|
+
'validation_timestamp': datetime.utcnow().isoformat(),
|
2371
|
+
'validation_passed': False
|
2372
|
+
}
|
2373
|
+
|
2374
|
+
try:
|
2375
|
+
# Check if VPC still exists
|
2376
|
+
ec2 = self.context.session.client('ec2')
|
2377
|
+
try:
|
2378
|
+
response = ec2.describe_vpcs(VpcIds=[vpc_id])
|
2379
|
+
if response['Vpcs']:
|
2380
|
+
validation_results['vpc_exists'] = True
|
2381
|
+
self.rich_console.print_warning(f"⚠️ VPC {vpc_id} still exists")
|
2382
|
+
except ClientError as e:
|
2383
|
+
if 'InvalidVpcID.NotFound' in str(e):
|
2384
|
+
validation_results['vpc_exists'] = False
|
2385
|
+
self.rich_console.print_success(f"✅ VPC {vpc_id} successfully deleted")
|
2386
|
+
else:
|
2387
|
+
raise
|
2388
|
+
|
2389
|
+
# Check for remaining dependencies
|
2390
|
+
if not validation_results['vpc_exists']:
|
2391
|
+
validation_results['validation_passed'] = True
|
2392
|
+
self.rich_console.print_success("✅ Post-cleanup validation PASSED")
|
2393
|
+
else:
|
2394
|
+
# Re-run dependency analysis
|
2395
|
+
dependency_result = self.dependency_analyzer.analyze_vpc_dependencies(vpc_id)
|
2396
|
+
validation_results['dependencies_remaining'] = [
|
2397
|
+
dep.__dict__ for dep in dependency_result.dependencies
|
2398
|
+
]
|
2399
|
+
validation_results['validation_passed'] = len(dependency_result.dependencies) == 0
|
2400
|
+
|
2401
|
+
if validation_results['validation_passed']:
|
2402
|
+
self.rich_console.print_success("✅ Post-cleanup validation PASSED - No dependencies remain")
|
2403
|
+
else:
|
2404
|
+
self.rich_console.print_warning(f"⚠️ {len(dependency_result.dependencies)} dependencies still exist")
|
2405
|
+
|
2406
|
+
except Exception as e:
|
2407
|
+
validation_results['error'] = str(e)
|
2408
|
+
validation_results['validation_passed'] = False
|
2409
|
+
self.rich_console.print_error(f"❌ Post-cleanup validation failed: {str(e)}")
|
2410
|
+
|
2411
|
+
return validation_results
|
2412
|
+
|
2413
|
+
def _generate_evidence_bundle(
|
2414
|
+
self,
|
2415
|
+
dependency_result,
|
2416
|
+
cleanup_plan: VPCCleanupPlan,
|
2417
|
+
execution_result: OperationResult,
|
2418
|
+
evidence_bundle_path: Optional[str] = None
|
2419
|
+
) -> Dict[str, Any]:
|
2420
|
+
"""Generate comprehensive evidence bundle for AWSO-5 compliance."""
|
2421
|
+
|
2422
|
+
evidence_bundle = {
|
2423
|
+
'metadata': {
|
2424
|
+
'framework': 'AWSO-5',
|
2425
|
+
'version': '1.0.0',
|
2426
|
+
'vpc_id': cleanup_plan.vpc_id,
|
2427
|
+
'timestamp': datetime.utcnow().isoformat(),
|
2428
|
+
'analyst': 'AWSO5VPCCleanupOperation',
|
2429
|
+
'operation_id': self.context.operation_id
|
2430
|
+
},
|
2431
|
+
'pre_cleanup_analysis': dependency_result.__dict__,
|
2432
|
+
'cleanup_plan': cleanup_plan.__dict__,
|
2433
|
+
'execution_results': execution_result.details,
|
2434
|
+
'evidence_artifacts': [],
|
2435
|
+
'compliance_validation': {
|
2436
|
+
'cis_benchmark_improvement': dependency_result.is_default,
|
2437
|
+
'attack_surface_reduction': True if execution_result.status == OperationStatus.COMPLETED else False,
|
2438
|
+
'security_posture_enhancement': len(dependency_result.dependencies) == 0
|
2439
|
+
}
|
2440
|
+
}
|
2441
|
+
|
2442
|
+
# Calculate evidence bundle hash
|
2443
|
+
import hashlib
|
2444
|
+
import json
|
2445
|
+
bundle_content = json.dumps(evidence_bundle, sort_keys=True, default=str)
|
2446
|
+
evidence_bundle['bundle_hash'] = hashlib.sha256(bundle_content.encode()).hexdigest()
|
2447
|
+
|
2448
|
+
# Save evidence bundle if path provided
|
2449
|
+
if evidence_bundle_path:
|
2450
|
+
with open(evidence_bundle_path, 'w') as f:
|
2451
|
+
json.dump(evidence_bundle, f, indent=2, default=str)
|
2452
|
+
self.rich_console.print_success(f"Evidence bundle saved: {evidence_bundle_path}")
|
2453
|
+
|
2454
|
+
self.rich_console.print_success(f"Evidence bundle generated with hash: {evidence_bundle['bundle_hash'][:16]}...")
|
2455
|
+
|
2456
|
+
return evidence_bundle
|
2457
|
+
|
2458
|
+
|
2459
|
+
def execute_vpc_cleanup(
|
2460
|
+
vpc_id: str,
|
2461
|
+
profile: Optional[str] = None,
|
2462
|
+
region: str = "us-east-1",
|
2463
|
+
dry_run: bool = True,
|
2464
|
+
approval_token: Optional[str] = None,
|
2465
|
+
evidence_bundle_path: Optional[str] = None
|
2466
|
+
) -> List[OperationResult]:
|
2467
|
+
"""
|
2468
|
+
CLI wrapper for AWSO-5 VPC cleanup operation.
|
2469
|
+
|
2470
|
+
Args:
|
2471
|
+
vpc_id: AWS VPC identifier to cleanup
|
2472
|
+
profile: AWS profile name
|
2473
|
+
region: AWS region
|
2474
|
+
dry_run: Execute in dry-run mode (default: True)
|
2475
|
+
approval_token: Platform lead approval token
|
2476
|
+
evidence_bundle_path: Path to save evidence bundle
|
2477
|
+
|
2478
|
+
Returns:
|
2479
|
+
Operation results with comprehensive cleanup information
|
2480
|
+
"""
|
2481
|
+
session = boto3.Session(profile_name=profile) if profile else boto3.Session()
|
2482
|
+
|
2483
|
+
# Create operation context
|
2484
|
+
context = OperationContext(
|
2485
|
+
operation_id=f"awso5-cleanup-{vpc_id}-{int(datetime.utcnow().timestamp())}",
|
2486
|
+
region=region,
|
2487
|
+
session=session,
|
2488
|
+
dry_run=dry_run
|
2489
|
+
)
|
2490
|
+
|
2491
|
+
# Create cleanup configuration
|
2492
|
+
config = VPCCleanupConfiguration(
|
2493
|
+
vpc_id=vpc_id,
|
2494
|
+
dry_run=dry_run,
|
2495
|
+
approval_token=approval_token,
|
2496
|
+
evidence_collection=True
|
2497
|
+
)
|
2498
|
+
|
2499
|
+
# Execute cleanup operation
|
2500
|
+
cleanup_operation = AWSO5VPCCleanupOperation(context)
|
2501
|
+
return cleanup_operation.execute_vpc_cleanup(config, evidence_bundle_path)
|