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.
- runbooks/__init__.py +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/assessment/compliance.py +1 -1
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cli/__init__.py +1 -1
- runbooks/cli/commands/cfat.py +64 -23
- runbooks/cli/commands/finops.py +1005 -54
- runbooks/cli/commands/inventory.py +135 -91
- runbooks/cli/commands/operate.py +9 -36
- runbooks/cli/commands/security.py +42 -18
- runbooks/cli/commands/validation.py +432 -18
- runbooks/cli/commands/vpc.py +81 -17
- runbooks/cli/registry.py +22 -10
- runbooks/cloudops/__init__.py +20 -27
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +544 -542
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +224 -225
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +177 -213
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +17 -12
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +40 -36
- runbooks/common/aws_utils.py +74 -79
- runbooks/common/business_logic.py +126 -104
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
- runbooks/common/cross_account_manager.py +197 -204
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +29 -19
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +476 -493
- runbooks/common/mcp_integration.py +99 -79
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +175 -193
- runbooks/common/patterns.py +23 -25
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +111 -37
- runbooks/common/rich_utils.py +315 -141
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +26 -30
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +484 -618
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +32 -29
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +223 -285
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +337 -174
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1512 -481
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +19 -23
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +64 -65
- runbooks/finops/scenarios.py +1277 -438
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +259 -265
- runbooks/finops/vpc_cleanup_exporter.py +189 -144
- runbooks/finops/vpc_cleanup_optimizer.py +591 -573
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/aws_decorators.py +2 -3
- runbooks/inventory/check_cloudtrail_compliance.py +2 -4
- runbooks/inventory/check_controltower_readiness.py +152 -151
- runbooks/inventory/check_landingzone_readiness.py +85 -84
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +1 -1
- runbooks/inventory/collectors/aws_networking.py +109 -99
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/core/formatter.py +11 -0
- runbooks/inventory/draw_org_structure.py +8 -9
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/ec2_vpc_utils.py +2 -2
- runbooks/inventory/find_cfn_drift_detection.py +5 -7
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
- runbooks/inventory/find_cfn_stackset_drift.py +5 -6
- runbooks/inventory/find_ec2_security_groups.py +48 -42
- runbooks/inventory/find_landingzone_versions.py +4 -6
- runbooks/inventory/find_vpc_flow_logs.py +7 -9
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/inventory_modules.py +103 -91
- runbooks/inventory/list_cfn_stacks.py +9 -10
- runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
- runbooks/inventory/list_cfn_stackset_operations.py +79 -57
- runbooks/inventory/list_cfn_stacksets.py +8 -10
- runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
- runbooks/inventory/list_ds_directories.py +65 -53
- runbooks/inventory/list_ec2_availability_zones.py +2 -4
- runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
- runbooks/inventory/list_ec2_instances.py +23 -28
- runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
- runbooks/inventory/list_elbs_load_balancers.py +22 -20
- runbooks/inventory/list_enis_network_interfaces.py +26 -33
- runbooks/inventory/list_guardduty_detectors.py +2 -4
- runbooks/inventory/list_iam_policies.py +2 -4
- runbooks/inventory/list_iam_roles.py +5 -7
- runbooks/inventory/list_iam_saml_providers.py +4 -6
- runbooks/inventory/list_lambda_functions.py +38 -38
- runbooks/inventory/list_org_accounts.py +6 -8
- runbooks/inventory/list_org_accounts_users.py +55 -44
- runbooks/inventory/list_rds_db_instances.py +31 -33
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/list_route53_hosted_zones.py +3 -5
- runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
- runbooks/inventory/list_sns_topics.py +2 -4
- runbooks/inventory/list_ssm_parameters.py +4 -7
- runbooks/inventory/list_vpc_subnets.py +2 -4
- runbooks/inventory/list_vpcs.py +7 -10
- runbooks/inventory/mcp_inventory_validator.py +554 -468
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +63 -55
- runbooks/inventory/recover_cfn_stack_ids.py +7 -8
- runbooks/inventory/requirements.txt +0 -1
- runbooks/inventory/rich_inventory_display.py +35 -34
- runbooks/inventory/run_on_multi_accounts.py +3 -5
- runbooks/inventory/unified_validation_engine.py +281 -253
- runbooks/inventory/verify_ec2_security_groups.py +1 -1
- runbooks/inventory/vpc_analyzer.py +735 -697
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +384 -380
- runbooks/inventory/vpc_flow_analyzer.py +1 -1
- runbooks/main.py +49 -34
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/networking_cost_heatmap.py +29 -8
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_operations.py +646 -616
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +70 -66
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +86 -60
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +46 -41
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +50 -47
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +745 -704
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +461 -454
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +185 -160
- runbooks/vpc/mcp_no_eni_validator.py +680 -639
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1297 -1124
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.6.dist-info/METADATA +327 -0
- runbooks-1.1.6.dist-info/RECORD +489 -0
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -973
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.4.dist-info/METADATA +0 -800
- runbooks-1.1.4.dist-info/RECORD +0 -468
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {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.
|
42
|
-
from
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
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(
|
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
|
-
|
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
|
-
|
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"] =
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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"] =
|
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(
|
479
|
-
|
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
|
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",
|
662
|
-
"
|
663
|
-
"
|
664
|
-
"
|
665
|
-
"
|
666
|
-
"eu-
|
667
|
-
"
|
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(
|
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
|
-
|
771
|
-
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
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
|
-
|
1154
|
-
|
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
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
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(
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
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
|
+
)
|