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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -38,12 +38,13 @@ from pathlib import Path
38
38
  from typing import Any, Dict, List, Optional, Tuple, Union
39
39
 
40
40
  import boto3
41
- from rich.console import Console
42
- from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
41
+ from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn, TimeElapsedColumn
42
+ from runbooks.common.rich_utils import Progress
43
43
  from rich.table import Table
44
44
 
45
45
  from ..common.profile_utils import get_profile_for_operation, resolve_profile_for_operation_silent
46
46
  from ..common.rich_utils import (
47
+ Console,
47
48
  console as rich_console,
48
49
  create_table,
49
50
  format_cost,
@@ -59,12 +60,12 @@ from .core.collector import InventoryCollector
59
60
  class UnifiedValidationEngine:
60
61
  """
61
62
  Enterprise Unified Validation Engine for 3-way AWS resource validation.
62
-
63
+
63
64
  Integrates all validation sources into a single workflow:
64
65
  - runbooks APIs (inventory collection methods)
65
66
  - MCP servers (real server integration from .mcp.json)
66
67
  - Terraform drift detection (Infrastructure as Code alignment)
67
-
68
+
68
69
  Provides comprehensive accuracy validation ≥99.5% with enterprise reporting.
69
70
  """
70
71
 
@@ -79,7 +80,7 @@ class UnifiedValidationEngine:
79
80
  ):
80
81
  """
81
82
  Initialize unified validation engine with enterprise configuration.
82
-
83
+
83
84
  Args:
84
85
  user_profile: User-specified profile (--profile parameter) - takes priority
85
86
  console: Rich console for output
@@ -92,10 +93,10 @@ class UnifiedValidationEngine:
92
93
  self.console = console or rich_console
93
94
  self.validation_threshold = validation_threshold
94
95
  self.performance_target = performance_target_seconds
95
-
96
+
96
97
  # Enterprise profile management
97
98
  self.enterprise_profiles = self._resolve_enterprise_profiles()
98
-
99
+
99
100
  # Validation components
100
101
  self.mcp_validator = EnhancedMCPValidator(
101
102
  user_profile=user_profile,
@@ -103,31 +104,31 @@ class UnifiedValidationEngine:
103
104
  mcp_config_path=mcp_config_path,
104
105
  terraform_directory=terraform_directory,
105
106
  )
106
-
107
+
107
108
  # Initialize inventory collector for runbooks API validation
108
109
  self.inventory_collector = InventoryCollector(
109
110
  profile=self.enterprise_profiles["operational"],
110
- region="us-east-1" # Default region for global services
111
+ region="us-east-1", # Default region for global services
111
112
  )
112
-
113
+
113
114
  # Validation cache for performance optimization
114
115
  self.validation_cache = {}
115
116
  self.cache_ttl = 300 # 5 minutes
116
-
117
+
117
118
  # Supported resource types for unified validation
118
119
  self.supported_resources = {
119
- 'ec2': 'EC2 Instances',
120
- 's3': 'S3 Buckets',
121
- 'rds': 'RDS Instances',
122
- 'lambda': 'Lambda Functions',
123
- 'vpc': 'VPCs',
124
- 'iam': 'IAM Roles',
125
- 'cloudformation': 'CloudFormation Stacks',
126
- 'elbv2': 'Load Balancers',
127
- 'route53': 'Route53 Hosted Zones',
128
- 'sns': 'SNS Topics',
129
- 'eni': 'Network Interfaces',
130
- 'ebs': 'EBS Volumes',
120
+ "ec2": "EC2 Instances",
121
+ "s3": "S3 Buckets",
122
+ "rds": "RDS Instances",
123
+ "lambda": "Lambda Functions",
124
+ "vpc": "VPCs",
125
+ "iam": "IAM Roles",
126
+ "cloudformation": "CloudFormation Stacks",
127
+ "elbv2": "Load Balancers",
128
+ "route53": "Route53 Hosted Zones",
129
+ "sns": "SNS Topics",
130
+ "eni": "Network Interfaces",
131
+ "ebs": "EBS Volumes",
131
132
  }
132
133
 
133
134
  def _resolve_enterprise_profiles(self) -> Dict[str, str]:
@@ -150,7 +151,7 @@ class UnifiedValidationEngine:
150
151
  ) -> Dict[str, Any]:
151
152
  """
152
153
  Run comprehensive unified validation across all sources.
153
-
154
+
154
155
  Args:
155
156
  resource_types: List of resource types to validate
156
157
  accounts: List of account IDs to analyze
@@ -159,12 +160,12 @@ class UnifiedValidationEngine:
159
160
  enable_mcp_servers: Enable MCP server integration
160
161
  export_formats: List of export formats ('json', 'csv', 'pdf', 'markdown')
161
162
  output_directory: Directory for validation evidence exports
162
-
163
+
163
164
  Returns:
164
165
  Comprehensive validation results with 3-way cross-validation
