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

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