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
@@ -27,7 +27,7 @@ from runbooks.common.dry_run_framework import (
27
27
  resource_creation_operation,
28
28
  resource_deletion_operation,
29
29
  remediation_operation,
30
- framework
30
+ framework,
31
31
  )
32
32
  from runbooks.common.rich_utils import console, print_success, print_warning, print_error
33
33
 
@@ -36,64 +36,65 @@ from runbooks.common.rich_utils import console, print_success, print_warning, pr
36
36
  # 1. DISCOVERY OPERATIONS (inventory, scan modules)
37
37
  # =============================================================================
38
38
 
39
+
39
40
  @discovery_operation
40
41
  def collect_ec2_instances(
41
- dry_run_context: DryRunContext,
42
- profile: Optional[str] = None,
43
- regions: Optional[List[str]] = None
42
+ dry_run_context: DryRunContext, profile: Optional[str] = None, regions: Optional[List[str]] = None
44
43
  ) -> Dict[str, Any]:
45
44
  """
46
45
  Example discovery operation - EC2 instance collection.
47
-
46
+
48
47
  Discovery operations are inherently safe, so dry-run simulates API calls
49
48
  for testing purposes only.
50
49
  """
51
50
  if dry_run_context.enabled:
52
51
  # Simulation mode - no real API calls
53
52
  console.print("[dim]🔄 Simulating EC2 instance discovery...[/dim]")
54
-
53
+
55
54
  # Return simulated data
56
55
  return {
57
56
  "instances": [
58
57
  {"id": "i-sim123", "type": "t3.micro", "state": "running"},
59
- {"id": "i-sim456", "type": "m5.large", "state": "stopped"}
58
+ {"id": "i-sim456", "type": "m5.large", "state": "stopped"},
60
59
  ],
61
60
  "simulated": True,
62
61
  "region_count": len(regions or ["us-east-1"]),
63
- "total_discovered": 2
62
+ "total_discovered": 2,
64
63
  }
65
-
64
+
66
65
  else:
67
66
  # Real discovery operation
68
67
  console.print("[cyan]🔍 Discovering EC2 instances across regions...[/cyan]")
69
-
68
+
70
69
  session = boto3.Session(profile_name=profile)
71
70
  instances = []
72
-
71
+
73
72
  for region in regions or ["us-east-1"]:
74
73
  try:
75
- ec2 = session.client('ec2', region_name=region)
74
+ ec2 = session.client("ec2", region_name=region)
76
75
  response = ec2.describe_instances()
77
-
78
- for reservation in response['Reservations']:
79
- for instance in reservation['Instances']:
80
- instances.append({
81
- "id": instance['InstanceId'],
82
- "type": instance['InstanceType'],
83
- "state": instance['State']['Name'],
84
- "region": region
85
- })
86
-
76
+
77
+ for reservation in response["Reservations"]:
78
+ for instance in reservation["Instances"]:
79
+ instances.append(
80
+ {
81
+ "id": instance["InstanceId"],
82
+ "type": instance["InstanceType"],
83
+ "state": instance["State"]["Name"],
84
+ "region": region,
85
+ }
86
+ )
87
+
87
88
  except ClientError as e:
88
89
  print_warning(f"Could not access region {region}: {e}")
89
-
90
+
90
91
  print_success(f"Discovered {len(instances)} EC2 instances")
91
-
92
+
92
93
  return {
93
94
  "instances": instances,
94
95
  "simulated": False,
95
96
  "region_count": len(regions or ["us-east-1"]),
96
- "total_discovered": len(instances)
97
+ "total_discovered": len(instances),
97
98
  }
98
99
 
99
100
 