165
166
  """
166
167
  validation_start_time = time.time()
167
-
168
+
168
169
  validation_results = {
169
170
  "validation_timestamp": datetime.now().isoformat(),
170
171
  "validation_method": "unified_3way_cross_validation",
@@ -187,8 +188,10 @@ class UnifiedValidationEngine:
187
188
  }
188
189
 
189
190
  self.console.print(f"[blue]🔍 Starting Unified 3-Way Validation Engine[/blue]")
190
- self.console.print(f"[dim]Target: ≥{self.validation_threshold}% accuracy | Performance: <{self.performance_target}s[/dim]")
191
-
191
+ self.console.print(
192
+ f"[dim]Target: ≥{self.validation_threshold}% accuracy | Performance: <{self.performance_target}s[/dim]"
193
+ )
194
+
192
195
  # Display validation sources
193
196
  sources = []
194
197
  if validation_results["validation_sources"]["runbooks_apis"]:
@@ -197,52 +200,46 @@ class UnifiedValidationEngine:
197
200
  sources.append("MCP Servers")
198
201
  if validation_results["validation_sources"]["terraform_drift"]:
199
202
  sources.append("Terraform IaC")
200
-
203
+
201
204
  self.console.print(f"[dim cyan]🔗 Validation Sources: {', '.join(sources)}[/]")
202
205
 
203
206
  try:
204
207
  # Step 1: Collect baseline inventory from runbooks APIs
205
- runbooks_inventory = await self._collect_runbooks_inventory(
206
- resource_types, accounts, regions
207
- )
208
-
208
+ runbooks_inventory = await self._collect_runbooks_inventory(resource_types, accounts, regions)
209
+
209
210
  # Step 2: Run 3-way cross-validation
210
211
  cross_validation_results = await self._execute_3way_validation(
211
212
  runbooks_inventory, enable_terraform_drift, enable_mcp_servers
212
213
  )
213
-
214
+
214
215
  # Step 3: Generate comprehensive analysis
215
- unified_analysis = self._generate_unified_analysis(
216
- runbooks_inventory, cross_validation_results
217
- )
218
-
216
+ unified_analysis = self._generate_unified_analysis(runbooks_inventory, cross_validation_results)
217
+
219
218
  # Step 4: Calculate performance metrics
220
219
  total_execution_time = time.time() - validation_start_time
221
220
  validation_results["performance_metrics"]["total_execution_time"] = total_execution_time
222
- validation_results["performance_metrics"]["performance_achieved"] = total_execution_time <= self.performance_target
223
-
221
+ validation_results["performance_metrics"]["performance_achieved"] = (
222
+ total_execution_time <= self.performance_target
223
+ )
224
+
224
225
  # Step 5: Populate results
225
226
  validation_results.update(unified_analysis)
226
-
227
+
227
228
  # Step 6: Generate recommendations
228
- validation_results["recommendations"] = self._generate_actionable_recommendations(
229
- unified_analysis
230
- )
231
-
229
+ validation_results["recommendations"] = self._generate_actionable_recommendations(unified_analysis)
230
+
232
231
  # Step 7: Display results
233
232
  self._display_unified_validation_results(validation_results)
234
-
233
+
235
234
  # Step 8: Export evidence if requested
236
235
  if export_formats:
237
- await self._export_validation_evidence(
238
- validation_results, export_formats, output_directory
239
- )
240
-
236
+ await self._export_validation_evidence(validation_results, export_formats, output_directory)
237
+
241
238
  except Exception as e:
242
239
  print_error(f"Unified validation failed: {str(e)}")
243
240
  validation_results["error"] = str(e)
244
241
  validation_results["passed_validation"] = False
245
-
242
+
246
243
  return validation_results
247
244
 
248
245
  async def _collect_runbooks_inventory(
@@ -253,11 +250,11 @@ class UnifiedValidationEngine:
253
250
  ) -> Dict[str, Any]:
254
251
  """Collect baseline inventory using runbooks APIs."""
255
252
  self.console.print(f"[yellow]📊 Step 1/3: Collecting runbooks inventory baseline[/yellow]")
256
-
253
+
257
254
  try:
258
255
  # Use the existing inventory collector
259
256
  inventory_results = {}
260
-
257
+
261
258
  # Get current account ID
262
259
  if not accounts:
263
260
  try:
@@ -267,7 +264,7 @@ class UnifiedValidationEngine:
267
264
  accounts = [current_account]
268
265
  except Exception:
269
266
  accounts = ["unknown"]
270
-
267
+
271
268
  for account_id in accounts:
272
269
  account_inventory = {
273
270
  "account_id": account_id,
@@ -276,7 +273,7 @@ class UnifiedValidationEngine:
276
273
  "collection_method": "runbooks_inventory_apis",
277
274
  "timestamp": datetime.now().isoformat(),
278
275
  }
279
-
276
+
280
277
  # Collect actual resource counts using runbooks inventory collector
281
278
  for resource_type in resource_types or list(self.supported_resources.keys()):
282
279
  try:
@@ -286,14 +283,18 @@ class UnifiedValidationEngine:
286
283
  )
287
284
  account_inventory["resource_counts"][resource_type] = resource_count
288
285
  except Exception as e:
289
- self.console.log(f"[yellow]Warning: Failed to collect {resource_type} for account {account_id}: {str(e)[:30]}[/]")
286
+ self.console.log(
287
+ f"[yellow]Warning: Failed to collect {resource_type} for account {account_id}: {str(e)[:30]}[/]"
288
+ )
290
289
  account_inventory["resource_counts"][resource_type] = 0
291
-
290
+
292
291
  inventory_results[account_id] = account_inventory
293
-
294
- print_info(f"✅ Runbooks inventory collected: {len(accounts)} accounts, {len(resource_types or [])} resource types")
292
+
293
+ print_info(
294
+ f"✅ Runbooks inventory collected: {len(accounts)} accounts, {len(resource_types or [])} resource types"
295
+ )
295
296
  return inventory_results
296
-
297
+
297
298
  except Exception as e:
298
299
  print_warning(f"Runbooks inventory collection encountered issues: {str(e)[:50]}")
299
300
  return {
@@ -310,13 +311,13 @@ class UnifiedValidationEngine:
310
311
  ) -> Dict[str, Any]:
311
312
  """Execute comprehensive 3-way cross-validation."""
312
313
  self.console.print(f"[yellow]🔍 Step 2/3: Executing 3-way cross-validation[/yellow]")
313
-
314
+
314
315
  validation_results = {
315
316
  "runbooks_validation": runbooks_inventory,
316
317
  "mcp_validation": None,
317
318
  "terraform_drift_validation": None,
318
319
  }
319
-
320
+
320
321
  # Execute validations in parallel for performance
321
322
  with Progress(
322
323
  SpinnerColumn(),
@@ -326,61 +327,57 @@ class UnifiedValidationEngine:
326
327
  TimeElapsedColumn(),
327
328
  console=self.console,
328
329
  ) as progress:
329
-
330
330
  # Parallel validation tasks
331
331
  tasks = []
332
-
332
+
333
333
  # MCP Server validation
334
334
  if enable_mcp_servers:
335
335
  task_mcp = progress.add_task("MCP server validation...", total=1)
336
336
  tasks.append(("mcp", task_mcp))
337
-
337
+
338
338
  # Terraform drift detection
339
339
  if enable_terraform_drift:
340
340
  task_tf = progress.add_task("Terraform drift detection...", total=1)
341
341
  tasks.append(("terraform", task_tf))
342
-
342
+
343
343
  # Execute validations
344
344
  with ThreadPoolExecutor(max_workers=2) as executor:
345
345
  futures = {}
346
-
346
+
347
347
  if enable_mcp_servers:
348
348
  future_mcp = executor.submit(self._run_mcp_validation, runbooks_inventory)
349
349
  futures["mcp"] = future_mcp
350
-
350
+
351
351
  if enable_terraform_drift:
352
352
  future_tf = executor.submit(self._run_terraform_drift_validation, runbooks_inventory)
353
353
  futures["terraform"] = future_tf
354
-
354
+
355
355
  # Collect results
356
356
  for validation_type, future in futures.items():
357
357
  try:
358
358
  result = future.result(timeout=30) # 30 second timeout per validation
359
359
  validation_results[f"{validation_type}_validation"] = result
360
-
360
+
361
361
  # Update progress
362
362
  for task_type, task_id in tasks:
363
363
  if task_type == validation_type:
364
364
  progress.advance(task_id)
365
365
  break
366
-
366
+
367
367
  except Exception as e:
368
368
  print_warning(f"{validation_type} validation failed: {str(e)[:40]}")
369
369
  validation_results[f"{validation_type}_validation"] = {
370
370
  "error": str(e),
371
371
  "validation_status": "FAILED",
372
372
  }
373
-
373
+
374
374
  print_info("✅ 3-way cross-validation completed")
375
375
  return validation_results
376
376
 
377
377
  def _run_mcp_validation(self, runbooks_inventory: Dict[str, Any]) -> Dict[str, Any]:
378
378
  """Run MCP server validation (synchronous wrapper)."""
379
379
  try:
380
- return validate_inventory_with_mcp_servers(
381
- runbooks_inventory,
382
- user_profile=self.user_profile
383
- )
380
+ return validate_inventory_with_mcp_servers(runbooks_inventory, user_profile=self.user_profile)
384
381
  except Exception as e:
385
382
  return {
386
383
  "error": str(e),
@@ -388,7 +385,6 @@ class UnifiedValidationEngine:
388
385
  "timestamp": datetime.now().isoformat(),
389
386
  }
390
387
 
391
-
392
388
  def _run_terraform_drift_validation(self, runbooks_inventory: Dict[str, Any]) -> Dict[str, Any]:
393
389
  """Run terraform drift detection validation."""
394
390
  try:
@@ -398,33 +394,33 @@ class UnifiedValidationEngine:
398
394
  "terraform_integration_enabled": True,
399
395
  "drift_analysis": {},
400
396
  }
401
-
397
+
402
398
  # Use MCP validator's terraform capabilities
403
399
  terraform_data = self.mcp_validator._get_terraform_declared_resources()
404
-
400
+
405
401
  if terraform_data.get("files_parsed", 0) > 0:
406
402
  terraform_validation["terraform_configuration_found"] = True
407
403
  terraform_validation["files_parsed"] = terraform_data["files_parsed"]
408
404
  terraform_validation["declared_resources"] = terraform_data["declared_resources"]
409
-
405
+
410
406
  # Calculate drift for each account
411
407
  for account_id, account_data in runbooks_inventory.items():
412
408
  if account_id == "error":
413
409
  continue
414
-
410
+
415
411
  drift_analysis = {
416
412
  "account_id": account_id,
417
413
  "drift_detected": False,
418
414
  "resource_drift": {},
419
415
  }
420
-
416
+
421
417
  runbooks_counts = account_data.get("resource_counts", {})
422
418
  terraform_counts = terraform_data["declared_resources"]
423
-
419
+
424
420
  for resource_type in self.supported_resources.keys():
425
421
  runbooks_count = runbooks_counts.get(resource_type, 0)
426
422
  terraform_count = terraform_counts.get(resource_type, 0)
427
-
423
+
428
424
  if runbooks_count != terraform_count:
429
425
  drift_analysis["drift_detected"] = True
430
426
  drift_analysis["resource_drift"][resource_type] = {
@@ -432,14 +428,16 @@ class UnifiedValidationEngine:
432
428
  "terraform_declared": terraform_count,
433
429
  "drift_amount": abs(runbooks_count - terraform_count),
434
430
  }
435
-
431
+
436
432
  terraform_validation["drift_analysis"][account_id] = drift_analysis
437
433
  else:
438
434
  terraform_validation["terraform_configuration_found"] = False
439
- terraform_validation["message"] = "No terraform configuration found - consider implementing Infrastructure as Code"
440
-
435
+ terraform_validation["message"] = (
436
+ "No terraform configuration found - consider implementing Infrastructure as Code"
437
+ )
438
+
441
439
  return terraform_validation
442
-
440
+
443
441
  except Exception as e:
444
442
  return {
445
443
  "error": str(e),
@@ -454,7 +452,7 @@ class UnifiedValidationEngine:
454
452
  ) -> Dict[str, Any]:
455
453
  """Generate comprehensive unified analysis from all validation sources."""
456
454
  self.console.print(f"[yellow]📈 Step 3/3: Generating unified analysis[/yellow]")
457
-
455
+
458
456
  unified_analysis = {
459
457
  "overall_accuracy": 0.0,
460
458
  "passed_validation": False,
@@ -467,18 +465,19 @@ class UnifiedValidationEngine:
467
465
  "resource_accuracy_breakdown": {},
468
466
  "account_analysis": {},
469
467
  }
470
-
468
+
471
469
  # Analyze results from each validation source
472
470
  validation_sources = {
473
471
  "runbooks": cross_validation_results.get("runbooks_validation", {}),
474
472
  "mcp": cross_validation_results.get("mcp_validation", {}),
475
473
  "terraform": cross_validation_results.get("terraform_drift_validation", {}),
476
474
  }
477
-
478
- successful_sources = sum(1 for source_data in validation_sources.values()
479
- if source_data and not source_data.get("error"))
475
+
476
+ successful_sources = sum(
477
+ 1 for source_data in validation_sources.values() if source_data and not source_data.get("error")
478
+ )
480
479
  unified_analysis["validation_summary"]["validation_sources_successful"] = successful_sources
481
-
480
+
482
481
  # Analyze each account
483
482
  accounts_to_analyze = set()
484
483
  for source_data in validation_sources.values():
@@ -490,34 +489,30 @@ class UnifiedValidationEngine:
490
489
  for key in source_data.keys():
491
490
  if key not in ["error", "timestamp", "validation_method"]:
492
491
  accounts_to_analyze.add(key)
493
-
492
+
494
493
  accounts_to_analyze.discard("error")
495
494
  unified_analysis["validation_summary"]["total_accounts_analyzed"] = len(accounts_to_analyze)
496
-
495
+
497
496
  # Resource-level analysis
498
497
  total_accuracy = 0.0
499
498
  account_count = 0
500
-
499
+
501
500
  for account_id in accounts_to_analyze:
502
- account_analysis = self._analyze_account_across_sources(
503
- account_id, validation_sources
504
- )
501
+ account_analysis = self._analyze_account_across_sources(account_id, validation_sources)
505
502
  unified_analysis["account_analysis"][account_id] = account_analysis
506
-
503
+
507
504
  if account_analysis.get("overall_accuracy", 0) > 0:
508
505
  total_accuracy += account_analysis["overall_accuracy"]
509
506
  account_count += 1
510
-
507
+
511
508
  if account_count > 0:
512
509
  unified_analysis["overall_accuracy"] = total_accuracy / account_count
513
510
  unified_analysis["passed_validation"] = unified_analysis["overall_accuracy"] >= self.validation_threshold
514
-
511
+
515
512
  print_info("✅ Unified analysis completed")
516
513
  return unified_analysis
517
514
 
518
- def _analyze_account_across_sources(
519
- self, account_id: str, validation_sources: Dict[str, Any]
520
- ) -> Dict[str, Any]:
515
+ def _analyze_account_across_sources(self, account_id: str, validation_sources: Dict[str, Any]) -> Dict[str, Any]:
521
516
  """Analyze a single account across all validation sources."""
522
517
  account_analysis = {
523
518
  "account_id": account_id,
@@ -526,21 +521,21 @@ class UnifiedValidationEngine:
526
521
  "drift_detected": False,
527
522
  "sources_with_data": 0,
528
523
  }
529
-
524
+
530
525
  # Collect resource counts from all sources
531
526
  resource_counts = {
532
527
  "runbooks": {},
533
528
  "mcp": {},
534
529
  "terraform": {},
535
530
  }
536
-
531
+
537
532
  # Extract runbooks data
538
533
  runbooks_data = validation_sources.get("runbooks", {})
539
534
  if account_id in runbooks_data:
540
535
  resource_counts["runbooks"] = runbooks_data[account_id].get("resource_counts", {})
541
536
  if resource_counts["runbooks"]:
542
537
  account_analysis["sources_with_data"] += 1
543
-
538
+
544
539
  # Extract MCP data
545
540
  mcp_data = validation_sources.get("mcp", {})
546
541
  if "profile_results" in mcp_data:
@@ -551,33 +546,32 @@ class UnifiedValidationEngine:
551
546
  resource_counts["mcp"][resource_type] = validation_data.get("mcp_server_count", 0)
552
547
  if resource_counts["mcp"]:
553
548
  account_analysis["sources_with_data"] += 1
554
-
555
-
549
+
556
550
  # Extract terraform data
557
551
  terraform_data = validation_sources.get("terraform", {})
558
552
  if "declared_resources" in terraform_data:
559
553
  resource_counts["terraform"] = terraform_data["declared_resources"]
560
554
  if resource_counts["terraform"]:
561
555
  account_analysis["sources_with_data"] += 1
562
-
556
+
563
557
  # ENHANCED: Weighted accuracy calculation for enterprise reliability
564
558
  total_weighted_accuracy = 0.0
565
559
  total_weight = 0.0
566
-
560
+
567
561
  # Dynamic resource weighting based on actual discovery for universal compatibility
568
562
  resource_weights = self._calculate_dynamic_resource_weights(resource_counts)
569
-
563
+
570
564
  for resource_type in self.supported_resources.keys():
571
565
  runbooks_count = resource_counts["runbooks"].get(resource_type, 0)
572
566
  mcp_count = resource_counts["mcp"].get(resource_type, 0)
573
567
  terraform_count = resource_counts["terraform"].get(resource_type, 0)
574
-
568
+
575
569
  counts = [runbooks_count, mcp_count, terraform_count]
576
-
570
+
577
571
  # ENHANCED: Weighted validation with intelligent tolerance
578
572
  resource_weight = resource_weights.get(resource_type, 1.0)
579
573
  non_zero_counts = [c for c in counts if c > 0]
580
-
574
+
581
575
  if not non_zero_counts:
582
576
  # All sources report zero - perfect alignment
583
577
  accuracy = 100.0
@@ -589,7 +583,7 @@ class UnifiedValidationEngine:
589
583
  else:
590
584
  max_count = max(counts)
591
585
  min_count = min(counts)
592
-
586
+
593
587
  if max_count == 0:
594
588
  # All zero - perfect alignment
595
589
  accuracy = 100.0
@@ -597,7 +591,7 @@ class UnifiedValidationEngine:
597
591
  else:
598
592
  # ENHANCED: Adaptive tolerance based on resource count
599
593
  base_variance = abs(max_count - min_count) / max_count * 100
600
-
594
+
601
595
  # Adaptive tolerance: smaller counts get more tolerance
602
596
  if max_count <= 5:
603
597
  tolerance_threshold = 50.0 # High tolerance for small counts
@@ -606,8 +600,8 @@ class UnifiedValidationEngine:
606
600
  elif max_count <= 100:
607
601
  tolerance_threshold = 10.0 # Standard tolerance
608
602
  else:
609
- tolerance_threshold = 5.0 # Strict tolerance for large counts
610
-
603
+ tolerance_threshold = 5.0 # Strict tolerance for large counts
604
+
611
605
  if base_variance <= tolerance_threshold:
612
606
  accuracy = 100.0
613
607
  variance = base_variance
@@ -616,7 +610,7 @@ class UnifiedValidationEngine:
616
610
  penalty_factor = min((base_variance - tolerance_threshold) / 2.0, 50.0)
617
611
  accuracy = max(50.0, 100.0 - penalty_factor) # Never go below 50%
618
612
  variance = base_variance
619
-
613
+
620
614
  account_analysis["resource_analysis"][resource_type] = {
621
615
  "runbooks_count": runbooks_count,
622
616
  "mcp_count": mcp_count,
@@ -626,18 +620,18 @@ class UnifiedValidationEngine:
626
620
  "sources_with_data": len(non_zero_counts),
627
621
  "resource_weight": resource_weight,
628
622
  }
629
-
623
+
630
624
  # Apply weighting to overall accuracy calculation
631
625
  if non_zero_counts or accuracy > 90.0: # Include high-accuracy resources
632
626
  total_weighted_accuracy += accuracy * resource_weight
633
627
  total_weight += resource_weight
634
-
628
+
635
629
  # Calculate overall account accuracy using weighted methodology
636
630
  if total_weight > 0:
637
631
  account_analysis["overall_accuracy"] = total_weighted_accuracy / total_weight
638
632
  else:
639
633
  account_analysis["overall_accuracy"] = 95.0 # Default high accuracy for no data
640
-
634
+
641
635
  return account_analysis
642
636
 
643
637
  def _get_all_aws_regions(self) -> List[str]:
@@ -646,25 +640,44 @@ class UnifiedValidationEngine:
646
640
  # Use a session to get all available regions
647
641
  session = boto3.Session(profile_name=self.enterprise_profiles["operational"])
648
642
  ec2_client = session.client("ec2", region_name="us-east-1")
649
-
643
+
650
644
  # Get all regions including opt-in regions
651
645
  response = ec2_client.describe_regions(AllRegions=True)
652
646
  regions = [region["RegionName"] for region in response["Regions"]]
653
-
647
+
654
648
  # Sort for consistent ordering
655
649
  regions.sort()
656
650
  return regions
657
-
651
+
658
652
  except Exception:
659
653
  # Fallback to comprehensive static list if API call fails
660
654
  return [
661
- "us-east-1", "us-east-2", "us-west-1", "us-west-2",
662
- "eu-west-1", "eu-west-2", "eu-west-3", "eu-central-1", "eu-north-1",
663
- "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", "ap-northeast-2", "ap-south-1",
664
- "ca-central-1", "sa-east-1",
665
- "af-south-1", "ap-east-1", "ap-southeast-3", "ap-northeast-3",
666
- "eu-central-2", "eu-south-1", "eu-south-2", "eu-west-3",
667
- "me-south-1", "me-central-1"
655
+ "us-east-1",
656
+ "us-east-2",
657
+ "us-west-1",
658
+ "us-west-2",
659
+ "eu-west-1",
660
+ "eu-west-2",
661
+ "eu-west-3",
662
+ "eu-central-1",
663
+ "eu-north-1",
664
+ "ap-southeast-1",
665
+ "ap-southeast-2",
666
+ "ap-northeast-1",
667
+ "ap-northeast-2",
668
+ "ap-south-1",
669
+ "ca-central-1",
670
+ "sa-east-1",
671
+ "af-south-1",
672
+ "ap-east-1",
673
+ "ap-southeast-3",
674
+ "ap-northeast-3",
675
+ "eu-central-2",
676
+ "eu-south-1",
677
+ "eu-south-2",
678
+ "eu-west-3",
679
+ "me-south-1",
680
+ "me-central-1",
668
681
  ]
669
682
 
670
683
  def _get_validated_session_for_resource(self, resource_type: str, region: str) -> Optional[boto3.Session]:
@@ -677,37 +690,37 @@ class UnifiedValidationEngine:
677
690
  "vpc": ["operational", "single_account"],
678
691
  "rds": ["operational", "single_account"],
679
692
  }
680
-
693
+
681
694
  profiles_to_try = profile_priorities.get(resource_type, ["operational", "single_account"])
682
-
695
+
683
696
  for profile_key in profiles_to_try:
684
697
  try:
685
698
  profile_name = self.enterprise_profiles.get(profile_key)
686
699
  if not profile_name:
687
700
  continue
688
-
701
+
689
702
  session = boto3.Session(profile_name=profile_name)
690
-
703
+
691
704
  # Quick validation test - try to get caller identity
692
705
  sts_client = session.client("sts", region_name=region)
693
706
  sts_client.get_caller_identity()
694
-
707
+
695
708
  return session
696
-
709
+
697
710
  except Exception as e:
698
711
  # Log session validation failures for debugging
699
712
  error_type = self._classify_aws_error(e)
700
713
  if error_type not in ["auth_expired", "unauthorized"]:
701
714
  self.console.log(f"[dim red]Session validation failed for {profile_key}: {error_type}[/]")
702
715
  continue
703
-
716
+
704
717
  # No valid session found
705
718
  return None
706
719
 
707
720
  def _classify_aws_error(self, error: Exception) -> str:
708
721
  """Classify AWS errors for better error handling and reporting."""
709
722
  error_str = str(error).lower()
710
-
723
+
711
724
  if "token has expired" in error_str or "expired" in error_str:
712
725
  return "auth_expired"
713
726
  elif "unauthorizedoperation" in error_str or "access denied" in error_str:
@@ -723,17 +736,15 @@ class UnifiedValidationEngine:
723
736
  else:
724
737
  return "unknown_error"
725
738
 
726
- async def _collect_resource_count(
727
- self, resource_type: str, account_id: str, regions: List[str]
728
- ) -> int:
739
+ async def _collect_resource_count(self, resource_type: str, account_id: str, regions: List[str]) -> int:
729
740
  """
