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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.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()