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.
Files changed (247) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/WEIGHT_CONFIG_README.md +1 -1
  8. runbooks/cfat/assessment/compliance.py +8 -8
  9. runbooks/cfat/assessment/runner.py +1 -0
  10. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  11. runbooks/cfat/models.py +6 -2
  12. runbooks/cfat/tests/__init__.py +6 -1
  13. runbooks/cli/__init__.py +13 -0
  14. runbooks/cli/commands/cfat.py +274 -0
  15. runbooks/cli/commands/finops.py +1164 -0
  16. runbooks/cli/commands/inventory.py +379 -0
  17. runbooks/cli/commands/operate.py +239 -0
  18. runbooks/cli/commands/security.py +248 -0
  19. runbooks/cli/commands/validation.py +825 -0
  20. runbooks/cli/commands/vpc.py +310 -0
  21. runbooks/cli/registry.py +107 -0
  22. runbooks/cloudops/__init__.py +23 -30
  23. runbooks/cloudops/base.py +96 -107
  24. runbooks/cloudops/cost_optimizer.py +549 -547
  25. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  26. runbooks/cloudops/interfaces.py +226 -227
  27. runbooks/cloudops/lifecycle_manager.py +5 -4
  28. runbooks/cloudops/mcp_cost_validation.py +252 -235
  29. runbooks/cloudops/models.py +78 -53
  30. runbooks/cloudops/monitoring_automation.py +5 -4
  31. runbooks/cloudops/notebook_framework.py +179 -215
  32. runbooks/cloudops/security_enforcer.py +125 -159
  33. runbooks/common/accuracy_validator.py +11 -0
  34. runbooks/common/aws_pricing.py +349 -326
  35. runbooks/common/aws_pricing_api.py +211 -212
  36. runbooks/common/aws_profile_manager.py +341 -0
  37. runbooks/common/aws_utils.py +75 -80
  38. runbooks/common/business_logic.py +127 -105
  39. runbooks/common/cli_decorators.py +36 -60
  40. runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
  41. runbooks/common/cross_account_manager.py +198 -205
  42. runbooks/common/date_utils.py +27 -39
  43. runbooks/common/decorators.py +235 -0
  44. runbooks/common/dry_run_examples.py +173 -208
  45. runbooks/common/dry_run_framework.py +157 -155
  46. runbooks/common/enhanced_exception_handler.py +15 -4
  47. runbooks/common/enhanced_logging_example.py +50 -64
  48. runbooks/common/enhanced_logging_integration_example.py +65 -37
  49. runbooks/common/env_utils.py +16 -16
  50. runbooks/common/error_handling.py +40 -38
  51. runbooks/common/lazy_loader.py +41 -23
  52. runbooks/common/logging_integration_helper.py +79 -86
  53. runbooks/common/mcp_cost_explorer_integration.py +478 -495
  54. runbooks/common/mcp_integration.py +63 -74
  55. runbooks/common/memory_optimization.py +140 -118
  56. runbooks/common/module_cli_base.py +37 -58
  57. runbooks/common/organizations_client.py +176 -194
  58. runbooks/common/patterns.py +204 -0
  59. runbooks/common/performance_monitoring.py +67 -71
  60. runbooks/common/performance_optimization_engine.py +283 -274
  61. runbooks/common/profile_utils.py +248 -39
  62. runbooks/common/rich_utils.py +643 -92
  63. runbooks/common/sre_performance_suite.py +177 -186
  64. runbooks/enterprise/__init__.py +1 -1
  65. runbooks/enterprise/logging.py +144 -106
  66. runbooks/enterprise/security.py +187 -204
  67. runbooks/enterprise/validation.py +43 -56
  68. runbooks/finops/__init__.py +29 -33
  69. runbooks/finops/account_resolver.py +1 -1
  70. runbooks/finops/advanced_optimization_engine.py +980 -0
  71. runbooks/finops/automation_core.py +268 -231
  72. runbooks/finops/business_case_config.py +184 -179
  73. runbooks/finops/cli.py +660 -139
  74. runbooks/finops/commvault_ec2_analysis.py +157 -164
  75. runbooks/finops/compute_cost_optimizer.py +336 -320
  76. runbooks/finops/config.py +20 -20
  77. runbooks/finops/cost_optimizer.py +488 -622
  78. runbooks/finops/cost_processor.py +332 -214
  79. runbooks/finops/dashboard_runner.py +1006 -172
  80. runbooks/finops/ebs_cost_optimizer.py +991 -657
  81. runbooks/finops/elastic_ip_optimizer.py +317 -257
  82. runbooks/finops/enhanced_mcp_integration.py +340 -0
  83. runbooks/finops/enhanced_progress.py +40 -37
  84. runbooks/finops/enhanced_trend_visualization.py +3 -2
  85. runbooks/finops/enterprise_wrappers.py +230 -292
  86. runbooks/finops/executive_export.py +203 -160
  87. runbooks/finops/helpers.py +130 -288
  88. runbooks/finops/iam_guidance.py +1 -1
  89. runbooks/finops/infrastructure/__init__.py +80 -0
  90. runbooks/finops/infrastructure/commands.py +506 -0
  91. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  92. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  93. runbooks/finops/markdown_exporter.py +338 -175
  94. runbooks/finops/mcp_validator.py +1952 -0
  95. runbooks/finops/nat_gateway_optimizer.py +1513 -482
  96. runbooks/finops/network_cost_optimizer.py +657 -587
  97. runbooks/finops/notebook_utils.py +226 -188
  98. runbooks/finops/optimization_engine.py +1136 -0
  99. runbooks/finops/optimizer.py +25 -29
  100. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  101. runbooks/finops/reservation_optimizer.py +427 -363
  102. runbooks/finops/scenario_cli_integration.py +77 -78
  103. runbooks/finops/scenarios.py +1278 -439
  104. runbooks/finops/schemas.py +218 -182
  105. runbooks/finops/snapshot_manager.py +2289 -0
  106. runbooks/finops/tests/test_finops_dashboard.py +3 -3
  107. runbooks/finops/tests/test_reference_images_validation.py +2 -2
  108. runbooks/finops/tests/test_single_account_features.py +17 -17
  109. runbooks/finops/tests/validate_test_suite.py +1 -1
  110. runbooks/finops/types.py +3 -3
  111. runbooks/finops/validation_framework.py +263 -269
  112. runbooks/finops/vpc_cleanup_exporter.py +191 -146
  113. runbooks/finops/vpc_cleanup_optimizer.py +593 -575
  114. runbooks/finops/workspaces_analyzer.py +171 -182
  115. runbooks/hitl/enhanced_workflow_engine.py +1 -1
  116. runbooks/integration/__init__.py +89 -0
  117. runbooks/integration/mcp_integration.py +1920 -0
  118. runbooks/inventory/CLAUDE.md +816 -0
  119. runbooks/inventory/README.md +3 -3
  120. runbooks/inventory/Tests/common_test_data.py +30 -30
  121. runbooks/inventory/__init__.py +2 -2
  122. runbooks/inventory/cloud_foundations_integration.py +144 -149
  123. runbooks/inventory/collectors/aws_comprehensive.py +28 -11
  124. runbooks/inventory/collectors/aws_networking.py +111 -101
  125. runbooks/inventory/collectors/base.py +4 -0
  126. runbooks/inventory/core/collector.py +495 -313
  127. runbooks/inventory/discovery.md +2 -2
  128. runbooks/inventory/drift_detection_cli.py +69 -96
  129. runbooks/inventory/find_ec2_security_groups.py +1 -1
  130. runbooks/inventory/inventory_mcp_cli.py +48 -46
  131. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  132. runbooks/inventory/mcp_inventory_validator.py +549 -465
  133. runbooks/inventory/mcp_vpc_validator.py +359 -442
  134. runbooks/inventory/organizations_discovery.py +56 -52
  135. runbooks/inventory/rich_inventory_display.py +33 -32
  136. runbooks/inventory/unified_validation_engine.py +278 -251
  137. runbooks/inventory/vpc_analyzer.py +733 -696
  138. runbooks/inventory/vpc_architecture_validator.py +293 -348
  139. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  140. runbooks/inventory/vpc_flow_analyzer.py +3 -3
  141. runbooks/main.py +152 -9147
  142. runbooks/main_final.py +91 -60
  143. runbooks/main_minimal.py +22 -10
  144. runbooks/main_optimized.py +131 -100
  145. runbooks/main_ultra_minimal.py +7 -2
  146. runbooks/mcp/__init__.py +36 -0
  147. runbooks/mcp/integration.py +679 -0
  148. runbooks/metrics/dora_metrics_engine.py +2 -2
  149. runbooks/monitoring/performance_monitor.py +9 -4
  150. runbooks/operate/dynamodb_operations.py +3 -1
  151. runbooks/operate/ec2_operations.py +145 -137
  152. runbooks/operate/iam_operations.py +146 -152
  153. runbooks/operate/mcp_integration.py +1 -1
  154. runbooks/operate/networking_cost_heatmap.py +33 -10
  155. runbooks/operate/privatelink_operations.py +1 -1
  156. runbooks/operate/rds_operations.py +223 -254
  157. runbooks/operate/s3_operations.py +107 -118
  158. runbooks/operate/vpc_endpoints.py +1 -1
  159. runbooks/operate/vpc_operations.py +648 -618
  160. runbooks/remediation/base.py +1 -1
  161. runbooks/remediation/commons.py +10 -7
  162. runbooks/remediation/commvault_ec2_analysis.py +71 -67
  163. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  164. runbooks/remediation/multi_account.py +24 -21
  165. runbooks/remediation/rds_snapshot_list.py +91 -65
  166. runbooks/remediation/remediation_cli.py +92 -146
  167. runbooks/remediation/universal_account_discovery.py +83 -79
  168. runbooks/remediation/workspaces_list.py +49 -44
  169. runbooks/security/__init__.py +19 -0
  170. runbooks/security/assessment_runner.py +1150 -0
  171. runbooks/security/baseline_checker.py +812 -0
  172. runbooks/security/cloudops_automation_security_validator.py +509 -535
  173. runbooks/security/compliance_automation_engine.py +17 -17
  174. runbooks/security/config/__init__.py +2 -2
  175. runbooks/security/config/compliance_config.py +50 -50
  176. runbooks/security/config_template_generator.py +63 -76
  177. runbooks/security/enterprise_security_framework.py +1 -1
  178. runbooks/security/executive_security_dashboard.py +519 -508
  179. runbooks/security/integration_test_enterprise_security.py +5 -3
  180. runbooks/security/multi_account_security_controls.py +959 -1210
  181. runbooks/security/real_time_security_monitor.py +422 -444
  182. runbooks/security/run_script.py +1 -1
  183. runbooks/security/security_baseline_tester.py +1 -1
  184. runbooks/security/security_cli.py +143 -112
  185. runbooks/security/test_2way_validation.py +439 -0
  186. runbooks/security/two_way_validation_framework.py +852 -0
  187. runbooks/sre/mcp_reliability_engine.py +6 -6
  188. runbooks/sre/production_monitoring_framework.py +167 -177
  189. runbooks/tdd/__init__.py +15 -0
  190. runbooks/tdd/cli.py +1071 -0
  191. runbooks/utils/__init__.py +14 -17
  192. runbooks/utils/logger.py +7 -2
  193. runbooks/utils/version_validator.py +51 -48
  194. runbooks/validation/__init__.py +6 -6
  195. runbooks/validation/cli.py +9 -3
  196. runbooks/validation/comprehensive_2way_validator.py +754 -708
  197. runbooks/validation/mcp_validator.py +906 -228
  198. runbooks/validation/terraform_citations_validator.py +104 -115
  199. runbooks/validation/terraform_drift_detector.py +447 -451
  200. runbooks/vpc/README.md +617 -0
  201. runbooks/vpc/__init__.py +8 -1
  202. runbooks/vpc/analyzer.py +577 -0
  203. runbooks/vpc/cleanup_wrapper.py +476 -413
  204. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  205. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  206. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  207. runbooks/vpc/config.py +92 -97
  208. runbooks/vpc/cost_engine.py +411 -148
  209. runbooks/vpc/cost_explorer_integration.py +553 -0
  210. runbooks/vpc/cross_account_session.py +101 -106
  211. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  212. runbooks/vpc/eni_gate_validator.py +961 -0
  213. runbooks/vpc/heatmap_engine.py +190 -162
  214. runbooks/vpc/mcp_no_eni_validator.py +681 -640
  215. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  216. runbooks/vpc/networking_wrapper.py +15 -8
  217. runbooks/vpc/pdca_remediation_planner.py +528 -0
  218. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  219. runbooks/vpc/runbooks_adapter.py +1167 -241
  220. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  221. runbooks/vpc/test_data_loader.py +358 -0
  222. runbooks/vpc/tests/conftest.py +314 -4
  223. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  224. runbooks/vpc/tests/test_cost_engine.py +0 -2
  225. runbooks/vpc/topology_generator.py +326 -0
  226. runbooks/vpc/unified_scenarios.py +1302 -1129
  227. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  228. runbooks-1.1.5.dist-info/METADATA +328 -0
  229. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
  230. runbooks/finops/README.md +0 -414
  231. runbooks/finops/accuracy_cross_validator.py +0 -647
  232. runbooks/finops/business_cases.py +0 -950
  233. runbooks/finops/dashboard_router.py +0 -922
  234. runbooks/finops/ebs_optimizer.py +0 -956
  235. runbooks/finops/embedded_mcp_validator.py +0 -1629
  236. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  237. runbooks/finops/finops_dashboard.py +0 -584
  238. runbooks/finops/finops_scenarios.py +0 -1218
  239. runbooks/finops/legacy_migration.py +0 -730
  240. runbooks/finops/multi_dashboard.py +0 -1519
  241. runbooks/finops/single_dashboard.py +0 -1113
  242. runbooks/finops/unlimited_scenarios.py +0 -393
  243. runbooks-1.1.3.dist-info/METADATA +0 -799
  244. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  245. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  246. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  247. {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" # 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
-
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" # 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
-
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" # Multi-resource operations
58
- CROSS_ACCOUNT = "cross_account" # Operations affecting multiple accounts
59
- FINANCIAL = "financial" # Budget modifications, billing changes
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, # Can simulate API calls
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, # Safety-first for resource creation
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, # Safety-first for modifications
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, # Safety-first for deletion
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, # Safety-first for remediation
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, # Safety-first for bulk operations
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, # Safety-first for cross-account
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, # Safety-first for financial operations
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(context, "context_created", {
208
- "dry_run_enabled": actual_dry_run,
209
- "safety_level": safety_level,
210
- "requires_confirmation": context.requires_confirmation
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
- message,
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
- message,
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(f"Are you sure you want to proceed with this {context.operation_type.value} operation? [y/N]: ")
316
- confirmed = response.lower().startswith('y')
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(context, "confirmation_requested", {
320
- "user_confirmed": confirmed,
321
- "safety_level": context.safety_level
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 [OperationType.DISCOVERY, OperationType.ANALYSIS, OperationType.ASSESSMENT]:
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(f"DryRun {event}", extra={
397
- "module": context.module_name,
398
- "operation": context.operation_name,
399
- "dry_run": context.enabled,
400
- **data
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('dry_run', None)
444
-
435
+ dry_run = kwargs.pop("dry_run", None)
436
+
445
437
  # Get module and operation names
446
- module_name = getattr(func, '__module__', 'unknown').split('.')[-2] if '.' in getattr(func, '__module__', '') else 'unknown'
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()