730
741
  Enhanced resource count collection with enterprise accuracy improvements.
731
-
742
+
732
743
  Args:
733
744
  resource_type: AWS resource type to collect
734
745
  account_id: AWS account ID
735
746
  regions: List of regions to search
736
-
747
+
737
748
  Returns:
738
749
  Actual resource count from AWS APIs with enhanced accuracy
739
750
  """
@@ -744,11 +755,11 @@ class UnifiedValidationEngine:
744
755
  total_count = 0
745
756
  successful_regions = 0
746
757
  failed_regions = []
747
-
758
+
748
759
  # Get all AWS regions for comprehensive coverage (enterprise enhancement)
749
760
  if not regions or regions == ["us-east-1"]:
750
761
  regions = self._get_all_aws_regions()
751
-
762
+
752
763
  for region in regions:
753
764
  try:
754
765
  # Enhanced session management with fallback profiles
@@ -756,19 +767,19 @@ class UnifiedValidationEngine:
756
767
  if not session:
757
768
  failed_regions.append(f"{region}:no_session")
758
769
  continue
759
-
770
+
760
771
  ec2_client = session.client("ec2", region_name=region)
761
-
772
+
762
773
  # Enhanced pagination with better error handling
763
- paginator = ec2_client.get_paginator('describe_instances')
774
+ paginator = ec2_client.get_paginator("describe_instances")
764
775
  region_instances = 0
765
-
776
+
766
777
  try:
767
778
  # Add timeout and retry logic for enterprise reliability
768
779
  for page in paginator.paginate(
769
780
  PaginationConfig={
770
- 'MaxItems': 10000, # Prevent runaway pagination
771
- 'PageSize': 500 # Optimize API call efficiency
781
+ "MaxItems": 10000, # Prevent runaway pagination
782
+ "PageSize": 500, # Optimize API call efficiency
772
783
  }
773
784
  ):
774
785
  for reservation in page.get("Reservations", []):
@@ -778,37 +789,43 @@ class UnifiedValidationEngine:
778
789
  except Exception as page_error:
779
790
  # Handle pagination-specific errors
780
791
  if "UnauthorizedOperation" not in str(page_error):
781
- self.console.log(f"[dim yellow]EC2 pagination error in {region}: {str(page_error)[:40]}[/]")
792
+ self.console.log(
793
+ f"[dim yellow]EC2 pagination error in {region}: {str(page_error)[:40]}[/]"
794
+ )
782
795
  failed_regions.append(f"{region}:pagination_error")
783
796
  continue
784
-
797
+
785
798
  total_count += region_instances
786
799
  successful_regions += 1
787
-
800
+
788
801
  # Log regional discovery for debugging
789
802
  if region_instances > 0:
790
803
  self.console.log(f"[dim green]EC2 {region}: {region_instances} instances[/]")
791
-
804
+
792
805
  except Exception as e:
793
806
  # Enhanced error handling with specific error classification
794
807
  error_type = self._classify_aws_error(e)
795
808
  failed_regions.append(f"{region}:{error_type}")
796
-
809
+
797
810
  # Only log unexpected errors to reduce noise
798
811
  if error_type not in ["auth_expired", "unauthorized", "region_disabled"]:
799
812
  self.console.log(f"[dim red]EC2 {region}: {error_type}[/]")
800
813
  continue
801
-
814
+
802
815
  # Enhanced reporting with enterprise context
803
816
  coverage_percent = (successful_regions / len(regions)) * 100 if regions else 0
804
- self.console.log(f"[cyan]EC2 Enhanced Discovery: {total_count} instances across {successful_regions}/{len(regions)} regions ({coverage_percent:.1f}% coverage)[/]")
805
-
817
+ self.console.log(
818
+ f"[cyan]EC2 Enhanced Discovery: {total_count} instances across {successful_regions}/{len(regions)} regions ({coverage_percent:.1f}% coverage)[/]"
819
+ )
820
+
806
821
  # Log failed regions for troubleshooting if significant
807
822
  if len(failed_regions) > 0 and coverage_percent < 80:
808
- self.console.log(f"[dim yellow]Failed regions: {failed_regions[:5]}{'...' if len(failed_regions) > 5 else ''}[/]")
809
-
823
+ self.console.log(
824
+ f"[dim yellow]Failed regions: {failed_regions[:5]}{'...' if len(failed_regions) > 5 else ''}[/]"
825
+ )
826
+
810
827
  return total_count
811
-
828
+
812
829
  elif resource_type == "s3":
813
830
  # S3 buckets are global, check once
814
831
  try:
@@ -818,7 +835,7 @@ class UnifiedValidationEngine:
818
835
  return len(response.get("Buckets", []))
819
836
  except Exception:
820
837
  return 0
821
-
838
+
822
839
  elif resource_type == "vpc":
823
840
  # Collect VPCs across regions
824
841
  total_count = 0
@@ -831,7 +848,7 @@ class UnifiedValidationEngine:
831
848
  except Exception:
832
849
  continue
833
850
  return total_count
834
-
851
+
835
852
  elif resource_type == "lambda":
836
853
  # Collect Lambda functions across regions
837
854
  total_count = 0
@@ -844,7 +861,7 @@ class UnifiedValidationEngine:
844
861
  except Exception:
845
862
  continue
846
863
  return total_count
847
-
864
+
848
865
  elif resource_type == "rds":
849
866
  # Collect RDS instances across regions
850
867
  total_count = 0
@@ -857,7 +874,7 @@ class UnifiedValidationEngine:
857
874
  except Exception:
858
875
  continue
859
876
  return total_count
860
-
877
+
861
878
  elif resource_type == "iam":
862
879
  # IAM roles are global
863
880
  try:
@@ -879,7 +896,9 @@ class UnifiedValidationEngine:
879
896
  session = boto3.Session(profile_name=self.enterprise_profiles["operational"])
880
897
  cf_client = session.client("cloudformation", region_name=region)
881
898
  paginator = cf_client.get_paginator("list_stacks")
882
- for page in paginator.paginate(StackStatusFilter=['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'ROLLBACK_COMPLETE']):
899
+ for page in paginator.paginate(
900
+ StackStatusFilter=["CREATE_COMPLETE", "UPDATE_COMPLETE", "ROLLBACK_COMPLETE"]
901
+ ):
883
902
  total_count += len(page.get("StackSummaries", []))
884
903
  except Exception:
885
904
  continue
@@ -953,42 +972,40 @@ class UnifiedValidationEngine:
953
972
  except Exception:
954
973
  continue
955
974
  return total_count
956
-
975
+
957
976
  else:
958
977
  # For any other resource types, return 0
959
978
  return 0
960
-
979
+
961
980
  except Exception as e:
962
981
  self.console.log(f"[red]Error collecting {resource_type}: {str(e)[:40]}[/]")
963
982
  return 0
964
983
 
965
- def _generate_actionable_recommendations(
966
- self, unified_analysis: Dict[str, Any]
967
- ) -> List[str]:
984
+ def _generate_actionable_recommendations(self, unified_analysis: Dict[str, Any]) -> List[str]:
968
985
  """Generate actionable recommendations based on validation results."""
969
986
  self.console.print(f"[yellow]💡 Generating actionable recommendations[/yellow]")
970
-
987
+
971
988
  recommendations = []
972
989
  overall_accuracy = unified_analysis.get("overall_accuracy", 0)
973
-
990
+
974
991
  # Overall accuracy recommendations
975
992
  if overall_accuracy < self.validation_threshold:
976
993
  recommendations.append(
977
994
  f"Overall validation accuracy ({overall_accuracy:.1f}%) is below enterprise threshold ({self.validation_threshold}%). "
978
995
  "Review resource discovery methods and API access permissions."
979
996
  )
980
-
997
+
981
998
  # Account-specific recommendations
982
999
  for account_id, account_data in unified_analysis.get("account_analysis", {}).items():
983
1000
  account_accuracy = account_data.get("overall_accuracy", 0)
984
1001
  sources_count = account_data.get("sources_with_data", 0)
985
-
1002
+
986
1003
  if account_accuracy < 90.0:
987
1004
  recommendations.append(
988
1005
  f"Account {account_id} has {account_accuracy:.1f}% accuracy with {sources_count} validation sources. "
989
1006
  "Consider reviewing AWS permissions and terraform configuration."
990
1007
  )
991
-
1008
+
992
1009
  # Resource-specific recommendations
993
1010
  for resource_type, resource_data in account_data.get("resource_analysis", {}).items():
994
1011
  variance = resource_data.get("variance_percent", 0)
@@ -997,30 +1014,30 @@ class UnifiedValidationEngine:
997
1014
  f"High variance detected for {self.supported_resources.get(resource_type, resource_type)} "
998
1015
  f"in account {account_id} ({variance:.1f}% variance). Verify collection methods."
999
1016
  )
1000
-
1017
+
1001
1018
  # Source-specific recommendations
1002
1019
  validation_summary = unified_analysis.get("validation_summary", {})
1003
1020
  successful_sources = validation_summary.get("validation_sources_successful", 0)
1004
-
1021
+
1005
1022
  if successful_sources < 2:
1006
1023
  recommendations.append(
1007
1024
  f"Only {successful_sources}/3 validation sources successful. "
1008
1025
  "Check MCP server configuration and terraform setup."
1009
1026
  )
1010
-
1027
+
1011
1028
  # Performance recommendations
1012
1029
  if not unified_analysis.get("performance_achieved", True):
1013
1030
  recommendations.append(
1014
1031
  f"Validation exceeded {self.performance_target}s target. "
1015
1032
  "Consider enabling caching or reducing scope for better performance."
1016
1033
  )
1017
-
1034
+
1018
1035
  if not recommendations:
1019
1036
  recommendations.append(
1020
1037
  "✅ Validation completed successfully with no issues detected. "
1021
1038
  "All sources are aligned and operating within enterprise thresholds."
1022
1039
  )
1023
-
1040
+
1024
1041
  return recommendations
1025
1042
 
1026
1043
  def _display_unified_validation_results(self, validation_results: Dict[str, Any]) -> None:
@@ -1029,48 +1046,53 @@ class UnifiedValidationEngine:
1029
1046
  passed = validation_results.get("passed_validation", False)
1030
1047
  performance_metrics = validation_results.get("performance_metrics", {})
1031
1048
  validation_summary = validation_results.get("validation_summary", {})
1032
-
1049
+
1033
1050
  self.console.print(f"\n[bright_cyan]🔍 Unified 3-Way Validation Results[/]")
1034
-
1051
+
1035
1052
  # Performance metrics
1036
1053
  total_time = performance_metrics.get("total_execution_time", 0)
1037
1054
  performance_achieved = performance_metrics.get("performance_achieved", True)
1038
1055
  performance_icon = "✅" if performance_achieved else "⚠️"
1039
-
1040
- self.console.print(f"[dim]⚡ Performance: {performance_icon} {total_time:.1f}s (target: <{self.performance_target}s)[/]")
1041
-
1056
+
1057
+ self.console.print(
1058
+ f"[dim]⚡ Performance: {performance_icon} {total_time:.1f}s (target: <{self.performance_target}s)[/]"
1059
+ )
1060
+
1042
1061
  # Validation sources summary
1043
1062
  sources_successful = validation_summary.get("validation_sources_successful", 0)
1044
1063
  total_accounts = validation_summary.get("total_accounts_analyzed", 0)
1045
1064
  total_resources = validation_summary.get("total_resource_types", 0)
1046
-
1047
- self.console.print(f"[dim]🔗 Sources: {sources_successful}/3 successful | Accounts: {total_accounts} | Resources: {total_resources}[/]")
1048
-
1065
+
1066
+ self.console.print(
1067
+ f"[dim]🔗 Sources: {sources_successful}/3 successful | Accounts: {total_accounts} | Resources: {total_resources}[/]"
1068
+ )
1069
+
1049
1070
  # Overall result
1050
1071
  if passed:
1051
1072
  print_success(f"✅ Unified Validation PASSED: {overall_accuracy:.1f}% accuracy achieved")
1052
1073
  else:
1053
- print_warning(f"🔄 Unified Validation: {overall_accuracy:.1f}% accuracy (≥{self.validation_threshold}% required)")
1054
-
1074
+ print_warning(
1075
+ f"🔄 Unified Validation: {overall_accuracy:.1f}% accuracy (≥{self.validation_threshold}% required)"
1076
+ )
1077
+
1055
1078
  # Account-level results table
1056
1079
  account_analysis = validation_results.get("account_analysis", {})
1057
1080
  if account_analysis:
1058
1081
  self.console.print(f"\n[bright_cyan]📊 Account-Level Validation Results[/]")
1059
-
1082
+
1060
1083
  account_table = create_table(
1061
- title="3-Way Cross-Validation Results",
1062
- caption="Sources: Runbooks | MCP | Terraform"
1084
+ title="3-Way Cross-Validation Results", caption="Sources: Runbooks | MCP | Terraform"
1063
1085
  )
1064
-
1086
+
1065
1087
  account_table.add_column("Account ID", style="cyan", no_wrap=True)
1066
1088
  account_table.add_column("Overall Accuracy", justify="right")
1067
1089
  account_table.add_column("Sources", justify="center")
1068
1090
  account_table.add_column("Status", style="yellow")
1069
-
1091
+
1070
1092
  for account_id, account_data in account_analysis.items():
1071
1093
  account_accuracy = account_data.get("overall_accuracy", 0)
1072
1094
  sources_count = account_data.get("sources_with_data", 0)
1073
-
1095
+
1074
1096
  # Determine status
1075
1097
  if account_accuracy >= self.validation_threshold:
1076
1098
  status = "✅ Passed"
@@ -1081,19 +1103,14 @@ class UnifiedValidationEngine:
1081
1103
  else:
1082
1104
  status = "❌ Needs Review"
1083
1105
  status_color = "red"
1084
-
1106
+
1085
1107
  accuracy_display = f"{account_accuracy:.1f}%"
1086
1108
  sources_display = f"{sources_count}/3"
1087
-
1088
- account_table.add_row(
1089
- account_id,
1090
- accuracy_display,
1091
- sources_display,
1092
- status
1093
- )
1094
-
1109
+
1110
+ account_table.add_row(account_id, accuracy_display, sources_display, status)
1111
+
1095
1112
  self.console.print(account_table)
1096
-
1113
+
1097
1114
  # Recommendations
1098
1115
  recommendations = validation_results.get("recommendations", [])
1099
1116
  if recommendations:
@@ -1109,14 +1126,14 @@ class UnifiedValidationEngine:
1109
1126
  ) -> None:
1110
1127
  """Export comprehensive validation evidence in multiple formats."""
1111
1128
  self.console.print(f"[blue]📤 Exporting validation evidence[/blue]")
1112
-
1129
+
1113
1130
  # Create output directory
1114
1131
  output_path = Path(output_directory)
1115
1132
  output_path.mkdir(parents=True, exist_ok=True)
1116
-
1133
+
1117
1134
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
1118
1135
  base_filename = f"unified_validation_{timestamp}"
1119
-
1136
+
1120
1137
  for export_format in export_formats:
1121
1138
  try:
1122
1139
  if export_format == "json":
@@ -1124,50 +1141,59 @@ class UnifiedValidationEngine:
1124
1141
  with open(json_file, "w") as f:
1125
1142
  json.dump(validation_results, f, indent=2, default=str)
1126
1143
  print_info(f"JSON export: {json_file}")
1127
-
1144
+
1128
1145
  elif export_format == "csv":
1129
1146
  csv_file = output_path / f"{base_filename}.csv"
1130
1147
  self._export_csv_evidence(validation_results, csv_file)
1131
1148
  print_info(f"CSV export: {csv_file}")
1132
-
1149
+
1133
1150
  elif export_format == "markdown":
1134
1151
  md_file = output_path / f"{base_filename}.md"
1135
1152
  self._export_markdown_evidence(validation_results, md_file)
1136
1153
  print_info(f"Markdown export: {md_file}")
1137
-
1154
+
1138
1155
  elif export_format == "pdf":
1139
1156
  print_info("PDF export: Feature planned for future release")
1140
-
1157
+
1141
1158
  except Exception as e:
1142
1159
  print_warning(f"Failed to export {export_format}: {str(e)[:40]}")
1143
1160
 
1144
1161
  def _export_csv_evidence(self, validation_results: Dict[str, Any], csv_file: Path) -> None:
1145
1162
  """Export validation evidence in CSV format."""
1146
1163
  import csv
1147
-
1164
+
1148
1165
  with open(csv_file, "w", newline="") as f:
1149
1166
  writer = csv.writer(f)
1150
-
1167
+
1151
1168
  # Header
1152
- writer.writerow([
1153
- "Account ID", "Resource Type", "Runbooks Count", "MCP Count",
1154
- "Terraform Count", "Accuracy %", "Variance %"
1155
- ])
1156
-
1169
+ writer.writerow(
1170
+ [
1171
+ "Account ID",
1172
+ "Resource Type",
1173
+ "Runbooks Count",
1174
+ "MCP Count",
1175
+ "Terraform Count",
1176
+ "Accuracy %",
1177
+ "Variance %",
1178
+ ]
1179
+ )
1180
+
1157
1181
  # Data rows
1158
1182
  account_analysis = validation_results.get("account_analysis", {})
1159
1183
  for account_id, account_data in account_analysis.items():
1160
1184
  resource_analysis = account_data.get("resource_analysis", {})
1161
1185
  for resource_type, resource_data in resource_analysis.items():
1162
- writer.writerow([
1163
- account_id,
1164
- self.supported_resources.get(resource_type, resource_type),
1165
- resource_data.get("runbooks_count", 0),
1166
- resource_data.get("mcp_count", 0),
1167
- resource_data.get("terraform_count", 0),
1168
- f"{resource_data.get('accuracy_percent', 0):.1f}",
1169
- f"{resource_data.get('variance_percent', 0):.1f}",
1170
- ])
1186
+ writer.writerow(
1187
+ [
1188
+ account_id,
1189
+ self.supported_resources.get(resource_type, resource_type),
1190
+ resource_data.get("runbooks_count", 0),
1191
+ resource_data.get("mcp_count", 0),
1192
+ resource_data.get("terraform_count", 0),
1193
+ f"{resource_data.get('accuracy_percent', 0):.1f}",
1194
+ f"{resource_data.get('variance_percent', 0):.1f}",
1195
+ ]
1196
+ )
1171
1197
 
1172
1198
  def _export_markdown_evidence(self, validation_results: Dict[str, Any], md_file: Path) -> None:
1173
1199
  """Export validation evidence in Markdown format."""
@@ -1176,13 +1202,13 @@ class UnifiedValidationEngine:
1176
1202
  f.write(f"**Generated**: {validation_results.get('validation_timestamp', 'Unknown')}\n")
1177
1203
  f.write(f"**Overall Accuracy**: {validation_results.get('overall_accuracy', 0):.1f}%\n")
1178
1204
  f.write(f"**Validation Passed**: {validation_results.get('passed_validation', False)}\n\n")
1179
-
1205
+
1180
1206
  # Performance metrics
1181
1207
  performance_metrics = validation_results.get("performance_metrics", {})
1182
1208
  total_time = performance_metrics.get("total_execution_time", 0)
1183
1209
  f.write(f"**Execution Time**: {total_time:.1f}s\n")
1184
1210
  f.write(f"**Performance Target**: <{self.performance_target}s\n\n")
1185
-
1211
+
1186
1212
  # Validation sources
1187
1213
  f.write("## Validation Sources\n\n")
1188
1214
  validation_sources = validation_results.get("validation_sources", {})
@@ -1190,7 +1216,7 @@ class UnifiedValidationEngine:
1190
1216
  status = "✅ Enabled" if enabled else "❌ Disabled"
1191
1217
  f.write(f"- **{source.replace('_', ' ').title()}**: {status}\n")
1192
1218
  f.write("\n")
1193
-
1219
+
1194
1220
  # Account analysis
1195
1221
  f.write("## Account Analysis\n\n")
1196
1222
  account_analysis = validation_results.get("account_analysis", {})
@@ -1198,21 +1224,23 @@ class UnifiedValidationEngine:
1198
1224
  f.write(f"### Account: {account_id}\n\n")
1199
1225
  f.write(f"- **Overall Accuracy**: {account_data.get('overall_accuracy', 0):.1f}%\n")
1200
1226
  f.write(f"- **Sources with Data**: {account_data.get('sources_with_data', 0)}/3\n\n")
1201
-
1227
+
1202
1228
  # Resource breakdown
1203
1229
  f.write("#### Resource Validation\n\n")
1204
1230
  f.write("| Resource Type | Runbooks | MCP | Terraform | Accuracy |\n")
1205
1231
  f.write("|---------------|----------|-----|-----------|----------|\n")
1206
-
1232
+
1207
1233
  resource_analysis = account_data.get("resource_analysis", {})
1208
1234
  for resource_type, resource_data in resource_analysis.items():
1209
- f.write(f"| {self.supported_resources.get(resource_type, resource_type)} | "
1210
- f"{resource_data.get('runbooks_count', 0)} | "
1211
- f"{resource_data.get('mcp_count', 0)} | "
1212
- f"{resource_data.get('terraform_count', 0)} | "
1213
- f"{resource_data.get('accuracy_percent', 0):.1f}% |\n")
1235
+ f.write(
1236
+ f"| {self.supported_resources.get(resource_type, resource_type)} | "
1237
+ f"{resource_data.get('runbooks_count', 0)} | "
1238
+ f"{resource_data.get('mcp_count', 0)} | "
1239
+ f"{resource_data.get('terraform_count', 0)} | "
1240
+ f"{resource_data.get('accuracy_percent', 0):.1f}% |\n"
1241
+ )
1214
1242
  f.write("\n")
1215
-
1243
+
1216
1244
  # Recommendations
1217
1245
  f.write("## Recommendations\n\n")
1218
1246
  recommendations = validation_results.get("recommendations", [])
@@ -1228,13 +1256,13 @@ def create_unified_validation_engine(
1228
1256
  ) -> UnifiedValidationEngine:
1229
1257
  """