@@ -101,15 +102,14 @@ def collect_ec2_instances(
101
102
  # 2. ANALYSIS OPERATIONS (finops, security assess, vpc analyze modules)
102
103
  # =============================================================================
103
104
 
105
+
104
106
  @analysis_operation
105
107
  def analyze_cost_optimization(
106
- dry_run_context: DryRunContext,
107
- profile: Optional[str] = None,
108
- account_id: Optional[str] = None
108
+ dry_run_context: DryRunContext, profile: Optional[str] = None, account_id: Optional[str] = None
109
109
  ) -> Dict[str, Any]:
110
110
  """
111
111
  Example analysis operation - cost optimization analysis.
112
-
112
+
113
113
  Analysis operations are read-only, so dry-run shows what would be analyzed
114
114
  without making API calls.
115
115
  """
@@ -119,35 +119,31 @@ def analyze_cost_optimization(
119
119
  console.print(f"[dim] • Account: {account_id or 'Current account'}[/dim]")
120
120
  console.print(f"[dim] • Services: EC2, RDS, S3, Lambda[/dim]")
121
121
  console.print(f"[dim] • Time range: Last 30 days[/dim]")
122
-
122
+
123
123
  return {
124
124
  "preview": True,
125
- "scope": {
126
- "account_id": account_id,
127
- "services": ["EC2", "RDS", "S3", "Lambda"],
128
- "time_range": "30 days"
129
- }
125
+ "scope": {"account_id": account_id, "services": ["EC2", "RDS", "S3", "Lambda"], "time_range": "30 days"},
130
126
  }
131
-
127
+
132
128
  else:
133
129
  # Real cost analysis
134
130
  console.print("[green]💰 Analyzing cost optimization opportunities...[/green]")
135
-
131
+
136
132
  session = boto3.Session(profile_name=profile)
137
- cost_explorer = session.client('ce')
138
-
133
+ cost_explorer = session.client("ce")
134
+
139
135
  # Real cost analysis logic here
140
136
  # This is a simplified example
141
-
137
+
142
138
  print_success("Cost analysis completed")
143
-
139
+
144
140
  return {
145
141
  "analysis_complete": True,
146
142
  "recommendations": [
147
143
  {"service": "EC2", "potential_savings": "$150/month"},
148
- {"service": "RDS", "potential_savings": "$75/month"}
144
+ {"service": "RDS", "potential_savings": "$75/month"},
149
145
  ],
150
- "total_potential_savings": "$225/month"
146
+ "total_potential_savings": "$225/month",
151
147
  }
152
148
 
153
149
 
@@ -155,51 +151,46 @@ def analyze_cost_optimization(
155
151
  # 3. ASSESSMENT OPERATIONS (cfat assess module)
156
152
  # =============================================================================
157
153
 
154
+
158
155
  @assessment_operation
159
156
  def assess_security_compliance(
160
- dry_run_context: DryRunContext,
161
- profile: Optional[str] = None,
162
- frameworks: Optional[List[str]] = None
157
+ dry_run_context: DryRunContext, profile: Optional[str] = None, frameworks: Optional[List[str]] = None
163
158
  ) -> Dict[str, Any]:
164
159
  """
165
160
  Example assessment operation - security compliance assessment.
166
-
161
+
167
162
  Assessment operations are read-only, so dry-run shows assessment scope.
168
163
  """
169
164
  frameworks = frameworks or ["SOC2", "PCI-DSS", "HIPAA"]
170
-
165
+
171
166
  if dry_run_context.enabled:
172
167
  # Preview mode - show assessment scope
173
168
  console.print("[dim]🔍 Preview: Security assessment scope[/dim]")
174
169
  console.print(f"[dim] • Frameworks: {', '.join(frameworks)}[/dim]")
175
170
  console.print(f"[dim] • Services to check: IAM, S3, EC2, VPC[/dim]")
176
171
  console.print(f"[dim] • Estimated duration: 5-10 minutes[/dim]")
177
-
178
- return {
179
- "preview": True,
180
- "frameworks": frameworks,
181
- "estimated_checks": 45
182
- }
183
-
172
+
173
+ return {"preview": True, "frameworks": frameworks, "estimated_checks": 45}
174
+
184
175
  else:
185
176
  # Real security assessment
186
177
  console.print("[blue]🔒 Conducting security compliance assessment...[/blue]")
187
-
178
+
188
179
  session = boto3.Session(profile_name=profile)
189
-
180
+
190
181
  # Real assessment logic here
191
182
  # This is a simplified example
192
-
183
+
193
184
  results = {
194
185
  "frameworks_assessed": frameworks,
195
186
  "total_checks": 45,
196
187
  "passed": 38,
197
188
  "failed": 7,
198
- "compliance_score": "84.4%"
189
+ "compliance_score": "84.4%",
199
190
  }
200
-
191
+
201
192
  print_success(f"Assessment completed - Compliance score: {results['compliance_score']}")
202
-
193
+
203
194
  return results
204
195
 
205
196
 
@@ -207,16 +198,17 @@ def assess_security_compliance(
207
198
  # 4. RESOURCE CREATION OPERATIONS (operate module)
208
199
  # =============================================================================
209
200
 
201
+
210
202
  @resource_creation_operation(estimated_impact="Create 1 EC2 instance (~$25/month)")
211
203
  def create_ec2_instance(
212
204
  dry_run_context: DryRunContext,
213
205
  instance_type: str = "t3.micro",
214
206
  image_id: Optional[str] = None,
215
- profile: Optional[str] = None
207
+ profile: Optional[str] = None,
216
208
  ) -> Dict[str, Any]:
217
209
  """
218
210
  Example resource creation operation - EC2 instance creation.
219
-
211
+
220
212
  Resource creation operations default to dry-run for safety.
221
213
  """
222
214
  if dry_run_context.enabled:
@@ -225,35 +217,30 @@ def create_ec2_instance(
225
217
  console.print(f"[dim] • Instance type: {instance_type}[/dim]")
226
218
  console.print(f"[dim] • AMI ID: {image_id or 'Latest Amazon Linux 2'}[/dim]")
227
219
  console.print(f"[dim] • Estimated cost: ~$25/month[/dim]")
228
-
229
- return {
230
- "preview": True,
231
- "instance_type": instance_type,
232
- "image_id": image_id,
233
- "estimated_monthly_cost": 25.00
234
- }
235
-
220
+
221
+ return {"preview": True, "instance_type": instance_type, "image_id": image_id, "estimated_monthly_cost": 25.00}
222
+
236
223
  else:
237
224
  # Real instance creation
238
225
  console.print("[green]🚀 Creating EC2 instance...[/green]")
239
-
226
+
240
227
  session = boto3.Session(profile_name=profile)
241
- ec2 = session.client('ec2')
242
-
228
+ ec2 = session.client("ec2")
229
+
243
230
  # Use default AMI if not specified
244
231
  if not image_id:
245
232
  # Get latest Amazon Linux 2 AMI
246
233
  images = ec2.describe_images(
247
- Owners=['amazon'],
234
+ Owners=["amazon"],
248
235
  Filters=[
249
- {'Name': 'name', 'Values': ['amzn2-ami-hvm-*']},
250
- {'Name': 'architecture', 'Values': ['x86_64']},
251
- {'Name': 'virtualization-type', 'Values': ['hvm']}
252
- ]
236
+ {"Name": "name", "Values": ["amzn2-ami-hvm-*"]},
237
+ {"Name": "architecture", "Values": ["x86_64"]},
238
+ {"Name": "virtualization-type", "Values": ["hvm"]},
239
+ ],
253
240
  )
254
- if images['Images']:
255
- image_id = sorted(images['Images'], key=lambda x: x['CreationDate'], reverse=True)[0]['ImageId']
256
-
241
+ if images["Images"]:
242
+ image_id = sorted(images["Images"], key=lambda x: x["CreationDate"], reverse=True)[0]["ImageId"]
243
+
257
244
  try:
258
245
  response = ec2.run_instances(
259
246
  ImageId=image_id,
@@ -262,25 +249,20 @@ def create_ec2_instance(
262
249
  InstanceType=instance_type,
263
250
  TagSpecifications=[
264
251
  {
265
- 'ResourceType': 'instance',
266
- 'Tags': [
267
- {'Key': 'CreatedBy', 'Value': 'CloudOps-Runbooks'},
268
- {'Key': 'Purpose', 'Value': 'Testing'}
269
- ]
252
+ "ResourceType": "instance",
253
+ "Tags": [
254
+ {"Key": "CreatedBy", "Value": "CloudOps-Runbooks"},
255
+ {"Key": "Purpose", "Value": "Testing"},
256
+ ],
270
257
  }
271
- ]
258
+ ],
272
259
  )
273
-
274
- instance_id = response['Instances'][0]['InstanceId']
260
+
261
+ instance_id = response["Instances"][0]["InstanceId"]
275
262
  print_success(f"EC2 instance created: {instance_id}")
276
-
277
- return {
278
- "instance_id": instance_id,
279
- "instance_type": instance_type,
280
- "image_id": image_id,
281
- "created": True
282
- }
283
-
263
+
264
+ return {"instance_id": instance_id, "instance_type": instance_type, "image_id": image_id, "created": True}
265
+
284
266
  except ClientError as e:
285
267
  print_error(f"Failed to create instance: {e}")
286
268
  raise
@@ -290,15 +272,14 @@ def create_ec2_instance(
290
272
  # 5. RESOURCE DELETION OPERATIONS (operate module)
291
273
  # =============================================================================
292
274
 
275
+
293
276
  @resource_deletion_operation(estimated_impact="Delete EC2 instances (~$150/month savings)")
294
277
  def terminate_ec2_instances(
295
- dry_run_context: DryRunContext,
296
- instance_ids: List[str],
297
- profile: Optional[str] = None
278
+ dry_run_context: DryRunContext, instance_ids: List[str], profile: Optional[str] = None
298
279
  ) -> Dict[str, Any]:
299
280
  """
300
281
  Example resource deletion operation - EC2 instance termination.
301
-
282
+
302
283
  Resource deletion operations default to dry-run and require confirmation.
303
284
  """
304
285
  if dry_run_context.enabled:
@@ -308,40 +289,38 @@ def terminate_ec2_instances(
308
289
  console.print(f"[dim] • Instance IDs: {', '.join(instance_ids)}[/dim]")
309
290
  console.print(f"[dim] • Estimated savings: ~$150/month[/dim]")
310
291
  console.print(f"[dim] • ⚠️ THIS OPERATION IS IRREVERSIBLE[/dim]")
311
-
292
+
312
293
  return {
313
294
  "preview": True,
314
295
  "instances_to_terminate": instance_ids,
315
296
  "estimated_savings": 150.00,
316
- "irreversible": True
297
+ "irreversible": True,
317
298
  }
318
-
299
+
319
300
  else:
320
301
  # Real instance termination
321
302
  console.print("[red]💥 Terminating EC2 instances...[/red]")
322
-
303
+
323
304
  session = boto3.Session(profile_name=profile)
324
- ec2 = session.client('ec2')
325
-
305
+ ec2 = session.client("ec2")
306
+
326
307
  try:
327
308
  response = ec2.terminate_instances(InstanceIds=instance_ids)
328
-
309
+
329
310
  terminated = []
330
- for instance in response['TerminatingInstances']:
331
- terminated.append({
332
- 'instance_id': instance['InstanceId'],
333
- 'current_state': instance['CurrentState']['Name'],
334
- 'previous_state': instance['PreviousState']['Name']
335
- })
336
-
311
+ for instance in response["TerminatingInstances"]:
312
+ terminated.append(
313
+ {
314
+ "instance_id": instance["InstanceId"],
315
+ "current_state": instance["CurrentState"]["Name"],
316
+ "previous_state": instance["PreviousState"]["Name"],
317
+ }
318
+ )
319
+
337
320
  print_success(f"Successfully initiated termination of {len(terminated)} instances")
338
-
339
- return {
340
- "terminated_instances": terminated,
341
- "count": len(terminated),
342
- "operation": "terminate"
343
- }
344
-
321
+
322
+ return {"terminated_instances": terminated, "count": len(terminated), "operation": "terminate"}
323
+
345
324
  except ClientError as e:
346
325
  print_error(f"Failed to terminate instances: {e}")
347
326
  raise
@@ -351,15 +330,14 @@ def terminate_ec2_instances(
351
330
  # 6. SECURITY REMEDIATION OPERATIONS (remediation module)
352
331
  # =============================================================================
353
332
 
333
+
354
334
  @remediation_operation(estimated_impact="Fix S3 public buckets (security improvement)")
355
335
  def fix_public_s3_buckets(
356
- dry_run_context: DryRunContext,
357
- bucket_names: List[str],
358
- profile: Optional[str] = None
336
+ dry_run_context: DryRunContext, bucket_names: List[str], profile: Optional[str] = None
359
337
  ) -> Dict[str, Any]:
360
338
  """
361
339
  Example security remediation operation - fix public S3 buckets.
362
-
340
+
363
341
  Remediation operations default to dry-run and require confirmation.
364
342
  """
365
343
  if dry_run_context.enabled:
@@ -368,61 +346,55 @@ def fix_public_s3_buckets(
368
346
  console.print(f"[dim] • Buckets to secure: {len(bucket_names)}[/dim]")
369
347
  console.print(f"[dim] • Bucket names: {', '.join(bucket_names)}[/dim]")
370
348
  console.print(f"[dim] • Actions: Remove public access, update policies[/dim]")
371
-
349
+
372
350
  return {
373
351
  "preview": True,
374
352
  "buckets_to_fix": bucket_names,
375
353
  "remediation_actions": [
376
354
  "Remove public read access",
377
- "Remove public write access",
355
+ "Remove public write access",
378
356
  "Update bucket policies",
379
- "Enable access logging"
380
- ]
357
+ "Enable access logging",
358
+ ],
381
359
  }
382
-
360
+
383
361
  else:
384
362
  # Real security remediation
385
363
  console.print("[green]🔒 Applying security remediation to S3 buckets...[/green]")
386
-
364
+
387
365
  session = boto3.Session(profile_name=profile)
388
- s3 = session.client('s3')
389
-
366
+ s3 = session.client("s3")
367
+
390
368
  remediated_buckets = []
391
-
369
+
392
370
  for bucket_name in bucket_names:
393
371
  try:
394
372
  # Block public access
395
373
  s3.put_public_access_block(
396
374
  Bucket=bucket_name,
397
375
  PublicAccessBlockConfiguration={
398
- 'BlockPublicAcls': True,
399
- 'IgnorePublicAcls': True,
400
- 'BlockPublicPolicy': True,
401
- 'RestrictPublicBuckets': True
402
- }
376
+ "BlockPublicAcls": True,
377
+ "IgnorePublicAcls": True,
378
+ "BlockPublicPolicy": True,
379
+ "RestrictPublicBuckets": True,
380
+ },
403
381
  )
404
-
405
- remediated_buckets.append({
406
- 'bucket_name': bucket_name,
407
- 'status': 'secured',
408
- 'actions_applied': ['public_access_blocked']
409
- })
410
-
382
+
383
+ remediated_buckets.append(
384
+ {"bucket_name": bucket_name, "status": "secured", "actions_applied": ["public_access_blocked"]}
385
+ )
386
+
411
387
  except ClientError as e:
412
388
  print_warning(f"Could not secure bucket {bucket_name}: {e}")
413
- remediated_buckets.append({
414
- 'bucket_name': bucket_name,
415
- 'status': 'failed',
416
- 'error': str(e)
417
- })
418
-
419
- successful = len([b for b in remediated_buckets if b['status'] == 'secured'])
389
+ remediated_buckets.append({"bucket_name": bucket_name, "status": "failed", "error": str(e)})
390
+
391
+ successful = len([b for b in remediated_buckets if b["status"] == "secured"])
420
392
  print_success(f"Successfully secured {successful}/{len(bucket_names)} S3 buckets")
421
-
393
+
422
394
  return {
423
395
  "buckets_processed": remediated_buckets,
424
396
  "successful_count": successful,
425
- "total_count": len(bucket_names)
397
+ "total_count": len(bucket_names),
426
398
  }
427
399
 
428
400
 
@@ -430,14 +402,13 @@ def fix_public_s3_buckets(
430
402
  # 7. DIRECT FRAMEWORK USAGE (without decorators)
431
403
  # =============================================================================
432
404
 
405
+
433
406
  def custom_operation_with_framework(
434
- dry_run: bool = True,
435
- operation_name: str = "custom_operation",
436
- resources: Optional[List[str]] = None
407
+ dry_run: bool = True, operation_name: str = "custom_operation", resources: Optional[List[str]] = None
437
408
  ) -> Dict[str, Any]:
438
409
  """
439
410
  Example of using the dry-run framework directly without decorators.
440
-
411
+
441
412
  This approach gives you full control over the dry-run behavior
442
413
  and is useful for complex operations that need custom handling.
443
414
  """
@@ -448,19 +419,19 @@ def custom_operation_with_framework(
448
419
  module_name="example",
449
420
  operation_name=operation_name,
450
421
  target_resources=resources,
451
- estimated_impact="Moderate configuration changes"
422
+ estimated_impact="Moderate configuration changes",
452
423
  )
453
-
424
+
454
425
  # Display banner
455
426
  framework.display_dry_run_banner(context)
456
-
427
+
457
428
  # Request confirmation if needed
458
429
  if not framework.confirm_operation(context):
459
430
  return {"cancelled": True}
460
-
431
+
461
432
  # Log operation start
462
433
  framework.log_operation_start(context, {"custom_parameter": "example"})
463
-
434
+
464
435
  try:
465
436
  if context.enabled:
466
437
  # Dry-run logic
@@ -471,12 +442,12 @@ def custom_operation_with_framework(
471
442
  console.print("[green]⚡ Executing custom operation...[/green]")
472
443
  # Your actual operation code here
473
444
  result = {"executed": True, "resources": resources or []}
474
-
445
+
475
446
  # Log success
476
447
  framework.log_operation_complete(context, success=True, results=result)
477
-
448
+
478
449
  return result
479
-
450
+
480
451
  except Exception as e:
481
452
  # Log failure
482
453
  framework.log_operation_complete(context, success=False, error=str(e))
@@ -487,58 +458,60 @@ def custom_operation_with_framework(
487
458
  # 8. MIGRATION HELPER FUNCTIONS
488
459
  # =============================================================================
489
460
 
461
+
490
462
  def migrate_legacy_dry_run_function(legacy_function: callable) -> callable:
491
463
  """
492
464
  Helper function to migrate legacy dry-run implementations to the new framework.
493
-
465
+
494
466
  This function can wrap existing functions that have their own dry-run logic
495
467
  and upgrade them to use the unified framework.
496
468
  """
469
+
497
470
  def wrapper(*args, **kwargs):
498
471
  # Extract dry_run parameter
499
- dry_run = kwargs.get('dry_run', True)
500
-
472
+ dry_run = kwargs.get("dry_run", True)
473
+
501
474
  # Determine operation type based on function name/behavior
502
475
  func_name = legacy_function.__name__
503
- if 'create' in func_name or 'provision' in func_name:
476
+ if "create" in func_name or "provision" in func_name:
504
477
  op_type = OperationType.RESOURCE_CREATE
505
- elif 'delete' in func_name or 'terminate' in func_name or 'remove' in func_name:
478
+ elif "delete" in func_name or "terminate" in func_name or "remove" in func_name:
506
479
  op_type = OperationType.RESOURCE_DELETE
507
- elif 'modify' in func_name or 'update' in func_name or 'change' in func_name:
480
+ elif "modify" in func_name or "update" in func_name or "change" in func_name:
508
481
  op_type = OperationType.RESOURCE_MODIFY
509
- elif 'fix' in func_name or 'remediat' in func_name:
482
+ elif "fix" in func_name or "remediat" in func_name:
510
483
  op_type = OperationType.REMEDIATION
511
484
  else:
512
485
  op_type = OperationType.ANALYSIS
513
-
486
+
514
487
  # Create context
515
488
  context = framework.create_context(
516
489
  dry_run=dry_run,
517
490
  operation_type=op_type,
518
- module_name=legacy_function.__module__.split('.')[-2] if '.' in legacy_function.__module__ else 'legacy',
519
- operation_name=func_name
491
+ module_name=legacy_function.__module__.split(".")[-2] if "." in legacy_function.__module__ else "legacy",
492
+ operation_name=func_name,
520
493
  )
521
-
494
+
522
495
  # Apply framework behavior
523
496
  framework.display_dry_run_banner(context)
524
-
497
+
525
498
  if not framework.confirm_operation(context):
526
499
  return None
527
-
500
+
528
501
  framework.log_operation_start(context)
529
-
502
+
530
503
  try:
531
504
  # Call the original function with updated context
532
- kwargs['dry_run'] = context.enabled
505
+ kwargs["dry_run"] = context.enabled
533
506
  result = legacy_function(*args, **kwargs)
534
-
507
+
535
508
  framework.log_operation_complete(context, success=True, results={"migrated": True})
536
509
  return result
537
-
510
+
538
511
  except Exception as e:
539
512
  framework.log_operation_complete(context, success=False, error=str(e))
540
513
  raise
541
-
514
+
542
515
  return wrapper
543
516
 
544
517
 
@@ -551,37 +524,29 @@ if __name__ == "__main__":
551
524
  Example CLI usage demonstrating the dry-run framework.
552
525
  """
553
526
  import click
554
-
527
+
555
528
  @click.group()
556
529
  def cli():
557
530
  """Example CLI with dry-run framework integration."""
558
531
  pass
559
-
532
+
560
533
  @cli.command()
561
- @click.option('--dry-run/--no-dry-run', default=True, help='Enable dry-run mode')
562
- @click.option('--profile', help='AWS profile name')
563
- @click.option('--regions', multiple=True, help='AWS regions')
534
+ @click.option("--dry-run/--no-dry-run", default=True, help="Enable dry-run mode")
535
+ @click.option("--profile", help="AWS profile name")
536
+ @click.option("--regions", multiple=True, help="AWS regions")
564
537
  def discover(dry_run, profile, regions):
565
538
  """Discover EC2 instances with dry-run support."""
566
- result = collect_ec2_instances(
567
- profile=profile,
568
- regions=list(regions) if regions else None,
569
- dry_run=dry_run
570
- )
539
+ result = collect_ec2_instances(profile=profile, regions=list(regions) if regions else None, dry_run=dry_run)
571
540
  console.print(f"Discovery result: {result}")
572
-
541
+
573
542
  @cli.command()
574
- @click.option('--dry-run/--no-dry-run', default=True, help='Enable dry-run mode')
575
- @click.option('--instance-type', default='t3.micro', help='Instance type')
576
- @click.option('--profile', help='AWS profile name')
543
+ @click.option("--dry-run/--no-dry-run", default=True, help="Enable dry-run mode")
544
+ @click.option("--instance-type", default="t3.micro", help="Instance type")
545
+ @click.option("--profile", help="AWS profile name")
577
546
  def create(dry_run, instance_type, profile):
578
547
  """Create EC2 instance with dry-run support."""
579
- result = create_ec2_instance(
580
- instance_type=instance_type,
581
- profile=profile,
582
- dry_run=dry_run
583
- )
548
+ result = create_ec2_instance(instance_type=instance_type, profile=profile, dry_run=dry_run)
584
549
  console.print(f"Creation result: {result}")
585
-
550
+
586
551
  # Run CLI
587
- cli()
552
+ cli()