runbooks 1.1.3__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.
- 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/WEIGHT_CONFIG_README.md +1 -1
- runbooks/cfat/assessment/compliance.py +8 -8
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cfat/models.py +6 -2
- runbooks/cfat/tests/__init__.py +6 -1
- runbooks/cli/__init__.py +13 -0
- runbooks/cli/commands/cfat.py +274 -0
- runbooks/cli/commands/finops.py +1164 -0
- runbooks/cli/commands/inventory.py +379 -0
- runbooks/cli/commands/operate.py +239 -0
- runbooks/cli/commands/security.py +248 -0
- runbooks/cli/commands/validation.py +825 -0
- runbooks/cli/commands/vpc.py +310 -0
- runbooks/cli/registry.py +107 -0
- runbooks/cloudops/__init__.py +23 -30
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +549 -547
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +226 -227
- 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 +179 -215
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +11 -0
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +341 -0
- runbooks/common/aws_utils.py +75 -80
- runbooks/common/business_logic.py +127 -105
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
- runbooks/common/cross_account_manager.py +198 -205
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +235 -0
- 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 +478 -495
- runbooks/common/mcp_integration.py +63 -74
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +176 -194
- runbooks/common/patterns.py +204 -0
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +248 -39
- runbooks/common/rich_utils.py +643 -92
- 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 +29 -33
- 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 +488 -622
- 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 +40 -37
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +230 -292
- 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 +338 -175
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1513 -482
- 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 +25 -29
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +77 -78
- runbooks/finops/scenarios.py +1278 -439
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/tests/test_finops_dashboard.py +3 -3
- runbooks/finops/tests/test_reference_images_validation.py +2 -2
- runbooks/finops/tests/test_single_account_features.py +17 -17
- runbooks/finops/tests/validate_test_suite.py +1 -1
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +263 -269
- runbooks/finops/vpc_cleanup_exporter.py +191 -146
- runbooks/finops/vpc_cleanup_optimizer.py +593 -575
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/hitl/enhanced_workflow_engine.py +1 -1
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/README.md +3 -3
- runbooks/inventory/Tests/common_test_data.py +30 -30
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +28 -11
- runbooks/inventory/collectors/aws_networking.py +111 -101
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/discovery.md +2 -2
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/find_ec2_security_groups.py +1 -1
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/mcp_inventory_validator.py +549 -465
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +56 -52
- runbooks/inventory/rich_inventory_display.py +33 -32
- runbooks/inventory/unified_validation_engine.py +278 -251
- runbooks/inventory/vpc_analyzer.py +733 -696
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +382 -378
- runbooks/inventory/vpc_flow_analyzer.py +3 -3
- runbooks/main.py +152 -9147
- 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/metrics/dora_metrics_engine.py +2 -2
- 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/mcp_integration.py +1 -1
- runbooks/operate/networking_cost_heatmap.py +33 -10
- runbooks/operate/privatelink_operations.py +1 -1
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_endpoints.py +1 -1
- runbooks/operate/vpc_operations.py +648 -618
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +71 -67
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +91 -65
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +49 -44
- 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/integration_test_enterprise_security.py +5 -3
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/run_script.py +1 -1
- 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/mcp_reliability_engine.py +6 -6
- 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 +51 -48
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +754 -708
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +447 -451
- 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 +190 -162
- runbooks/vpc/mcp_no_eni_validator.py +681 -640
- 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 +1302 -1129
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.5.dist-info/METADATA +328 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
- 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 -956
- 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.3.dist-info/METADATA +0 -799
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -33,36 +33,36 @@ from runbooks.common.rich_utils import (
|
|
33
33
|
print_success,
|
34
34
|
print_warning,
|
35
35
|
print_error,
|
36
|
-
STATUS_INDICATORS
|
36
|
+
STATUS_INDICATORS,
|
37
37
|
)
|
38
38
|
|
39
39
|
|
40
40
|
class OperationType(Enum):
|
41
41
|
"""Classification of operation types for appropriate dry-run behavior."""
|
42
|
-
|
42
|
+
|
43
43
|
# READ-ONLY Operations (inherently safe)
|
44
|
-
DISCOVERY = "discovery"
|
45
|
-
ANALYSIS = "analysis"
|
46
|
-
ASSESSMENT = "assessment"
|
47
|
-
REPORTING = "reporting"
|
48
|
-
|
44
|
+
DISCOVERY = "discovery" # inventory collect, scan
|
45
|
+
ANALYSIS = "analysis" # finops dashboard, security assess, vpc analyze
|
46
|
+
ASSESSMENT = "assessment" # cfat assess
|
47
|
+
REPORTING = "reporting" # generate reports, export data
|
48
|
+
|
49
49
|
# STATE-CHANGING Operations (require safety controls)
|
50
|
-
RESOURCE_CREATE = "create"
|
51
|
-
RESOURCE_MODIFY = "modify"
|
52
|
-
RESOURCE_DELETE = "delete"
|
53
|
-
CONFIGURATION = "config"
|
54
|
-
REMEDIATION = "remediation"
|
55
|
-
|
50
|
+
RESOURCE_CREATE = "create" # EC2 instances, S3 buckets, VPCs
|
51
|
+
RESOURCE_MODIFY = "modify" # Update configurations, scaling
|
52
|
+
RESOURCE_DELETE = "delete" # Terminate, delete resources
|
53
|
+
CONFIGURATION = "config" # Change settings, policies
|
54
|
+
REMEDIATION = "remediation" # Security fixes, compliance actions
|
55
|
+
|
56
56
|
# HIGH-RISK Operations (explicit confirmation required)
|
57
|
-
BULK_OPERATIONS = "bulk"
|
58
|
-
CROSS_ACCOUNT = "cross_account"
|
59
|
-
FINANCIAL = "financial"
|
57
|
+
BULK_OPERATIONS = "bulk" # Multi-resource operations
|
58
|
+
CROSS_ACCOUNT = "cross_account" # Operations affecting multiple accounts
|
59
|
+
FINANCIAL = "financial" # Budget modifications, billing changes
|
60
60
|
|
61
61
|
|
62
62
|
@dataclass
|
63
63
|
class DryRunContext:
|
64
64
|
"""Context information for dry-run operations."""
|
65
|
-
|
65
|
+
|
66
66
|
enabled: bool
|
67
67
|
operation_type: OperationType
|
68
68
|
module_name: str
|
@@ -72,7 +72,7 @@ class DryRunContext:
|
|
72
72
|
safety_level: str = "standard" # standard, high, critical
|
73
73
|
requires_confirmation: bool = False
|
74
74
|
audit_trail: List[Dict[str, Any]] = None
|
75
|
-
|
75
|
+
|
76
76
|
def __post_init__(self):
|
77
77
|
if self.audit_trail is None:
|
78
78
|
self.audit_trail = []
|
@@ -81,79 +81,79 @@ class DryRunContext:
|
|
81
81
|
class DryRunSafetyFramework:
|
82
82
|
"""
|
83
83
|
Universal dry-run safety framework for enterprise operations.
|
84
|
-
|
84
|
+
|
85
85
|
Provides consistent dry-run behavior, safety controls, and audit trails
|
86
86
|
across all runbooks modules.
|
87
87
|
"""
|
88
|
-
|
88
|
+
|
89
89
|
def __init__(self, console: Optional[Console] = None):
|
90
90
|
self.console = console or Console()
|
91
91
|
self.logger = logging.getLogger(__name__)
|
92
|
-
|
92
|
+
|
93
93
|
# Safety configuration
|
94
94
|
self.safety_configs = {
|
95
95
|
OperationType.DISCOVERY: {
|
96
96
|
"default_dry_run": False, # Discovery is inherently safe
|
97
97
|
"requires_confirmation": False,
|
98
|
-
"simulation_mode": True,
|
99
|
-
"warning_message": None
|
98
|
+
"simulation_mode": True, # Can simulate API calls
|
99
|
+
"warning_message": None,
|
100
100
|
},
|
101
101
|
OperationType.ANALYSIS: {
|
102
102
|
"default_dry_run": False, # Analysis is read-only
|
103
103
|
"requires_confirmation": False,
|
104
104
|
"simulation_mode": False, # Real API calls for analysis
|
105
|
-
"warning_message": None
|
105
|
+
"warning_message": None,
|
106
106
|
},
|
107
107
|
OperationType.ASSESSMENT: {
|
108
108
|
"default_dry_run": False, # Assessment is read-only
|
109
109
|
"requires_confirmation": False,
|
110
110
|
"simulation_mode": False,
|
111
|
-
"warning_message": None
|
111
|
+
"warning_message": None,
|
112
112
|
},
|
113
113
|
OperationType.RESOURCE_CREATE: {
|
114
|
-
"default_dry_run": True,
|
114
|
+
"default_dry_run": True, # Safety-first for resource creation
|
115
115
|
"requires_confirmation": True,
|
116
116
|
"simulation_mode": True,
|
117
|
-
"warning_message": "⚠️ RESOURCE CREATION: This will create new AWS resources and incur costs"
|
117
|
+
"warning_message": "⚠️ RESOURCE CREATION: This will create new AWS resources and incur costs",
|
118
118
|
},
|
119
119
|
OperationType.RESOURCE_MODIFY: {
|
120
|
-
"default_dry_run": True,
|
120
|
+
"default_dry_run": True, # Safety-first for modifications
|
121
121
|
"requires_confirmation": True,
|
122
122
|
"simulation_mode": True,
|
123
|
-
"warning_message": "⚠️ RESOURCE MODIFICATION: This will modify existing AWS resources"
|
123
|
+
"warning_message": "⚠️ RESOURCE MODIFICATION: This will modify existing AWS resources",
|
124
124
|
},
|
125
125
|
OperationType.RESOURCE_DELETE: {
|
126
|
-
"default_dry_run": True,
|
126
|
+
"default_dry_run": True, # Safety-first for deletion
|
127
127
|
"requires_confirmation": True,
|
128
128
|
"simulation_mode": True,
|
129
|
-
"warning_message": "🚨 RESOURCE DELETION: This will permanently delete AWS resources"
|
129
|
+
"warning_message": "🚨 RESOURCE DELETION: This will permanently delete AWS resources",
|
130
130
|
},
|
131
131
|
OperationType.REMEDIATION: {
|
132
|
-
"default_dry_run": True,
|
132
|
+
"default_dry_run": True, # Safety-first for remediation
|
133
133
|
"requires_confirmation": True,
|
134
134
|
"simulation_mode": True,
|
135
|
-
"warning_message": "🔧 SECURITY REMEDIATION: This will apply security fixes to resources"
|
135
|
+
"warning_message": "🔧 SECURITY REMEDIATION: This will apply security fixes to resources",
|
136
136
|
},
|
137
137
|
OperationType.BULK_OPERATIONS: {
|
138
|
-
"default_dry_run": True,
|
138
|
+
"default_dry_run": True, # Safety-first for bulk operations
|
139
139
|
"requires_confirmation": True,
|
140
140
|
"simulation_mode": True,
|
141
|
-
"warning_message": "🔥 BULK OPERATION: This will affect multiple resources simultaneously"
|
141
|
+
"warning_message": "🔥 BULK OPERATION: This will affect multiple resources simultaneously",
|
142
142
|
},
|
143
143
|
OperationType.CROSS_ACCOUNT: {
|
144
|
-
"default_dry_run": True,
|
144
|
+
"default_dry_run": True, # Safety-first for cross-account
|
145
145
|
"requires_confirmation": True,
|
146
146
|
"simulation_mode": True,
|
147
|
-
"warning_message": "🌐 CROSS-ACCOUNT OPERATION: This will affect multiple AWS accounts"
|
147
|
+
"warning_message": "🌐 CROSS-ACCOUNT OPERATION: This will affect multiple AWS accounts",
|
148
148
|
},
|
149
149
|
OperationType.FINANCIAL: {
|
150
|
-
"default_dry_run": True,
|
150
|
+
"default_dry_run": True, # Safety-first for financial operations
|
151
151
|
"requires_confirmation": True,
|
152
152
|
"simulation_mode": True,
|
153
|
-
"warning_message": "💰 FINANCIAL OPERATION: This will modify budgets or billing configurations"
|
154
|
-
}
|
153
|
+
"warning_message": "💰 FINANCIAL OPERATION: This will modify budgets or billing configurations",
|
154
|
+
},
|
155
155
|
}
|
156
|
-
|
156
|
+
|
157
157
|
def create_context(
|
158
158
|
self,
|
159
159
|
dry_run: bool,
|
@@ -161,11 +161,11 @@ class DryRunSafetyFramework:
|
|
161
161
|
module_name: str,
|
162
162
|
operation_name: str,
|
163
163
|
target_resources: Optional[List[str]] = None,
|
164
|
-
estimated_impact: Optional[str] = None
|
164
|
+
estimated_impact: Optional[str] = None,
|
165
165
|
) -> DryRunContext:
|
166
166
|
"""
|
167
167
|
Create a dry-run context for an operation.
|
168
|
-
|
168
|
+
|
169
169
|
Args:
|
170
170
|
dry_run: User-specified dry-run flag
|
171
171
|
operation_type: Type of operation being performed
|
@@ -173,25 +173,25 @@ class DryRunSafetyFramework:
|
|
173
173
|
operation_name: Specific operation name
|
174
174
|
target_resources: List of resources that will be affected
|
175
175
|
estimated_impact: Human-readable impact description
|
176
|
-
|
176
|
+
|
177
177
|
Returns:
|
178
178
|
DryRunContext with appropriate safety settings
|
179
179
|
"""
|
180
180
|
config = self.safety_configs.get(operation_type, self.safety_configs[OperationType.RESOURCE_MODIFY])
|
181
|
-
|
181
|
+
|
182
182
|
# Determine actual dry-run state
|
183
183
|
if dry_run is None:
|
184
184
|
actual_dry_run = config["default_dry_run"]
|
185
185
|
else:
|
186
186
|
actual_dry_run = dry_run
|
187
|
-
|
187
|
+
|
188
188
|
# Determine safety level
|
189
189
|
safety_level = "standard"
|
190
190
|
if operation_type in [OperationType.RESOURCE_DELETE, OperationType.BULK_OPERATIONS]:
|
191
191
|
safety_level = "high"
|
192
192
|
elif operation_type in [OperationType.CROSS_ACCOUNT, OperationType.FINANCIAL]:
|
193
193
|
safety_level = "critical"
|
194
|
-
|
194
|
+
|
195
195
|
context = DryRunContext(
|
196
196
|
enabled=actual_dry_run,
|
197
197
|
operation_type=operation_type,
|
@@ -200,29 +200,33 @@ class DryRunSafetyFramework:
|
|
200
200
|
target_resources=target_resources or [],
|
201
201
|
estimated_impact=estimated_impact,
|
202
202
|
safety_level=safety_level,
|
203
|
-
requires_confirmation=config["requires_confirmation"] and not actual_dry_run
|
203
|
+
requires_confirmation=config["requires_confirmation"] and not actual_dry_run,
|
204
204
|
)
|
205
|
-
|
205
|
+
|
206
206
|
# Log context creation
|
207
|
-
self._add_audit_entry(
|
208
|
-
|
209
|
-
"
|
210
|
-
|
211
|
-
|
212
|
-
|
207
|
+
self._add_audit_entry(
|
208
|
+
context,
|
209
|
+
"context_created",
|
210
|
+
{
|
211
|
+
"dry_run_enabled": actual_dry_run,
|
212
|
+
"safety_level": safety_level,
|
213
|
+
"requires_confirmation": context.requires_confirmation,
|
214
|
+
},
|
215
|
+
)
|
216
|
+
|
213
217
|
return context
|
214
|
-
|
218
|
+
|
215
219
|
def display_dry_run_banner(self, context: DryRunContext) -> None:
|
216
220
|
"""
|
217
221
|
Display appropriate dry-run banner based on operation type.
|
218
|
-
|
222
|
+
|
219
223
|
Args:
|
220
224
|
context: Dry-run context with operation details
|
221
225
|
"""
|
222
226
|
if context.enabled:
|
223
227
|
# Dry-run mode banner
|
224
228
|
title = f"{STATUS_INDICATORS['info']} DRY-RUN MODE ENABLED"
|
225
|
-
|
229
|
+
|
226
230
|
if context.operation_type in [OperationType.DISCOVERY, OperationType.ANALYSIS, OperationType.ASSESSMENT]:
|
227
231
|
message = f"[cyan]Simulation mode: No AWS API calls will be made[/cyan]\n"
|
228
232
|
message += f"[dim]Operation: {context.module_name} {context.operation_name}[/dim]"
|
@@ -231,262 +235,255 @@ class DryRunSafetyFramework:
|
|
231
235
|
message += f"[dim]Operation: {context.module_name} {context.operation_name}[/dim]\n"
|
232
236
|
if context.target_resources:
|
233
237
|
message += f"[dim]Target resources: {len(context.target_resources)} items[/dim]"
|
234
|
-
|
235
|
-
panel = Panel(
|
236
|
-
|
237
|
-
title=title,
|
238
|
-
border_style="cyan",
|
239
|
-
title_align="left"
|
240
|
-
)
|
241
|
-
|
238
|
+
|
239
|
+
panel = Panel(message, title=title, border_style="cyan", title_align="left")
|
240
|
+
|
242
241
|
else:
|
243
242
|
# Live mode banner with warnings
|
244
243
|
config = self.safety_configs.get(context.operation_type)
|
245
244
|
if config and config.get("warning_message"):
|
246
245
|
title = f"{STATUS_INDICATORS['warning']} LIVE MODE - CHANGES WILL BE APPLIED"
|
247
|
-
|
246
|
+
|
248
247
|
message = f"[red]{config['warning_message']}[/red]\n"
|
249
248
|
message += f"[dim]Operation: {context.module_name} {context.operation_name}[/dim]"
|
250
249
|
if context.estimated_impact:
|
251
250
|
message += f"\n[yellow]Estimated impact: {context.estimated_impact}[/yellow]"
|
252
|
-
|
253
|
-
panel = Panel(
|
254
|
-
message,
|
255
|
-
title=title,
|
256
|
-
border_style="red",
|
257
|
-
title_align="left"
|
258
|
-
)
|
251
|
+
|
252
|
+
panel = Panel(message, title=title, border_style="red", title_align="left")
|
259
253
|
else:
|
260
254
|
# Standard live mode for read-only operations
|
261
255
|
title = f"{STATUS_INDICATORS['success']} LIVE MODE - REAL DATA ANALYSIS"
|
262
256
|
message = f"[green]Real AWS API calls will be made for analysis[/green]\n"
|
263
257
|
message += f"[dim]Operation: {context.module_name} {context.operation_name}[/dim]"
|
264
|
-
|
265
|
-
panel = Panel(
|
266
|
-
|
267
|
-
title=title,
|
268
|
-
border_style="green",
|
269
|
-
title_align="left"
|
270
|
-
)
|
271
|
-
|
258
|
+
|
259
|
+
panel = Panel(message, title=title, border_style="green", title_align="left")
|
260
|
+
|
272
261
|
self.console.print(panel)
|
273
262
|
self.console.print() # Add spacing
|
274
|
-
|
263
|
+
|
275
264
|
def confirm_operation(self, context: DryRunContext) -> bool:
|
276
265
|
"""
|
277
266
|
Request confirmation for operations that require it.
|
278
|
-
|
267
|
+
|
279
268
|
Args:
|
280
269
|
context: Dry-run context
|
281
|
-
|
270
|
+
|
282
271
|
Returns:
|
283
272
|
True if user confirms, False otherwise
|
284
273
|
"""
|
285
274
|
if not context.requires_confirmation:
|
286
275
|
return True
|
287
|
-
|
276
|
+
|
288
277
|
# Show operation details
|
289
278
|
table = Table(title="Operation Confirmation Required")
|
290
279
|
table.add_column("Property", style="cyan")
|
291
280
|
table.add_column("Value", style="white")
|
292
|
-
|
281
|
+
|
293
282
|
table.add_row("Module", context.module_name)
|
294
283
|
table.add_row("Operation", context.operation_name)
|
295
284
|
table.add_row("Safety Level", context.safety_level.upper())
|
296
|
-
|
285
|
+
|
297
286
|
if context.target_resources:
|
298
287
|
table.add_row("Resources Affected", str(len(context.target_resources)))
|
299
|
-
|
288
|
+
|
300
289
|
if context.estimated_impact:
|
301
290
|
table.add_row("Estimated Impact", context.estimated_impact)
|
302
|
-
|
291
|
+
|
303
292
|
self.console.print(table)
|
304
293
|
self.console.print()
|
305
|
-
|
294
|
+
|
306
295
|
# Request confirmation
|
307
296
|
try:
|
308
297
|
import click
|
298
|
+
|
309
299
|
confirmed = click.confirm(
|
310
|
-
f"Are you sure you want to proceed with this {context.operation_type.value} operation?",
|
311
|
-
default=False
|
300
|
+
f"Are you sure you want to proceed with this {context.operation_type.value} operation?", default=False
|
312
301
|
)
|
313
302
|
except ImportError:
|
314
303
|
# Fallback for environments without click
|
315
|
-
response = input(
|
316
|
-
|
317
|
-
|
304
|
+
response = input(
|
305
|
+
f"Are you sure you want to proceed with this {context.operation_type.value} operation? [y/N]: "
|
306
|
+
)
|
307
|
+
confirmed = response.lower().startswith("y")
|
308
|
+
|
318
309
|
# Log confirmation
|
319
|
-
self._add_audit_entry(
|
320
|
-
"user_confirmed": confirmed,
|
321
|
-
|
322
|
-
|
323
|
-
|
310
|
+
self._add_audit_entry(
|
311
|
+
context, "confirmation_requested", {"user_confirmed": confirmed, "safety_level": context.safety_level}
|
312
|
+
)
|
313
|
+
|
324
314
|
if not confirmed:
|
325
315
|
print_warning("Operation cancelled by user")
|
326
|
-
|
316
|
+
|
327
317
|
return confirmed
|
328
|
-
|
318
|
+
|
329
319
|
def log_operation_start(self, context: DryRunContext, details: Optional[Dict[str, Any]] = None) -> None:
|
330
320
|
"""Log the start of an operation with full context."""
|
331
321
|
mode = "DRY-RUN" if context.enabled else "LIVE"
|
332
|
-
|
322
|
+
|
333
323
|
log_entry = {
|
334
324
|
"mode": mode,
|
335
325
|
"operation_type": context.operation_type.value,
|
336
326
|
"module": context.module_name,
|
337
327
|
"operation": context.operation_name,
|
338
328
|
"target_count": len(context.target_resources),
|
339
|
-
"safety_level": context.safety_level
|
329
|
+
"safety_level": context.safety_level,
|
340
330
|
}
|
341
|
-
|
331
|
+
|
342
332
|
if details:
|
343
333
|
log_entry.update(details)
|
344
|
-
|
334
|
+
|
345
335
|
self._add_audit_entry(context, "operation_started", log_entry)
|
346
|
-
|
336
|
+
|
347
337
|
# Console output
|
348
338
|
status = STATUS_INDICATORS.get("running", "🔄")
|
349
339
|
self.console.print(f"{status} Starting {mode} operation: {context.operation_name}")
|
350
|
-
|
340
|
+
|
351
341
|
def log_operation_complete(
|
352
342
|
self,
|
353
343
|
context: DryRunContext,
|
354
344
|
success: bool = True,
|
355
345
|
results: Optional[Dict[str, Any]] = None,
|
356
|
-
error: Optional[str] = None
|
346
|
+
error: Optional[str] = None,
|
357
347
|
) -> None:
|
358
348
|
"""Log the completion of an operation."""
|
359
349
|
mode = "DRY-RUN" if context.enabled else "LIVE"
|
360
|
-
|
350
|
+
|
361
351
|
log_entry = {
|
362
352
|
"mode": mode,
|
363
353
|
"success": success,
|
364
354
|
"duration": self._calculate_duration(context),
|
365
355
|
}
|
366
|
-
|
356
|
+
|
367
357
|
if results:
|
368
358
|
log_entry["results"] = results
|
369
|
-
|
359
|
+
|
370
360
|
if error:
|
371
361
|
log_entry["error"] = error
|
372
|
-
|
362
|
+
|
373
363
|
self._add_audit_entry(context, "operation_completed", log_entry)
|
374
|
-
|
364
|
+
|
375
365
|
# Console output
|
376
366
|
if success:
|
377
367
|
status = STATUS_INDICATORS.get("success", "✅")
|
378
368
|
print_success(f"Operation completed successfully in {mode} mode")
|
379
|
-
|
380
|
-
if context.enabled and context.operation_type not in [
|
369
|
+
|
370
|
+
if context.enabled and context.operation_type not in [
|
371
|
+
OperationType.DISCOVERY,
|
372
|
+
OperationType.ANALYSIS,
|
373
|
+
OperationType.ASSESSMENT,
|
374
|
+
]:
|
381
375
|
self.console.print(f"[dim]💡 To execute changes, run the same command with --no-dry-run[/dim]")
|
382
376
|
else:
|
383
377
|
status = STATUS_INDICATORS.get("error", "❌")
|
384
378
|
print_error(f"Operation failed in {mode} mode: {error}")
|
385
|
-
|
379
|
+
|
386
380
|
def _add_audit_entry(self, context: DryRunContext, event: str, data: Dict[str, Any]) -> None:
|
387
381
|
"""Add an entry to the audit trail."""
|
388
|
-
entry = {
|
389
|
-
"timestamp": datetime.utcnow().isoformat(),
|
390
|
-
"event": event,
|
391
|
-
"data": data
|
392
|
-
}
|
382
|
+
entry = {"timestamp": datetime.utcnow().isoformat(), "event": event, "data": data}
|
393
383
|
context.audit_trail.append(entry)
|
394
|
-
|
384
|
+
|
395
385
|
# Log to system logger
|
396
|
-
self.logger.info(
|
397
|
-
"
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
386
|
+
self.logger.info(
|
387
|
+
f"DryRun {event}",
|
388
|
+
extra={
|
389
|
+
"module": context.module_name,
|
390
|
+
"operation": context.operation_name,
|
391
|
+
"dry_run": context.enabled,
|
392
|
+
**data,
|
393
|
+
},
|
394
|
+
)
|
395
|
+
|
403
396
|
def _calculate_duration(self, context: DryRunContext) -> Optional[str]:
|
404
397
|
"""Calculate operation duration from audit trail."""
|
405
398
|
start_time = None
|
406
399
|
end_time = datetime.utcnow()
|
407
|
-
|
400
|
+
|
408
401
|
for entry in context.audit_trail:
|
409
402
|
if entry["event"] == "operation_started":
|
410
403
|
start_time = datetime.fromisoformat(entry["timestamp"])
|
411
404
|
break
|
412
|
-
|
405
|
+
|
413
406
|
if start_time:
|
414
407
|
duration = end_time - start_time
|
415
408
|
return f"{duration.total_seconds():.2f}s"
|
416
|
-
|
409
|
+
|
417
410
|
return None
|
418
411
|
|
419
412
|
|
420
413
|
def dry_run_operation(
|
421
|
-
operation_type: OperationType,
|
422
|
-
requires_confirmation: Optional[bool] = None,
|
423
|
-
estimated_impact: Optional[str] = None
|
414
|
+
operation_type: OperationType, requires_confirmation: Optional[bool] = None, estimated_impact: Optional[str] = None
|
424
415
|
):
|
425
416
|
"""
|
426
417
|
Decorator for operations that support dry-run mode.
|
427
|
-
|
418
|
+
|
428
419
|
Args:
|
429
420
|
operation_type: Type of operation for appropriate safety controls
|
430
421
|
requires_confirmation: Override default confirmation requirement
|
431
422
|
estimated_impact: Description of operation impact
|
432
|
-
|
423
|
+
|
433
424
|
Usage:
|
434
425
|
@dry_run_operation(OperationType.RESOURCE_DELETE, estimated_impact="Delete 5 VPCs")
|
435
426
|
def delete_vpcs(dry_run: bool = True, **kwargs):
|
436
427
|
# Function receives dry_run_context as first argument
|
437
428
|
pass
|
438
429
|
"""
|
430
|
+
|
439
431
|
def decorator(func: Callable) -> Callable:
|
440
432
|
@functools.wraps(func)
|
441
433
|
def wrapper(*args, **kwargs):
|
442
434
|
# Extract dry_run parameter
|
443
|
-
dry_run = kwargs.pop(
|
444
|
-
|
435
|
+
dry_run = kwargs.pop("dry_run", None)
|
436
|
+
|
445
437
|
# Get module and operation names
|
446
|
-
module_name =
|
438
|
+
module_name = (
|
439
|
+
getattr(func, "__module__", "unknown").split(".")[-2]
|
440
|
+
if "." in getattr(func, "__module__", "")
|
441
|
+
else "unknown"
|
442
|
+
)
|
447
443
|
operation_name = func.__name__
|
448
|
-
|
444
|
+
|
449
445
|
# Create dry-run framework instance
|
450
446
|
framework = DryRunSafetyFramework()
|
451
|
-
|
447
|
+
|
452
448
|
# Create context
|
453
449
|
context = framework.create_context(
|
454
450
|
dry_run=dry_run,
|
455
451
|
operation_type=operation_type,
|
456
452
|
module_name=module_name,
|
457
453
|
operation_name=operation_name,
|
458
|
-
estimated_impact=estimated_impact
|
454
|
+
estimated_impact=estimated_impact,
|
459
455
|
)
|
460
|
-
|
456
|
+
|
461
457
|
# Override confirmation requirement if specified
|
462
458
|
if requires_confirmation is not None:
|
463
459
|
context.requires_confirmation = requires_confirmation and not context.enabled
|
464
|
-
|
460
|
+
|
465
461
|
# Display banner
|
466
462
|
framework.display_dry_run_banner(context)
|
467
|
-
|
463
|
+
|
468
464
|
# Request confirmation if required
|
469
465
|
if not framework.confirm_operation(context):
|
470
466
|
return None
|
471
|
-
|
467
|
+
|
472
468
|
# Log operation start
|
473
469
|
framework.log_operation_start(context)
|
474
|
-
|
470
|
+
|
475
471
|
try:
|
476
472
|
# Call the original function with context as first argument
|
477
473
|
result = func(context, *args, **kwargs)
|
478
|
-
|
474
|
+
|
479
475
|
# Log success
|
480
476
|
framework.log_operation_complete(context, success=True, results={"completed": True})
|
481
|
-
|
477
|
+
|
482
478
|
return result
|
483
|
-
|
479
|
+
|
484
480
|
except Exception as e:
|
485
481
|
# Log failure
|
486
482
|
framework.log_operation_complete(context, success=False, error=str(e))
|
487
483
|
raise
|
488
|
-
|
484
|
+
|
489
485
|
return wrapper
|
486
|
+
|
490
487
|
return decorator
|
491
488
|
|
492
489
|
|
@@ -495,26 +492,31 @@ def discovery_operation(func: Callable) -> Callable:
|
|
495
492
|
"""Decorator for discovery operations (inventory, scan)."""
|
496
493
|
return dry_run_operation(OperationType.DISCOVERY)(func)
|
497
494
|
|
495
|
+
|
498
496
|
def analysis_operation(func: Callable) -> Callable:
|
499
497
|
"""Decorator for analysis operations (finops, security assess, vpc analyze)."""
|
500
498
|
return dry_run_operation(OperationType.ANALYSIS)(func)
|
501
499
|
|
500
|
+
|
502
501
|
def assessment_operation(func: Callable) -> Callable:
|
503
502
|
"""Decorator for assessment operations (cfat assess)."""
|
504
503
|
return dry_run_operation(OperationType.ASSESSMENT)(func)
|
505
504
|
|
505
|
+
|
506
506
|
def resource_creation_operation(estimated_impact: str = None):
|
507
507
|
"""Decorator for resource creation operations."""
|
508
508
|
return dry_run_operation(OperationType.RESOURCE_CREATE, estimated_impact=estimated_impact)
|
509
509
|
|
510
|
+
|
510
511
|
def resource_deletion_operation(estimated_impact: str = None):
|
511
512
|
"""Decorator for resource deletion operations."""
|
512
513
|
return dry_run_operation(OperationType.RESOURCE_DELETE, estimated_impact=estimated_impact)
|
513
514
|
|
515
|
+
|
514
516
|
def remediation_operation(estimated_impact: str = None):
|
515
517
|
"""Decorator for security remediation operations."""
|
516
518
|
return dry_run_operation(OperationType.REMEDIATION, estimated_impact=estimated_impact)
|
517
519
|
|
518
520
|
|
519
521
|
# Global framework instance for direct use
|
520
|
-
framework = DryRunSafetyFramework()
|
522
|
+
framework = DryRunSafetyFramework()
|