1230
1258
  Factory function to create unified validation engine.
1231
-
1259
+
1232
1260
  Args:
1233
1261
  user_profile: User-specified profile (--profile parameter)
1234
1262
  console: Rich console for output
1235
1263
  mcp_config_path: Path to .mcp.json configuration file
1236
1264
  terraform_directory: Path to terraform configurations
1237
-
1265
+
1238
1266
  Returns:
1239
1267
  Unified validation engine instance
1240
1268
  """
@@ -1256,7 +1284,7 @@ async def run_comprehensive_validation(
1256
1284
  ) -> Dict[str, Any]:
1257
1285
  """
1258
1286
  Convenience function to run comprehensive 3-way validation.
1259
-
1287
+
1260
1288
  Args:
1261
1289
  user_profile: User-specified profile
1262
1290
  resource_types: List of resource types to validate
@@ -1264,16 +1292,16 @@ async def run_comprehensive_validation(
1264
1292
  regions: List of regions to analyze
1265
1293
  export_formats: List of export formats
1266
1294
  output_directory: Directory for evidence exports
1267
-
1295
+
1268
1296
  Returns:
1269
1297
  Comprehensive validation results
1270
1298
  """
1271
1299
  engine = create_unified_validation_engine(user_profile=user_profile)
1272
-
1300
+
1273
1301
  return await engine.run_unified_validation(
1274
1302
  resource_types=resource_types,
1275
1303
  accounts=accounts,
1276
1304
  regions=regions,
1277
1305
  export_formats=export_formats,
1278
1306
  output_directory=output_directory,
1279
- )
1307
+ )