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
@@ -10,13 +10,13 @@ Framework: Multi-account security orchestration with proven coordination pattern
10
10
  Status: Enterprise-ready with systematic delegation and FAANG SDLC compliance
11
11
 
12
12
  Strategic Alignment:
13
- - 3 Strategic Objectives: runbooks package + FAANG SDLC + GitHub SSoT
13
+ - 3 Strategic Objectives: runbooks package + FAANG SDLC + GitHub SSoT
14
14
  - Core Principles: "Do one thing and do it well" + "Move Fast, But Not So Fast We Crash"
15
15
  - Enterprise Coordination: Multi-agent security validation with systematic delegation
16
16
 
17
17
  Key Capabilities:
18
18
  - 61-account concurrent security control deployment
19
- - Cross-account role-based security policy enforcement
19
+ - Cross-account role-based security policy enforcement
20
20
  - Organization-wide compliance monitoring and reporting
21
21
  - Automated security baseline implementation
22
22
  - Executive security posture dashboards
@@ -53,7 +53,7 @@ from runbooks.common.rich_utils import (
53
53
 
54
54
  class SecurityControlType(Enum):
55
55
  """Types of security controls for multi-account deployment."""
56
-
56
+
57
57
  IAM_BASELINE = "IAM_BASELINE"
58
58
  ENCRYPTION_ENFORCEMENT = "ENCRYPTION_ENFORCEMENT"
59
59
  NETWORK_SECURITY = "NETWORK_SECURITY"
@@ -66,16 +66,16 @@ class SecurityControlType(Enum):
66
66
 
67
67
  class DeploymentStrategy(Enum):
68
68
  """Security control deployment strategies."""
69
-
70
- PARALLEL_ALL = "PARALLEL_ALL" # Deploy to all accounts simultaneously
71
- STAGED_ROLLOUT = "STAGED_ROLLOUT" # Deploy in waves with validation gates
72
- PILOT_FIRST = "PILOT_FIRST" # Deploy to pilot accounts first
73
- CRITICAL_FIRST = "CRITICAL_FIRST" # Deploy to critical accounts first
69
+
70
+ PARALLEL_ALL = "PARALLEL_ALL" # Deploy to all accounts simultaneously
71
+ STAGED_ROLLOUT = "STAGED_ROLLOUT" # Deploy in waves with validation gates
72
+ PILOT_FIRST = "PILOT_FIRST" # Deploy to pilot accounts first
73
+ CRITICAL_FIRST = "CRITICAL_FIRST" # Deploy to critical accounts first
74
74
 
75
75
 
76
76
  class ControlStatus(Enum):
77
77
  """Status of security control deployment."""
78
-
78
+
79
79
  PENDING = "PENDING"
80
80
  DEPLOYING = "DEPLOYING"
81
81
  DEPLOYED = "DEPLOYED"
@@ -88,7 +88,7 @@ class ControlStatus(Enum):
88
88
  @dataclass
89
89
  class SecurityControl:
90
90
  """Represents a security control for multi-account deployment."""
91
-
91
+
92
92
  control_id: str
93
93
  control_name: str
94
94
  control_type: SecurityControlType
@@ -103,7 +103,7 @@ class SecurityControl:
103
103
  estimated_deployment_time: int # minutes
104
104
  requires_approval: bool = False
105
105
  cross_account_role_required: bool = True
106
-
106
+
107
107
  # Deployment tracking
108
108
  deployment_status: ControlStatus = ControlStatus.PENDING
109
109
  deployed_accounts: List[str] = field(default_factory=list)
@@ -114,7 +114,7 @@ class SecurityControl:
114
114
  @dataclass
115
115
  class AccountSecurityProfile:
116
116
  """Security profile for individual AWS account."""
117
-
117
+
118
118
  account_id: str
119
119
  account_name: str
120
120
  environment_type: str # prod, staging, dev, sandbox
@@ -130,7 +130,7 @@ class AccountSecurityProfile:
130
130
  @dataclass
131
131
  class MultiAccountSecurityReport:
132
132
  """Comprehensive security report across all accounts."""
133
-
133
+
134
134
  report_id: str
135
135
  timestamp: datetime
136
136
  total_accounts: int
@@ -150,10 +150,10 @@ class MultiAccountSecurityController:
150
150
  """
151
151
  Multi-Account Security Controls Framework
152
152
  ========================================
153
-
153
+
154
154
  Orchestrates security control deployment and compliance monitoring across
155
155
  enterprise AWS Organizations with up to 61 concurrent account operations.
156
-
156
+
157
157
  Enterprise Features:
158
158
  - Parallel security control deployment with intelligent batching
159
159
  - Cross-account role-based policy enforcement
@@ -163,34 +163,34 @@ class MultiAccountSecurityController:
163
163
  """
164
164
 
165
165
  def __init__(
166
- self,
167
- profile: str = "default",
166
+ self,
167
+ profile: str = "default",
168
168
  output_dir: str = "./artifacts/multi-account-security",
169
169
  max_concurrent_accounts: int = 61,
170
- dry_run: bool = True
170
+ dry_run: bool = True,
171
171
  ):
172
172
  self.profile = profile
173
173
  self.output_dir = Path(output_dir)
174
174
  self.output_dir.mkdir(parents=True, exist_ok=True)
175
175
  self.max_concurrent_accounts = max_concurrent_accounts
176
176
  self.dry_run = dry_run
177
-
177
+
178
178
  # Initialize secure management session
179
179
  self.session = self._create_secure_session()
180
-
180
+
181
181
  # Security control definitions
182
182
  self.security_controls = self._initialize_security_controls()
183
-
183
+
184
184
  # Account discovery and profiling
185
185
  self.account_profiles = {}
186
186
  self.organization_structure = {}
187
-
187
+
188
188
  # Cross-account role management
189
189
  self.cross_account_role_arn = self._get_cross_account_role_arn()
190
-
190
+
191
191
  # Deployment tracking
192
192
  self.deployment_tracker = MultiAccountDeploymentTracker(self.output_dir)
193
-
193
+
194
194
  print_header("Multi-Account Security Controller", "1.0.0")
195
195
  print_info(f"Profile: {profile}")
196
196
  print_info(f"Max concurrent accounts: {max_concurrent_accounts}")
@@ -200,282 +200,260 @@ class MultiAccountSecurityController:
200
200
  def _create_secure_session(self) -> boto3.Session:
201
201
  """Create secure AWS session with organization-level permissions."""
202
202
  try:
203
- session = create_management_session(profile=self.profile)
204
-
203
+ session = create_management_session(profile_name=self.profile)
204
+
205
205
  # Validate organization access
206
206
  try:
207
- organizations = session.client('organizations')
207
+ organizations = session.client("organizations")
208
208
  org_info = organizations.describe_organization()
209
209
  print_success(f"Organization access validated: {org_info['Organization']['Id']}")
210
210
  except ClientError as e:
211
211
  print_warning(f"Limited organization access: {str(e)}")
212
-
212
+
213
213
  # Validate session credentials
214
214
  sts_client = session.client("sts")
215
215
  identity = sts_client.get_caller_identity()
216
-
216
+
217
217
  print_info(f"Management session established for: {identity.get('Arn', 'Unknown')}")
218
218
  return session
219
-
219
+
220
220
  except (ClientError, NoCredentialsError) as e:
221
221
  print_error(f"Failed to establish management session: {str(e)}")
222
222
  raise
223
223
 
224
224
  def _get_cross_account_role_arn(self) -> str:
225
225
  """Get cross-account role ARN for security operations."""
226
-
226
+
227
227
  # Standard cross-account security role
228
228
  return "arn:aws:iam::{account_id}:role/CloudOpsSecurityRole"
229
229
 
230
230
  def _initialize_security_controls(self) -> List[SecurityControl]:
231
231
  """Initialize comprehensive security controls for enterprise deployment."""
232
-
232
+
233
233
  controls = []
234
-
234
+
235
235
  # IAM Baseline Controls
236
- controls.append(SecurityControl(
237
- control_id="IAM-001",
238
- control_name="IAM Password Policy Enforcement",
239
- control_type=SecurityControlType.IAM_BASELINE,
240
- description="Enforce strong password policy across all accounts",
241
- aws_services=["iam"],
242
- compliance_frameworks=["SOC2", "CIS Benchmarks", "AWS Well-Architected"],
243
- deployment_template={
244
- "MinimumPasswordLength": 14,
245
- "RequireUppercaseCharacters": True,
246
- "RequireLowercaseCharacters": True,
247
- "RequireNumbers": True,
248
- "RequireSymbols": True,
249
- "MaxPasswordAge": 90,
250
- "PasswordReusePrevention": 24,
251
- "HardExpiry": False
252
- },
253
- validation_checks=[
254
- "verify_password_policy_applied",
255
- "check_minimum_password_length",
256
- "validate_complexity_requirements"
257
- ],
258
- rollback_procedure=[
259
- "revert_to_previous_password_policy",
260
- "notify_security_team_of_rollback"
261
- ],
262
- business_justification="Reduces account compromise risk by 80%",
263
- risk_if_not_implemented="High risk of credential-based attacks",
264
- estimated_deployment_time=5,
265
- requires_approval=False
266
- ))
267
-
268
- controls.append(SecurityControl(
269
- control_id="IAM-002",
270
- control_name="Root Account MFA Enforcement",
271
- control_type=SecurityControlType.IAM_BASELINE,
272
- description="Ensure MFA is enabled on all root accounts",
273
- aws_services=["iam"],
274
- compliance_frameworks=["SOC2", "CIS Benchmarks", "PCI-DSS"],
275
- deployment_template={
276
- "mfa_required": True,
277
- "virtual_mfa_preferred": True,
278
- "hardware_mfa_fallback": True
279
- },
280
- validation_checks=[
281
- "verify_root_mfa_enabled",
282
- "check_mfa_device_type",
283
- "validate_mfa_functionality"
284
- ],
285
- rollback_procedure=[
286
- "document_mfa_removal_justification",
287
- "notify_compliance_team"
288
- ],
289
- business_justification="Prevents root account compromise - critical for enterprise security",
290
- risk_if_not_implemented="Critical - complete account takeover possible",
291
- estimated_deployment_time=10,
292
- requires_approval=True # Root account changes require approval
293
- ))
294
-
236
+ controls.append(
237
+ SecurityControl(
238
+ control_id="IAM-001",
239
+ control_name="IAM Password Policy Enforcement",
240
+ control_type=SecurityControlType.IAM_BASELINE,
241
+ description="Enforce strong password policy across all accounts",
242
+ aws_services=["iam"],
243
+ compliance_frameworks=["SOC2", "CIS Benchmarks", "AWS Well-Architected"],
244
+ deployment_template={
245
+ "MinimumPasswordLength": 14,
246
+ "RequireUppercaseCharacters": True,
247
+ "RequireLowercaseCharacters": True,
248
+ "RequireNumbers": True,
249
+ "RequireSymbols": True,
250
+ "MaxPasswordAge": 90,
251
+ "PasswordReusePrevention": 24,
252
+ "HardExpiry": False,
253
+ },
254
+ validation_checks=[
255
+ "verify_password_policy_applied",
256
+ "check_minimum_password_length",
257
+ "validate_complexity_requirements",
258
+ ],
259
+ rollback_procedure=["revert_to_previous_password_policy", "notify_security_team_of_rollback"],
260
+ business_justification="Reduces account compromise risk by 80%",
261
+ risk_if_not_implemented="High risk of credential-based attacks",
262
+ estimated_deployment_time=5,
263
+ requires_approval=False,
264
+ )
265
+ )
266
+
267
+ controls.append(
268
+ SecurityControl(
269
+ control_id="IAM-002",
270
+ control_name="Root Account MFA Enforcement",
271
+ control_type=SecurityControlType.IAM_BASELINE,
272
+ description="Ensure MFA is enabled on all root accounts",
273
+ aws_services=["iam"],
274
+ compliance_frameworks=["SOC2", "CIS Benchmarks", "PCI-DSS"],
275
+ deployment_template={
276
+ "mfa_required": True,
277
+ "virtual_mfa_preferred": True,
278
+ "hardware_mfa_fallback": True,
279
+ },
280
+ validation_checks=["verify_root_mfa_enabled", "check_mfa_device_type", "validate_mfa_functionality"],
281
+ rollback_procedure=["document_mfa_removal_justification", "notify_compliance_team"],
282
+ business_justification="Prevents root account compromise - critical for enterprise security",
283
+ risk_if_not_implemented="Critical - complete account takeover possible",
284
+ estimated_deployment_time=10,
285
+ requires_approval=True, # Root account changes require approval
286
+ )
287
+ )
288
+
295
289
  # Encryption Controls
296
- controls.append(SecurityControl(
297
- control_id="ENC-001",
298
- control_name="S3 Bucket Encryption Enforcement",
299
- control_type=SecurityControlType.ENCRYPTION_ENFORCEMENT,
300
- description="Enforce encryption at rest for all S3 buckets",
301
- aws_services=["s3"],
302
- compliance_frameworks=["SOC2", "PCI-DSS", "HIPAA"],
303
- deployment_template={
304
- "encryption_algorithm": "AES256",
305
- "kms_encryption_preferred": True,
306
- "bucket_key_enabled": True,
307
- "deny_unencrypted_object_uploads": True
308
- },
309
- validation_checks=[
310
- "verify_bucket_encryption_enabled",
311
- "check_default_encryption_configuration",
312
- "validate_object_encryption_status"
313
- ],
314
- rollback_procedure=[
315
- "disable_encryption_requirement",
316
- "restore_previous_bucket_policies"
317
- ],
318
- business_justification="Protects sensitive data and meets compliance requirements",
319
- risk_if_not_implemented="Data breach risk, compliance violations",
320
- estimated_deployment_time=15
321
- ))
322
-
323
- controls.append(SecurityControl(
324
- control_id="ENC-002",
325
- control_name="EBS Volume Encryption",
326
- control_type=SecurityControlType.ENCRYPTION_ENFORCEMENT,
327
- description="Enforce encryption for all EBS volumes",
328
- aws_services=["ec2"],
329
- compliance_frameworks=["SOC2", "PCI-DSS", "HIPAA"],
330
- deployment_template={
331
- "default_encryption_enabled": True,
332
- "kms_key_id": "alias/aws/ebs",
333
- "delete_on_termination": True
334
- },
335
- validation_checks=[
336
- "verify_ebs_encryption_default",
337
- "check_existing_volume_encryption",
338
- "validate_kms_key_permissions"
339
- ],
340
- rollback_procedure=[
341
- "disable_default_ebs_encryption",
342
- "document_encryption_rollback"
343
- ],
344
- business_justification="Protects data at rest on compute instances",
345
- risk_if_not_implemented="Data exposure from compromised or lost instances",
346
- estimated_deployment_time=10
347
- ))
348
-
290
+ controls.append(
291
+ SecurityControl(
292
+ control_id="ENC-001",
293
+ control_name="S3 Bucket Encryption Enforcement",
294
+ control_type=SecurityControlType.ENCRYPTION_ENFORCEMENT,
295
+ description="Enforce encryption at rest for all S3 buckets",
296
+ aws_services=["s3"],
297
+ compliance_frameworks=["SOC2", "PCI-DSS", "HIPAA"],
298
+ deployment_template={
299
+ "encryption_algorithm": "AES256",
300
+ "kms_encryption_preferred": True,
301
+ "bucket_key_enabled": True,
302
+ "deny_unencrypted_object_uploads": True,
303
+ },
304
+ validation_checks=[
305
+ "verify_bucket_encryption_enabled",
306
+ "check_default_encryption_configuration",
307
+ "validate_object_encryption_status",
308
+ ],
309
+ rollback_procedure=["disable_encryption_requirement", "restore_previous_bucket_policies"],
310
+ business_justification="Protects sensitive data and meets compliance requirements",
311
+ risk_if_not_implemented="Data breach risk, compliance violations",
312
+ estimated_deployment_time=15,
313
+ )
314
+ )
315
+
316
+ controls.append(
317
+ SecurityControl(
318
+ control_id="ENC-002",
319
+ control_name="EBS Volume Encryption",
320
+ control_type=SecurityControlType.ENCRYPTION_ENFORCEMENT,
321
+ description="Enforce encryption for all EBS volumes",
322
+ aws_services=["ec2"],
323
+ compliance_frameworks=["SOC2", "PCI-DSS", "HIPAA"],
324
+ deployment_template={
325
+ "default_encryption_enabled": True,
326
+ "kms_key_id": "alias/aws/ebs",
327
+ "delete_on_termination": True,
328
+ },
329
+ validation_checks=[
330
+ "verify_ebs_encryption_default",
331
+ "check_existing_volume_encryption",
332
+ "validate_kms_key_permissions",
333
+ ],
334
+ rollback_procedure=["disable_default_ebs_encryption", "document_encryption_rollback"],
335
+ business_justification="Protects data at rest on compute instances",
336
+ risk_if_not_implemented="Data exposure from compromised or lost instances",
337
+ estimated_deployment_time=10,
338
+ )
339
+ )
340
+
349
341
  # Network Security Controls
350
- controls.append(SecurityControl(
351
- control_id="NET-001",
352
- control_name="VPC Flow Logs Enablement",
353
- control_type=SecurityControlType.NETWORK_SECURITY,
354
- description="Enable VPC Flow Logs for all VPCs",
355
- aws_services=["ec2", "logs"],
356
- compliance_frameworks=["SOC2", "AWS Well-Architected"],
357
- deployment_template={
358
- "log_destination_type": "cloud-watch-logs",
359
- "traffic_type": "ALL",
360
- "log_format": "${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${windowstart} ${windowend} ${action}",
361
- "max_aggregation_interval": 60
362
- },
363
- validation_checks=[
364
- "verify_flow_logs_enabled",
365
- "check_log_destination_access",
366
- "validate_log_format_compliance"
367
- ],
368
- rollback_procedure=[
369
- "disable_vpc_flow_logs",
370
- "clean_up_log_groups"
371
- ],
372
- business_justification="Enables network security monitoring and forensics",
373
- risk_if_not_implemented="Limited visibility into network traffic and security events",
374
- estimated_deployment_time=20
375
- ))
376
-
342
+ controls.append(
343
+ SecurityControl(
344
+ control_id="NET-001",
345
+ control_name="VPC Flow Logs Enablement",
346
+ control_type=SecurityControlType.NETWORK_SECURITY,
347
+ description="Enable VPC Flow Logs for all VPCs",
348
+ aws_services=["ec2", "logs"],
349
+ compliance_frameworks=["SOC2", "AWS Well-Architected"],
350
+ deployment_template={
351
+ "log_destination_type": "cloud-watch-logs",
352
+ "traffic_type": "ALL",
353
+ "log_format": "${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${windowstart} ${windowend} ${action}",
354
+ "max_aggregation_interval": 60,
355
+ },
356
+ validation_checks=[
357
+ "verify_flow_logs_enabled",
358
+ "check_log_destination_access",
359
+ "validate_log_format_compliance",
360
+ ],
361
+ rollback_procedure=["disable_vpc_flow_logs", "clean_up_log_groups"],
362
+ business_justification="Enables network security monitoring and forensics",
363
+ risk_if_not_implemented="Limited visibility into network traffic and security events",
364
+ estimated_deployment_time=20,
365
+ )
366
+ )
367
+
377
368
  # Audit Logging Controls
378
- controls.append(SecurityControl(
379
- control_id="AUD-001",
380
- control_name="CloudTrail Organization-wide Logging",
381
- control_type=SecurityControlType.AUDIT_LOGGING,
382
- description="Enable comprehensive CloudTrail logging across organization",
383
- aws_services=["cloudtrail", "s3"],
384
- compliance_frameworks=["SOC2", "PCI-DSS", "AWS Well-Architected"],
385
- deployment_template={
386
- "include_global_service_events": True,
387
- "is_multi_region_trail": True,
388
- "enable_log_file_validation": True,
389
- "event_selectors": [
390
- {
391
- "read_write_type": "All",
392
- "include_management_events": True,
393
- "data_resources": [
394
- {
395
- "type": "AWS::S3::Object",
396
- "values": ["arn:aws:s3:::*/*"]
397
- }
398
- ]
399
- }
400
- ]
401
- },
402
- validation_checks=[
403
- "verify_cloudtrail_enabled",
404
- "check_log_file_validation",
405
- "validate_s3_bucket_security"
406
- ],
407
- rollback_procedure=[
408
- "disable_organization_cloudtrail",
409
- "remove_log_bucket_policies"
410
- ],
411
- business_justification="Essential for compliance, security monitoring, and forensics",
412
- risk_if_not_implemented="No audit trail for security investigations",
413
- estimated_deployment_time=30,
414
- requires_approval=True # Organization-wide changes require approval
415
- ))
416
-
369
+ controls.append(
370
+ SecurityControl(
371
+ control_id="AUD-001",
372
+ control_name="CloudTrail Organization-wide Logging",
373
+ control_type=SecurityControlType.AUDIT_LOGGING,
374
+ description="Enable comprehensive CloudTrail logging across organization",
375
+ aws_services=["cloudtrail", "s3"],
376
+ compliance_frameworks=["SOC2", "PCI-DSS", "AWS Well-Architected"],
377
+ deployment_template={
378
+ "include_global_service_events": True,
379
+ "is_multi_region_trail": True,
380
+ "enable_log_file_validation": True,
381
+ "event_selectors": [
382
+ {
383
+ "read_write_type": "All",
384
+ "include_management_events": True,
385
+ "data_resources": [{"type": "AWS::S3::Object", "values": ["arn:aws:s3:::*/*"]}],
386
+ }
387
+ ],
388
+ },
389
+ validation_checks=[
390
+ "verify_cloudtrail_enabled",
391
+ "check_log_file_validation",
392
+ "validate_s3_bucket_security",
393
+ ],
394
+ rollback_procedure=["disable_organization_cloudtrail", "remove_log_bucket_policies"],
395
+ business_justification="Essential for compliance, security monitoring, and forensics",
396
+ risk_if_not_implemented="No audit trail for security investigations",
397
+ estimated_deployment_time=30,
398
+ requires_approval=True, # Organization-wide changes require approval
399
+ )
400
+ )
401
+
417
402
  # Compliance Monitoring Controls
418
- controls.append(SecurityControl(
419
- control_id="CMP-001",
420
- control_name="AWS Config Multi-Account Setup",
421
- control_type=SecurityControlType.COMPLIANCE_MONITORING,
422
- description="Deploy AWS Config for continuous compliance monitoring",
423
- aws_services=["config", "s3"],
424
- compliance_frameworks=["SOC2", "CIS Benchmarks", "AWS Well-Architected"],
425
- deployment_template={
426
- "configuration_recorder": {
427
- "record_all_supported": True,
428
- "include_global_resource_types": True,
429
- "recording_group": {
430
- "all_supported": True,
431
- "include_global_resource_types": True
432
- }
403
+ controls.append(
404
+ SecurityControl(
405
+ control_id="CMP-001",
406
+ control_name="AWS Config Multi-Account Setup",
407
+ control_type=SecurityControlType.COMPLIANCE_MONITORING,
408
+ description="Deploy AWS Config for continuous compliance monitoring",
409
+ aws_services=["config", "s3"],
410
+ compliance_frameworks=["SOC2", "CIS Benchmarks", "AWS Well-Architected"],
411
+ deployment_template={
412
+ "configuration_recorder": {
413
+ "record_all_supported": True,
414
+ "include_global_resource_types": True,
415
+ "recording_group": {"all_supported": True, "include_global_resource_types": True},
416
+ },
417
+ "delivery_channel": {
418
+ "s3_bucket_name": "organization-config-bucket",
419
+ "config_snapshot_delivery_properties": {"delivery_frequency": "TwentyFour_Hours"},
420
+ },
433
421
  },
434
- "delivery_channel": {
435
- "s3_bucket_name": "organization-config-bucket",
436
- "config_snapshot_delivery_properties": {
437
- "delivery_frequency": "TwentyFour_Hours"
438
- }
439
- }
440
- },
441
- validation_checks=[
442
- "verify_config_recorder_status",
443
- "check_delivery_channel_status",
444
- "validate_config_rules_deployment"
445
- ],
446
- rollback_procedure=[
447
- "stop_configuration_recorder",
448
- "delete_delivery_channel",
449
- "clean_up_config_rules"
450
- ],
451
- business_justification="Automated compliance monitoring reduces manual audit overhead",
452
- risk_if_not_implemented="Manual compliance checking, delayed non-compliance detection",
453
- estimated_deployment_time=45
454
- ))
455
-
422
+ validation_checks=[
423
+ "verify_config_recorder_status",
424
+ "check_delivery_channel_status",
425
+ "validate_config_rules_deployment",
426
+ ],
427
+ rollback_procedure=["stop_configuration_recorder", "delete_delivery_channel", "clean_up_config_rules"],
428
+ business_justification="Automated compliance monitoring reduces manual audit overhead",
429
+ risk_if_not_implemented="Manual compliance checking, delayed non-compliance detection",
430
+ estimated_deployment_time=45,
431
+ )
432
+ )
433
+
456
434
  return controls
457
435
 
458
436
  async def deploy_security_controls_organization_wide(
459
437
  self,
460
438
  control_ids: Optional[List[str]] = None,
461
439
  target_accounts: Optional[List[str]] = None,
462
- deployment_strategy: DeploymentStrategy = DeploymentStrategy.STAGED_ROLLOUT
440
+ deployment_strategy: DeploymentStrategy = DeploymentStrategy.STAGED_ROLLOUT,
463
441
  ) -> MultiAccountSecurityReport:
464
442
  """
465
443
  Deploy security controls across the entire AWS Organization.
466
-
444
+
467
445
  Args:
468
446
  control_ids: Specific controls to deploy (None for all controls)
469
447
  target_accounts: Specific accounts to target (None for all accounts)
470
448
  deployment_strategy: How to deploy controls across accounts
471
-
449
+
472
450
  Returns:
473
451
  MultiAccountSecurityReport with comprehensive deployment results
474
452
  """
475
-
453
+
476
454
  deployment_id = f"deploy-{int(time.time())}"
477
455
  start_time = datetime.utcnow()
478
-
456
+
479
457
  console.print(
480
458
  create_panel(
481
459
  f"[bold cyan]Organization-wide Security Control Deployment[/bold cyan]\n\n"
@@ -486,105 +464,93 @@ class MultiAccountSecurityController:
486
464
  border_style="cyan",
487
465
  )
488
466
  )
489
-
467
+
490
468
  # Discover and profile target accounts
491
469
  if not target_accounts:
492
470
  target_accounts = await self._discover_organization_accounts()
493
-
471
+
494
472
  await self._profile_target_accounts(target_accounts)
495
-
473
+
496
474
  # Select controls to deploy
497
475
  controls_to_deploy = self._select_controls_for_deployment(control_ids)
498
-
476
+
499
477
  print_info(f"Target accounts: {len(target_accounts)}")
500
478
  print_info(f"Controls to deploy: {len(controls_to_deploy)}")
501
-
479
+
502
480
  # Execute deployment based on strategy
503
481
  deployment_results = await self._execute_deployment_strategy(
504
- controls_to_deploy,
505
- target_accounts,
506
- deployment_strategy,
507
- deployment_id
482
+ controls_to_deploy, target_accounts, deployment_strategy, deployment_id
508
483
  )
509
-
484
+
510
485
  # Validate deployments
511
- validation_results = await self._validate_control_deployments(
512
- deployment_results,
513
- target_accounts
514
- )
515
-
486
+ validation_results = await self._validate_control_deployments(deployment_results, target_accounts)
487
+
516
488
  # Generate comprehensive report
517
489
  report = await self._generate_deployment_report(
518
- deployment_id,
519
- start_time,
520
- controls_to_deploy,
521
- target_accounts,
522
- deployment_results,
523
- validation_results
490
+ deployment_id, start_time, controls_to_deploy, target_accounts, deployment_results, validation_results
524
491
  )
525
-
492
+
526
493
  # Display summary
527
494
  self._display_deployment_summary(report)
528
-
495
+
529
496
  # Export report
530
497
  await self._export_deployment_report(report)
531
-
498
+
532
499
  return report
533
500
 
534
501
  async def _discover_organization_accounts(self) -> List[str]:
535
502
  """Discover all active accounts in the AWS Organization."""
536
-
503
+
537
504
  accounts = []
538
-
505
+
539
506
  try:
540
- organizations = self.session.client('organizations')
541
-
507
+ organizations = self.session.client("organizations")
508
+
542
509
  # Get organization details
543
510
  org_info = organizations.describe_organization()
544
511
  print_info(f"Organization ID: {org_info['Organization']['Id']}")
545
-
512
+
546
513
  # List all accounts
547
- paginator = organizations.get_paginator('list_accounts')
548
-
514
+ paginator = organizations.get_paginator("list_accounts")
515
+
549
516
  for page in paginator.paginate():
550
- for account in page.get('Accounts', []):
551
- if account['Status'] == 'ACTIVE':
552
- accounts.append(account['Id'])
553
-
517
+ for account in page.get("Accounts", []):
518
+ if account["Status"] == "ACTIVE":
519
+ accounts.append(account["Id"])
520
+
554
521
  print_success(f"Discovered {len(accounts)} active organization accounts")
555
-
522
+
556
523
  # Limit to max concurrent if needed
557
524
  if len(accounts) > self.max_concurrent_accounts:
558
525
  print_warning(f"Limiting to {self.max_concurrent_accounts} accounts for deployment")
559
- accounts = accounts[:self.max_concurrent_accounts]
560
-
526
+ accounts = accounts[: self.max_concurrent_accounts]
527
+
561
528
  except ClientError as e:
562
529
  print_warning(f"Could not discover organization accounts: {str(e)}")
563
530
  # Fallback to current account
564
- sts = self.session.client('sts')
565
- current_account = sts.get_caller_identity()['Account']
531
+ sts = self.session.client("sts")
532
+ current_account = sts.get_caller_identity()["Account"]
566
533
  accounts = [current_account]
567
534
  print_info(f"Using current account: {current_account}")
568
-
535
+
569
536
  return accounts
570
537
 
571
538
  async def _profile_target_accounts(self, target_accounts: List[str]):
572
539
  """Profile target accounts for deployment planning."""
573
-
540
+
574
541
  print_info(f"Profiling {len(target_accounts)} target accounts...")
575
-
542
+
576
543
  with create_progress_bar() as progress:
577
544
  task = progress.add_task("[cyan]Profiling accounts...", total=len(target_accounts))
578
-
545
+
579
546
  # Use ThreadPoolExecutor for concurrent account profiling
580
547
  with ThreadPoolExecutor(max_workers=min(10, len(target_accounts))) as executor:
581
-
582
548
  # Submit profiling tasks
583
549
  future_to_account = {
584
- executor.submit(self._profile_single_account, account_id): account_id
550
+ executor.submit(self._profile_single_account, account_id): account_id
585
551
  for account_id in target_accounts
586
552
  }
587
-
553
+
588
554
  # Process results as they complete
589
555
  for future in as_completed(future_to_account):
590
556
  account_id = future_to_account[future]
@@ -599,244 +565,232 @@ class MultiAccountSecurityController:
599
565
  account_name=f"Account-{account_id}",
600
566
  environment_type="unknown",
601
567
  business_criticality="medium",
602
- compliance_requirements=["SOC2"] # Default
568
+ compliance_requirements=["SOC2"], # Default
603
569
  )
604
-
570
+
605
571
  progress.update(task, advance=1)
606
-
572
+
607
573
  print_success(f"Account profiling completed: {len(self.account_profiles)} profiles created")
608
574
 
609
575
  def _profile_single_account(self, account_id: str) -> AccountSecurityProfile:
610
576
  """Profile a single account for security control deployment."""
611
-
577
+
612
578
  try:
613
579
  # Attempt to assume cross-account role
614
580
  account_session = self._assume_cross_account_role(account_id)
615
-
581
+
616
582
  if not account_session:
617
583
  # Use management session if cross-account role not available
618
584
  account_session = self.session
619
-
585
+
620
586
  # Gather account information
621
587
  account_info = self._gather_account_info(account_session, account_id)
622
-
588
+
623
589
  # Determine environment type from account name/tags
624
590
  environment_type = self._determine_environment_type(account_info)
625
-
591
+
626
592
  # Assess business criticality
627
593
  business_criticality = self._assess_business_criticality(account_info, environment_type)
628
-
594
+
629
595
  # Determine compliance requirements
630
- compliance_requirements = self._determine_compliance_requirements(
631
- environment_type, business_criticality
632
- )
633
-
596
+ compliance_requirements = self._determine_compliance_requirements(environment_type, business_criticality)
597
+
634
598
  # Check existing security controls
635
599
  deployed_controls = self._check_existing_security_controls(account_session)
636
-
600
+
637
601
  # Calculate current security score
638
602
  security_score = self._calculate_security_score(deployed_controls, compliance_requirements)
639
-
603
+
640
604
  return AccountSecurityProfile(
641
605
  account_id=account_id,
642
- account_name=account_info.get('name', f'Account-{account_id}'),
606
+ account_name=account_info.get("name", f"Account-{account_id}"),
643
607
  environment_type=environment_type,
644
608
  business_criticality=business_criticality,
645
609
  compliance_requirements=compliance_requirements,
646
610
  deployed_controls=deployed_controls,
647
611
  security_score=security_score,
648
- last_assessment=datetime.utcnow()
612
+ last_assessment=datetime.utcnow(),
649
613
  )
650
-
614
+
651
615
  except Exception as e:
652
616
  print_warning(f"Error profiling account {account_id}: {str(e)}")
653
-
617
+
654
618
  # Return minimal profile on error
655
619
  return AccountSecurityProfile(
656
620
  account_id=account_id,
657
- account_name=f'Account-{account_id}',
621
+ account_name=f"Account-{account_id}",
658
622
  environment_type="unknown",
659
623
  business_criticality="medium",
660
- compliance_requirements=["SOC2"]
624
+ compliance_requirements=["SOC2"],
661
625
  )
662
626
 
663
627
  def _assume_cross_account_role(self, account_id: str) -> Optional[boto3.Session]:
664
628
  """Assume cross-account role for security operations."""
665
-
629
+
666
630
  try:
667
631
  role_arn = self.cross_account_role_arn.format(account_id=account_id)
668
-
669
- sts = self.session.client('sts')
632
+
633
+ sts = self.session.client("sts")
670
634
  response = sts.assume_role(
671
- RoleArn=role_arn,
672
- RoleSessionName=f'CloudOpsSecurityDeployment-{int(time.time())}'
635
+ RoleArn=role_arn, RoleSessionName=f"CloudOpsSecurityDeployment-{int(time.time())}"
673
636
  )
674
-
675
- credentials = response['Credentials']
676
-
637
+
638
+ credentials = response["Credentials"]
639
+
677
640
  return boto3.Session(
678
- aws_access_key_id=credentials['AccessKeyId'],
679
- aws_secret_access_key=credentials['SecretAccessKey'],
680
- aws_session_token=credentials['SessionToken']
641
+ aws_access_key_id=credentials["AccessKeyId"],
642
+ aws_secret_access_key=credentials["SecretAccessKey"],
643
+ aws_session_token=credentials["SessionToken"],
681
644
  )
682
-
645
+
683
646
  except ClientError as e:
684
647
  print_warning(f"Could not assume role in account {account_id}: {str(e)}")
685
648
  return None
686
649
 
687
650
  def _gather_account_info(self, session: boto3.Session, account_id: str) -> Dict[str, Any]:
688
651
  """Gather basic account information."""
689
-
690
- account_info = {'id': account_id}
691
-
652
+
653
+ account_info = {"id": account_id}
654
+
692
655
  try:
693
656
  # Try to get account alias
694
- iam = session.client('iam')
695
- aliases = iam.list_account_aliases()['AccountAliases']
657
+ iam = session.client("iam")
658
+ aliases = iam.list_account_aliases()["AccountAliases"]
696
659
  if aliases:
697
- account_info['name'] = aliases[0]
698
- account_info['alias'] = aliases[0]
660
+ account_info["name"] = aliases[0]
661
+ account_info["alias"] = aliases[0]
699
662
  except ClientError:
700
663
  pass
701
-
664
+
702
665
  try:
703
666
  # Get account attributes if possible
704
- organizations = self.session.client('organizations')
667
+ organizations = self.session.client("organizations")
705
668
  account_details = organizations.describe_account(AccountId=account_id)
706
- account_info['name'] = account_details['Account']['Name']
707
- account_info['email'] = account_details['Account']['Email']
708
- account_info['status'] = account_details['Account']['Status']
669
+ account_info["name"] = account_details["Account"]["Name"]
670
+ account_info["email"] = account_details["Account"]["Email"]
671
+ account_info["status"] = account_details["Account"]["Status"]
709
672
  except ClientError:
710
673
  pass
711
-
674
+
712
675
  return account_info
713
676
 
714
677
  def _determine_environment_type(self, account_info: Dict[str, Any]) -> str:
715
678
  """Determine environment type from account information."""
716
-
717
- account_name = account_info.get('name', '').lower()
718
-
719
- if any(keyword in account_name for keyword in ['prod', 'production', 'prd']):
720
- return 'production'
721
- elif any(keyword in account_name for keyword in ['stg', 'staging', 'stage']):
722
- return 'staging'
723
- elif any(keyword in account_name for keyword in ['dev', 'development', 'develop']):
724
- return 'development'
725
- elif any(keyword in account_name for keyword in ['test', 'testing', 'qa']):
726
- return 'testing'
727
- elif any(keyword in account_name for keyword in ['sandbox', 'sb', 'demo']):
728
- return 'sandbox'
679
+
680
+ account_name = account_info.get("name", "").lower()
681
+
682
+ if any(keyword in account_name for keyword in ["prod", "production", "prd"]):
683
+ return "production"
684
+ elif any(keyword in account_name for keyword in ["stg", "staging", "stage"]):
685
+ return "staging"
686
+ elif any(keyword in account_name for keyword in ["dev", "development", "develop"]):
687
+ return "development"
688
+ elif any(keyword in account_name for keyword in ["test", "testing", "qa"]):
689
+ return "testing"
690
+ elif any(keyword in account_name for keyword in ["sandbox", "sb", "demo"]):
691
+ return "sandbox"
729
692
  else:
730
- return 'unknown'
693
+ return "unknown"
731
694
 
732
695
  def _assess_business_criticality(self, account_info: Dict[str, Any], environment_type: str) -> str:
733
696
  """Assess business criticality of account."""
734
-
697
+
735
698
  # Production accounts are typically high/critical
736
- if environment_type == 'production':
737
- return 'critical'
738
- elif environment_type in ['staging', 'testing']:
739
- return 'high'
740
- elif environment_type == 'development':
741
- return 'medium'
699
+ if environment_type == "production":
700
+ return "critical"
701
+ elif environment_type in ["staging", "testing"]:
702
+ return "high"
703
+ elif environment_type == "development":
704
+ return "medium"
742
705
  else:
743
- return 'low'
706
+ return "low"
744
707
 
745
- def _determine_compliance_requirements(
746
- self,
747
- environment_type: str,
748
- business_criticality: str
749
- ) -> List[str]:
708
+ def _determine_compliance_requirements(self, environment_type: str, business_criticality: str) -> List[str]:
750
709
  """Determine compliance requirements based on account characteristics."""
751
-
752
- requirements = ['SOC2'] # Base requirement
753
-
754
- if business_criticality in ['critical', 'high']:
755
- requirements.extend(['AWS Well-Architected', 'CIS Benchmarks'])
756
-
757
- if environment_type == 'production':
758
- requirements.extend(['PCI-DSS', 'HIPAA']) # May be applicable
759
-
710
+
711
+ requirements = ["SOC2"] # Base requirement
712
+
713
+ if business_criticality in ["critical", "high"]:
714
+ requirements.extend(["AWS Well-Architected", "CIS Benchmarks"])
715
+
716
+ if environment_type == "production":
717
+ requirements.extend(["PCI-DSS", "HIPAA"]) # May be applicable
718
+
760
719
  return list(set(requirements)) # Remove duplicates
761
720
 
762
721
  def _check_existing_security_controls(self, session: boto3.Session) -> List[str]:
763
722
  """Check what security controls are already deployed in account."""
764
-
723
+
765
724
  deployed_controls = []
766
-
725
+
767
726
  try:
768
727
  # Check IAM password policy
769
- iam = session.client('iam')
728
+ iam = session.client("iam")
770
729
  try:
771
730
  iam.get_account_password_policy()
772
- deployed_controls.append('IAM-001')
731
+ deployed_controls.append("IAM-001")
773
732
  except ClientError:
774
733
  pass
775
-
734
+
776
735
  # Check CloudTrail
777
- cloudtrail = session.client('cloudtrail')
778
- trails = cloudtrail.describe_trails()['trailList']
736
+ cloudtrail = session.client("cloudtrail")
737
+ trails = cloudtrail.describe_trails()["trailList"]
779
738
  if trails:
780
- deployed_controls.append('AUD-001')
781
-
739
+ deployed_controls.append("AUD-001")
740
+
782
741
  # Check Config
783
- config = session.client('config')
742
+ config = session.client("config")
784
743
  try:
785
744
  config.describe_configuration_recorders()
786
- deployed_controls.append('CMP-001')
745
+ deployed_controls.append("CMP-001")
787
746
  except ClientError:
788
747
  pass
789
-
748
+
790
749
  # Check VPC Flow Logs (simplified check)
791
- ec2 = session.client('ec2')
792
- vpcs = ec2.describe_vpcs()['Vpcs']
793
- flow_logs = ec2.describe_flow_logs()['FlowLogs']
794
-
795
- vpc_with_flow_logs = {fl['ResourceId'] for fl in flow_logs if fl['ResourceType'] == 'VPC'}
750
+ ec2 = session.client("ec2")
751
+ vpcs = ec2.describe_vpcs()["Vpcs"]
752
+ flow_logs = ec2.describe_flow_logs()["FlowLogs"]
753
+
754
+ vpc_with_flow_logs = {fl["ResourceId"] for fl in flow_logs if fl["ResourceType"] == "VPC"}
796
755
  if len(vpc_with_flow_logs) > 0:
797
- deployed_controls.append('NET-001')
798
-
756
+ deployed_controls.append("NET-001")
757
+
799
758
  except Exception as e:
800
759
  print_warning(f"Error checking existing controls: {str(e)}")
801
-
760
+
802
761
  return deployed_controls
803
762
 
804
- def _calculate_security_score(
805
- self,
806
- deployed_controls: List[str],
807
- compliance_requirements: List[str]
808
- ) -> float:
763
+ def _calculate_security_score(self, deployed_controls: List[str], compliance_requirements: List[str]) -> float:
809
764
  """Calculate security score based on deployed controls."""
810
-
765
+
811
766
  total_applicable_controls = len(self.security_controls)
812
767
  deployed_count = len(deployed_controls)
813
-
768
+
814
769
  base_score = (deployed_count / total_applicable_controls) * 100
815
-
770
+
816
771
  # Adjust based on compliance requirements
817
772
  compliance_multiplier = 1.0 + (len(compliance_requirements) * 0.1)
818
-
773
+
819
774
  return min(100.0, base_score * compliance_multiplier)
820
775
 
821
776
  def _select_controls_for_deployment(self, control_ids: Optional[List[str]]) -> List[SecurityControl]:
822
777
  """Select security controls for deployment."""
823
-
778
+
824
779
  if control_ids:
825
780
  # Deploy specific controls
826
- selected_controls = [
827
- control for control in self.security_controls
828
- if control.control_id in control_ids
829
- ]
781
+ selected_controls = [control for control in self.security_controls if control.control_id in control_ids]
830
782
  else:
831
783
  # Deploy all controls
832
784
  selected_controls = self.security_controls.copy()
833
-
785
+
834
786
  # Sort by deployment priority (critical controls first)
835
- selected_controls.sort(key=lambda c: (
836
- c.requires_approval, # Non-approval controls first
837
- c.estimated_deployment_time # Faster deployments first
838
- ))
839
-
787
+ selected_controls.sort(
788
+ key=lambda c: (
789
+ c.requires_approval, # Non-approval controls first
790
+ c.estimated_deployment_time, # Faster deployments first
791
+ )
792
+ )
793
+
840
794
  return selected_controls
841
795
 
842
796
  async def _execute_deployment_strategy(
@@ -844,26 +798,22 @@ class MultiAccountSecurityController:
844
798
  controls_to_deploy: List[SecurityControl],
845
799
  target_accounts: List[str],
846
800
  deployment_strategy: DeploymentStrategy,
847
- deployment_id: str
801
+ deployment_id: str,
848
802
  ) -> Dict[str, Any]:
849
803
  """Execute security control deployment based on strategy."""
850
-
804
+
851
805
  deployment_results = {
852
- 'deployment_id': deployment_id,
853
- 'strategy': deployment_strategy.value,
854
- 'total_controls': len(controls_to_deploy),
855
- 'total_accounts': len(target_accounts),
856
- 'control_results': {},
857
- 'account_results': {},
858
- 'summary': {
859
- 'successful_deployments': 0,
860
- 'failed_deployments': 0,
861
- 'total_deployment_time': 0
862
- }
806
+ "deployment_id": deployment_id,
807
+ "strategy": deployment_strategy.value,
808
+ "total_controls": len(controls_to_deploy),
809
+ "total_accounts": len(target_accounts),
810
+ "control_results": {},
811
+ "account_results": {},
812
+ "summary": {"successful_deployments": 0, "failed_deployments": 0, "total_deployment_time": 0},
863
813
  }
864
-
814
+
865
815
  start_time = time.time()
866
-
816
+
867
817
  if deployment_strategy == DeploymentStrategy.PARALLEL_ALL:
868
818
  deployment_results = await self._parallel_deployment(
869
819
  controls_to_deploy, target_accounts, deployment_results
@@ -880,375 +830,317 @@ class MultiAccountSecurityController:
880
830
  deployment_results = await self._critical_first_deployment(
881
831
  controls_to_deploy, target_accounts, deployment_results
882
832
  )
883
-
884
- deployment_results['summary']['total_deployment_time'] = time.time() - start_time
885
-
833
+
834
+ deployment_results["summary"]["total_deployment_time"] = time.time() - start_time
835
+
886
836
  return deployment_results
887
837
 
888
838
  async def _parallel_deployment(
889
- self,
890
- controls_to_deploy: List[SecurityControl],
891
- target_accounts: List[str],
892
- deployment_results: Dict[str, Any]
839
+ self, controls_to_deploy: List[SecurityControl], target_accounts: List[str], deployment_results: Dict[str, Any]
893
840
  ) -> Dict[str, Any]:
894
841
  """Deploy all controls to all accounts in parallel."""
895
-
842
+
896
843
  print_info("Executing parallel deployment strategy")
897
-
844
+
898
845
  # Create deployment tasks for all control-account combinations
899
846
  deployment_tasks = []
900
-
847
+
901
848
  for control in controls_to_deploy:
902
849
  for account_id in target_accounts:
903
- task = asyncio.create_task(
904
- self._deploy_control_to_account(control, account_id)
905
- )
906
- deployment_tasks.append({
907
- 'task': task,
908
- 'control_id': control.control_id,
909
- 'account_id': account_id
910
- })
911
-
850
+ task = asyncio.create_task(self._deploy_control_to_account(control, account_id))
851
+ deployment_tasks.append({"task": task, "control_id": control.control_id, "account_id": account_id})
852
+
912
853
  # Execute all deployments with progress tracking
913
854
  with create_progress_bar() as progress:
914
- deploy_task = progress.add_task(
915
- "[green]Deploying controls...",
916
- total=len(deployment_tasks)
917
- )
918
-
855
+ deploy_task = progress.add_task("[green]Deploying controls...", total=len(deployment_tasks))
856
+
919
857
  # Process deployments as they complete
920
- for task_info in asyncio.as_completed([t['task'] for t in deployment_tasks]):
858
+ for task_info in asyncio.as_completed([t["task"] for t in deployment_tasks]):
921
859
  try:
922
860
  result = await task_info
923
-
861
+
924
862
  # Find the corresponding task info
925
- completed_task = next(
926
- t for t in deployment_tasks
927
- if t['task'] == task_info
928
- )
929
-
863
+ completed_task = next(t for t in deployment_tasks if t["task"] == task_info)
864
+
930
865
  # Store result
931
- control_id = completed_task['control_id']
932
- account_id = completed_task['account_id']
933
-
934
- if control_id not in deployment_results['control_results']:
935
- deployment_results['control_results'][control_id] = {}
936
-
937
- deployment_results['control_results'][control_id][account_id] = result
938
-
939
- if result['success']:
940
- deployment_results['summary']['successful_deployments'] += 1
866
+ control_id = completed_task["control_id"]
867
+ account_id = completed_task["account_id"]
868
+
869
+ if control_id not in deployment_results["control_results"]:
870
+ deployment_results["control_results"][control_id] = {}
871
+
872
+ deployment_results["control_results"][control_id][account_id] = result
873
+
874
+ if result["success"]:
875
+ deployment_results["summary"]["successful_deployments"] += 1
941
876
  else:
942
- deployment_results['summary']['failed_deployments'] += 1
943
-
877
+ deployment_results["summary"]["failed_deployments"] += 1
878
+
944
879
  progress.update(deploy_task, advance=1)
945
-
880
+
946
881
  except Exception as e:
947
882
  print_error(f"Deployment task failed: {str(e)}")
948
- deployment_results['summary']['failed_deployments'] += 1
883
+ deployment_results["summary"]["failed_deployments"] += 1
949
884
  progress.update(deploy_task, advance=1)
950
-
885
+
951
886
  return deployment_results
952
887
 
953
888
  async def _staged_rollout_deployment(
954
- self,
955
- controls_to_deploy: List[SecurityControl],
956
- target_accounts: List[str],
957
- deployment_results: Dict[str, Any]
889
+ self, controls_to_deploy: List[SecurityControl], target_accounts: List[str], deployment_results: Dict[str, Any]
958
890
  ) -> Dict[str, Any]:
959
891
  """Deploy controls in stages with validation gates."""
960
-
892
+
961
893
  print_info("Executing staged rollout deployment strategy")
962
-
894
+
963
895
  # Divide accounts into stages based on business criticality
964
896
  stage_accounts = self._create_deployment_stages(target_accounts)
965
-
897
+
966
898
  for stage_num, accounts in enumerate(stage_accounts, 1):
967
899
  print_info(f"Deploying to Stage {stage_num}: {len(accounts)} accounts")
968
-
900
+
969
901
  # Deploy to current stage
970
- stage_results = await self._deploy_to_account_group(
971
- controls_to_deploy, accounts, f"Stage-{stage_num}"
972
- )
973
-
902
+ stage_results = await self._deploy_to_account_group(controls_to_deploy, accounts, f"Stage-{stage_num}")
903
+
974
904
  # Merge results
975
905
  for control_id, control_results in stage_results.items():
976
- if control_id not in deployment_results['control_results']:
977
- deployment_results['control_results'][control_id] = {}
978
- deployment_results['control_results'][control_id].update(control_results)
979
-
906
+ if control_id not in deployment_results["control_results"]:
907
+ deployment_results["control_results"][control_id] = {}
908
+ deployment_results["control_results"][control_id].update(control_results)
909
+
980
910
  # Validation gate - check success rate before proceeding
981
911
  stage_success_rate = self._calculate_stage_success_rate(stage_results)
982
-
912
+
983
913
  if stage_success_rate < 0.8: # 80% success threshold
984
914
  print_warning(f"Stage {stage_num} success rate ({stage_success_rate:.1%}) below threshold")
985
-
915
+
986
916
  # Pause for investigation (in production, would require approval to continue)
987
917
  if not self.dry_run:
988
918
  print_warning("Pausing deployment for investigation")
989
919
  break
990
-
920
+
991
921
  print_success(f"Stage {stage_num} completed with {stage_success_rate:.1%} success rate")
992
-
922
+
993
923
  return deployment_results
994
924
 
995
925
  def _create_deployment_stages(self, target_accounts: List[str]) -> List[List[str]]:
996
926
  """Create deployment stages based on account characteristics."""
997
-
927
+
998
928
  # Group accounts by criticality and environment
999
929
  stages = {
1000
930
  1: [], # Sandbox/Development accounts first
1001
931
  2: [], # Testing/Staging accounts
1002
- 3: [] # Production accounts last
932
+ 3: [], # Production accounts last
1003
933
  }
1004
-
934
+
1005
935
  for account_id in target_accounts:
1006
936
  profile = self.account_profiles.get(account_id)
1007
-
937
+
1008
938
  if not profile:
1009
939
  stages[2].append(account_id) # Default to middle stage
1010
940
  continue
1011
-
1012
- if profile.environment_type in ['sandbox', 'development']:
941
+
942
+ if profile.environment_type in ["sandbox", "development"]:
1013
943
  stages[1].append(account_id)
1014
- elif profile.environment_type in ['testing', 'staging']:
944
+ elif profile.environment_type in ["testing", "staging"]:
1015
945
  stages[2].append(account_id)
1016
946
  else: # production or unknown
1017
947
  stages[3].append(account_id)
1018
-
948
+
1019
949
  # Return non-empty stages
1020
950
  return [accounts for accounts in stages.values() if accounts]
1021
951
 
1022
952
  async def _deploy_to_account_group(
1023
- self,
1024
- controls_to_deploy: List[SecurityControl],
1025
- account_group: List[str],
1026
- group_name: str
953
+ self, controls_to_deploy: List[SecurityControl], account_group: List[str], group_name: str
1027
954
  ) -> Dict[str, Dict[str, Any]]:
1028
955
  """Deploy controls to a group of accounts."""
1029
-
956
+
1030
957
  group_results = {}
1031
-
958
+
1032
959
  with create_progress_bar() as progress:
1033
960
  task = progress.add_task(
1034
- f"[cyan]Deploying to {group_name}...",
1035
- total=len(controls_to_deploy) * len(account_group)
961
+ f"[cyan]Deploying to {group_name}...", total=len(controls_to_deploy) * len(account_group)
1036
962
  )
1037
-
963
+
1038
964
  for control in controls_to_deploy:
1039
965
  control_results = {}
1040
-
966
+
1041
967
  # Deploy control to all accounts in group
1042
968
  deployment_tasks = [
1043
- self._deploy_control_to_account(control, account_id)
1044
- for account_id in account_group
969
+ self._deploy_control_to_account(control, account_id) for account_id in account_group
1045
970
  ]
1046
-
971
+
1047
972
  # Wait for all deployments to complete
1048
973
  results = await asyncio.gather(*deployment_tasks, return_exceptions=True)
1049
-
974
+
1050
975
  # Process results
1051
976
  for account_id, result in zip(account_group, results):
1052
977
  if isinstance(result, Exception):
1053
- control_results[account_id] = {
1054
- 'success': False,
1055
- 'error': str(result),
1056
- 'deployment_time': 0
1057
- }
978
+ control_results[account_id] = {"success": False, "error": str(result), "deployment_time": 0}
1058
979
  else:
1059
980
  control_results[account_id] = result
1060
-
981
+
1061
982
  progress.update(task, advance=1)
1062
-
983
+
1063
984
  group_results[control.control_id] = control_results
1064
-
985
+
1065
986
  return group_results
1066
987
 
1067
988
  def _calculate_stage_success_rate(self, stage_results: Dict[str, Dict[str, Any]]) -> float:
1068
989
  """Calculate success rate for a deployment stage."""
1069
-
990
+
1070
991
  total_deployments = 0
1071
992
  successful_deployments = 0
1072
-
993
+
1073
994
  for control_results in stage_results.values():
1074
995
  for account_result in control_results.values():
1075
996
  total_deployments += 1
1076
- if account_result.get('success', False):
997
+ if account_result.get("success", False):
1077
998
  successful_deployments += 1
1078
-
999
+
1079
1000
  if total_deployments == 0:
1080
1001
  return 0.0
1081
-
1002
+
1082
1003
  return successful_deployments / total_deployments
1083
1004
 
1084
1005
  async def _pilot_first_deployment(
1085
- self,
1086
- controls_to_deploy: List[SecurityControl],
1087
- target_accounts: List[str],
1088
- deployment_results: Dict[str, Any]
1006
+ self, controls_to_deploy: List[SecurityControl], target_accounts: List[str], deployment_results: Dict[str, Any]
1089
1007
  ) -> Dict[str, Any]:
1090
1008
  """Deploy to pilot accounts first, then full rollout."""
1091
-
1009
+
1092
1010
  print_info("Executing pilot-first deployment strategy")
1093
-
1011
+
1094
1012
  # Select pilot accounts (typically 10% of total, minimum 1, maximum 5)
1095
1013
  pilot_count = max(1, min(5, len(target_accounts) // 10))
1096
1014
  pilot_accounts = target_accounts[:pilot_count]
1097
1015
  remaining_accounts = target_accounts[pilot_count:]
1098
-
1016
+
1099
1017
  # Pilot deployment
1100
1018
  print_info(f"Pilot deployment to {len(pilot_accounts)} accounts")
1101
- pilot_results = await self._deploy_to_account_group(
1102
- controls_to_deploy, pilot_accounts, "Pilot"
1103
- )
1104
-
1019
+ pilot_results = await self._deploy_to_account_group(controls_to_deploy, pilot_accounts, "Pilot")
1020
+
1105
1021
  # Check pilot success
1106
1022
  pilot_success_rate = self._calculate_stage_success_rate(pilot_results)
1107
1023
  print_info(f"Pilot deployment success rate: {pilot_success_rate:.1%}")
1108
-
1024
+
1109
1025
  # Merge pilot results
1110
1026
  for control_id, control_results in pilot_results.items():
1111
- deployment_results['control_results'][control_id] = control_results
1112
-
1027
+ deployment_results["control_results"][control_id] = control_results
1028
+
1113
1029
  # Full deployment if pilot successful
1114
1030
  if pilot_success_rate >= 0.9: # 90% success required for full rollout
1115
1031
  print_info(f"Pilot successful, proceeding with full deployment to {len(remaining_accounts)} accounts")
1116
-
1117
- full_results = await self._deploy_to_account_group(
1118
- controls_to_deploy, remaining_accounts, "Full Rollout"
1119
- )
1120
-
1032
+
1033
+ full_results = await self._deploy_to_account_group(controls_to_deploy, remaining_accounts, "Full Rollout")
1034
+
1121
1035
  # Merge full deployment results
1122
1036
  for control_id, control_results in full_results.items():
1123
- if control_id not in deployment_results['control_results']:
1124
- deployment_results['control_results'][control_id] = {}
1125
- deployment_results['control_results'][control_id].update(control_results)
1037
+ if control_id not in deployment_results["control_results"]:
1038
+ deployment_results["control_results"][control_id] = {}
1039
+ deployment_results["control_results"][control_id].update(control_results)
1126
1040
  else:
1127
1041
  print_warning("Pilot deployment failed, stopping full rollout")
1128
-
1042
+
1129
1043
  return deployment_results
1130
1044
 
1131
1045
  async def _critical_first_deployment(
1132
- self,
1133
- controls_to_deploy: List[SecurityControl],
1134
- target_accounts: List[str],
1135
- deployment_results: Dict[str, Any]
1046
+ self, controls_to_deploy: List[SecurityControl], target_accounts: List[str], deployment_results: Dict[str, Any]
1136
1047
  ) -> Dict[str, Any]:
1137
1048
  """Deploy to critical accounts first."""
1138
-
1049
+
1139
1050
  print_info("Executing critical-first deployment strategy")
1140
-
1051
+
1141
1052
  # Group accounts by criticality
1142
1053
  critical_accounts = []
1143
1054
  other_accounts = []
1144
-
1055
+
1145
1056
  for account_id in target_accounts:
1146
1057
  profile = self.account_profiles.get(account_id)
1147
-
1148
- if profile and profile.business_criticality == 'critical':
1058
+
1059
+ if profile and profile.business_criticality == "critical":
1149
1060
  critical_accounts.append(account_id)
1150
1061
  else:
1151
1062
  other_accounts.append(account_id)
1152
-
1063
+
1153
1064
  # Deploy to critical accounts first
1154
1065
  if critical_accounts:
1155
1066
  print_info(f"Deploying to {len(critical_accounts)} critical accounts")
1156
1067
  critical_results = await self._deploy_to_account_group(
1157
1068
  controls_to_deploy, critical_accounts, "Critical Accounts"
1158
1069
  )
1159
-
1070
+
1160
1071
  # Merge critical results
1161
1072
  for control_id, control_results in critical_results.items():
1162
- deployment_results['control_results'][control_id] = control_results
1163
-
1073
+ deployment_results["control_results"][control_id] = control_results
1074
+
1164
1075
  # Deploy to other accounts
1165
1076
  if other_accounts:
1166
1077
  print_info(f"Deploying to {len(other_accounts)} other accounts")
1167
- other_results = await self._deploy_to_account_group(
1168
- controls_to_deploy, other_accounts, "Other Accounts"
1169
- )
1170
-
1078
+ other_results = await self._deploy_to_account_group(controls_to_deploy, other_accounts, "Other Accounts")
1079
+
1171
1080
  # Merge other results
1172
1081
  for control_id, control_results in other_results.items():
1173
- if control_id not in deployment_results['control_results']:
1174
- deployment_results['control_results'][control_id] = {}
1175
- deployment_results['control_results'][control_id].update(control_results)
1176
-
1082
+ if control_id not in deployment_results["control_results"]:
1083
+ deployment_results["control_results"][control_id] = {}
1084
+ deployment_results["control_results"][control_id].update(control_results)
1085
+
1177
1086
  return deployment_results
1178
1087
 
1179
- async def _deploy_control_to_account(
1180
- self,
1181
- control: SecurityControl,
1182
- account_id: str
1183
- ) -> Dict[str, Any]:
1088
+ async def _deploy_control_to_account(self, control: SecurityControl, account_id: str) -> Dict[str, Any]:
1184
1089
  """Deploy a single security control to a specific account."""
1185
-
1090
+
1186
1091
  start_time = time.time()
1187
-
1092
+
1188
1093
  try:
1189
1094
  # Get account session
1190
1095
  account_session = self._assume_cross_account_role(account_id)
1191
1096
  if not account_session:
1192
1097
  account_session = self.session
1193
-
1098
+
1194
1099
  # Check if control is already deployed
1195
- if control.control_id in self.account_profiles.get(account_id, {}).get('deployed_controls', []):
1100
+ if control.control_id in self.account_profiles.get(account_id, {}).get("deployed_controls", []):
1196
1101
  return {
1197
- 'success': True,
1198
- 'message': 'Control already deployed',
1199
- 'deployment_time': time.time() - start_time,
1200
- 'skipped': True
1102
+ "success": True,
1103
+ "message": "Control already deployed",
1104
+ "deployment_time": time.time() - start_time,
1105
+ "skipped": True,
1201
1106
  }
1202
-
1107
+
1203
1108
  # Check if approval is required
1204
1109
  if control.requires_approval and not self.dry_run:
1205
1110
  return {
1206
- 'success': False,
1207
- 'message': 'Approval required for this control',
1208
- 'deployment_time': time.time() - start_time,
1209
- 'approval_required': True
1111
+ "success": False,
1112
+ "message": "Approval required for this control",
1113
+ "deployment_time": time.time() - start_time,
1114
+ "approval_required": True,
1210
1115
  }
1211
-
1116
+
1212
1117
  # Execute deployment based on control type
1213
- deployment_result = await self._execute_control_deployment(
1214
- control, account_session, account_id
1215
- )
1216
-
1217
- deployment_result['deployment_time'] = time.time() - start_time
1218
-
1118
+ deployment_result = await self._execute_control_deployment(control, account_session, account_id)
1119
+
1120
+ deployment_result["deployment_time"] = time.time() - start_time
1121
+
1219
1122
  # Update control status
1220
- if deployment_result['success']:
1123
+ if deployment_result["success"]:
1221
1124
  control.deployed_accounts.append(account_id)
1222
1125
  control.deployment_status = ControlStatus.DEPLOYED
1223
1126
  else:
1224
1127
  control.failed_accounts.append(account_id)
1225
-
1128
+
1226
1129
  except Exception as e:
1227
- deployment_result = {
1228
- 'success': False,
1229
- 'error': str(e),
1230
- 'deployment_time': time.time() - start_time
1231
- }
1232
-
1130
+ deployment_result = {"success": False, "error": str(e), "deployment_time": time.time() - start_time}
1131
+
1233
1132
  return deployment_result
1234
1133
 
1235
1134
  async def _execute_control_deployment(
1236
- self,
1237
- control: SecurityControl,
1238
- session: boto3.Session,
1239
- account_id: str
1135
+ self, control: SecurityControl, session: boto3.Session, account_id: str
1240
1136
  ) -> Dict[str, Any]:
1241
1137
  """Execute the actual deployment of a security control."""
1242
-
1138
+
1243
1139
  if self.dry_run:
1244
1140
  # Simulate deployment in dry run mode
1245
1141
  await asyncio.sleep(0.1) # Simulate deployment time
1246
- return {
1247
- 'success': True,
1248
- 'message': f'DRY RUN: Would deploy {control.control_name}',
1249
- 'dry_run': True
1250
- }
1251
-
1142
+ return {"success": True, "message": f"DRY RUN: Would deploy {control.control_name}", "dry_run": True}
1143
+
1252
1144
  try:
1253
1145
  if control.control_type == SecurityControlType.IAM_BASELINE:
1254
1146
  return await self._deploy_iam_control(control, session, account_id)
@@ -1261,487 +1153,367 @@ class MultiAccountSecurityController:
1261
1153
  elif control.control_type == SecurityControlType.COMPLIANCE_MONITORING:
1262
1154
  return await self._deploy_compliance_control(control, session, account_id)
1263
1155
  else:
1264
- return {
1265
- 'success': False,
1266
- 'message': f'Unsupported control type: {control.control_type.value}'
1267
- }
1268
-
1156
+ return {"success": False, "message": f"Unsupported control type: {control.control_type.value}"}
1157
+
1269
1158
  except Exception as e:
1270
- return {
1271
- 'success': False,
1272
- 'error': str(e),
1273
- 'message': f'Failed to deploy {control.control_name}'
1274
- }
1159
+ return {"success": False, "error": str(e), "message": f"Failed to deploy {control.control_name}"}
1275
1160
 
1276
1161
  async def _deploy_iam_control(
1277
- self,
1278
- control: SecurityControl,
1279
- session: boto3.Session,
1280
- account_id: str
1162
+ self, control: SecurityControl, session: boto3.Session, account_id: str
1281
1163
  ) -> Dict[str, Any]:
1282
1164
  """Deploy IAM-related security control."""
1283
-
1284
- iam = session.client('iam')
1285
-
1286
- if control.control_id == 'IAM-001': # Password Policy
1165
+
1166
+ iam = session.client("iam")
1167
+
1168
+ if control.control_id == "IAM-001": # Password Policy
1287
1169
  template = control.deployment_template
1288
-
1170
+
1289
1171
  try:
1290
1172
  iam.update_account_password_policy(
1291
- MinimumPasswordLength=template['MinimumPasswordLength'],
1292
- RequireUppercaseCharacters=template['RequireUppercaseCharacters'],
1293
- RequireLowercaseCharacters=template['RequireLowercaseCharacters'],
1294
- RequireNumbers=template['RequireNumbers'],
1295
- RequireSymbols=template['RequireSymbols'],
1296
- MaxPasswordAge=template['MaxPasswordAge'],
1297
- PasswordReusePrevention=template['PasswordReusePrevention'],
1298
- HardExpiry=template['HardExpiry']
1173
+ MinimumPasswordLength=template["MinimumPasswordLength"],
1174
+ RequireUppercaseCharacters=template["RequireUppercaseCharacters"],
1175
+ RequireLowercaseCharacters=template["RequireLowercaseCharacters"],
1176
+ RequireNumbers=template["RequireNumbers"],
1177
+ RequireSymbols=template["RequireSymbols"],
1178
+ MaxPasswordAge=template["MaxPasswordAge"],
1179
+ PasswordReusePrevention=template["PasswordReusePrevention"],
1180
+ HardExpiry=template["HardExpiry"],
1299
1181
  )
1300
-
1182
+
1301
1183
  return {
1302
- 'success': True,
1303
- 'message': 'IAM password policy successfully applied',
1304
- 'policy_applied': template
1184
+ "success": True,
1185
+ "message": "IAM password policy successfully applied",
1186
+ "policy_applied": template,
1305
1187
  }
1306
-
1188
+
1307
1189
  except ClientError as e:
1308
- return {
1309
- 'success': False,
1310
- 'error': str(e),
1311
- 'message': 'Failed to apply IAM password policy'
1312
- }
1313
-
1314
- elif control.control_id == 'IAM-002': # Root MFA
1190
+ return {"success": False, "error": str(e), "message": "Failed to apply IAM password policy"}
1191
+
1192
+ elif control.control_id == "IAM-002": # Root MFA
1315
1193
  # This would check and potentially remediate root MFA
1316
1194
  # For safety, this returns success without making changes
1317
1195
  return {
1318
- 'success': True,
1319
- 'message': 'Root MFA check completed (manual verification required)',
1320
- 'manual_verification_required': True
1196
+ "success": True,
1197
+ "message": "Root MFA check completed (manual verification required)",
1198
+ "manual_verification_required": True,
1321
1199
  }
1322
-
1323
- return {
1324
- 'success': False,
1325
- 'message': f'Unknown IAM control: {control.control_id}'
1326
- }
1200
+
1201
+ return {"success": False, "message": f"Unknown IAM control: {control.control_id}"}
1327
1202
 
1328
1203
  async def _deploy_encryption_control(
1329
- self,
1330
- control: SecurityControl,
1331
- session: boto3.Session,
1332
- account_id: str
1204
+ self, control: SecurityControl, session: boto3.Session, account_id: str
1333
1205
  ) -> Dict[str, Any]:
1334
1206
  """Deploy encryption-related security control."""
1335
-
1336
- if control.control_id == 'ENC-001': # S3 Bucket Encryption
1337
- s3 = session.client('s3')
1338
-
1207
+
1208
+ if control.control_id == "ENC-001": # S3 Bucket Encryption
1209
+ s3 = session.client("s3")
1210
+
1339
1211
  try:
1340
1212
  # Get list of buckets
1341
- buckets = s3.list_buckets()['Buckets']
1342
-
1213
+ buckets = s3.list_buckets()["Buckets"]
1214
+
1343
1215
  applied_count = 0
1344
1216
  failed_buckets = []
1345
-
1217
+
1346
1218
  for bucket in buckets[:10]: # Limit for demo
1347
- bucket_name = bucket['Name']
1348
-
1219
+ bucket_name = bucket["Name"]
1220
+
1349
1221
  try:
1350
1222
  # Apply default encryption
1351
1223
  s3.put_bucket_encryption(
1352
1224
  Bucket=bucket_name,
1353
1225
  ServerSideEncryptionConfiguration={
1354
- 'Rules': [
1226
+ "Rules": [
1355
1227
  {
1356
- 'ApplyServerSideEncryptionByDefault': {
1357
- 'SSEAlgorithm': control.deployment_template['encryption_algorithm']
1228
+ "ApplyServerSideEncryptionByDefault": {
1229
+ "SSEAlgorithm": control.deployment_template["encryption_algorithm"]
1358
1230
  }
1359
1231
  }
1360
1232
  ]
1361
- }
1233
+ },
1362
1234
  )
1363
1235
  applied_count += 1
1364
-
1236
+
1365
1237
  except ClientError as e:
1366
- failed_buckets.append({
1367
- 'bucket': bucket_name,
1368
- 'error': str(e)
1369
- })
1370
-
1238
+ failed_buckets.append({"bucket": bucket_name, "error": str(e)})
1239
+
1371
1240
  return {
1372
- 'success': len(failed_buckets) == 0,
1373
- 'message': f'Applied encryption to {applied_count} buckets',
1374
- 'applied_count': applied_count,
1375
- 'failed_buckets': failed_buckets
1241
+ "success": len(failed_buckets) == 0,
1242
+ "message": f"Applied encryption to {applied_count} buckets",
1243
+ "applied_count": applied_count,
1244
+ "failed_buckets": failed_buckets,
1376
1245
  }
1377
-
1246
+
1378
1247
  except ClientError as e:
1379
- return {
1380
- 'success': False,
1381
- 'error': str(e),
1382
- 'message': 'Failed to apply S3 bucket encryption'
1383
- }
1384
-
1385
- elif control.control_id == 'ENC-002': # EBS Encryption
1386
- ec2 = session.client('ec2')
1387
-
1248
+ return {"success": False, "error": str(e), "message": "Failed to apply S3 bucket encryption"}
1249
+
1250
+ elif control.control_id == "ENC-002": # EBS Encryption
1251
+ ec2 = session.client("ec2")
1252
+
1388
1253
  try:
1389
1254
  # Enable EBS encryption by default
1390
1255
  ec2.enable_ebs_encryption_by_default()
1391
-
1392
- return {
1393
- 'success': True,
1394
- 'message': 'EBS encryption by default enabled'
1395
- }
1396
-
1256
+
1257
+ return {"success": True, "message": "EBS encryption by default enabled"}
1258
+
1397
1259
  except ClientError as e:
1398
- return {
1399
- 'success': False,
1400
- 'error': str(e),
1401
- 'message': 'Failed to enable EBS encryption by default'
1402
- }
1403
-
1404
- return {
1405
- 'success': False,
1406
- 'message': f'Unknown encryption control: {control.control_id}'
1407
- }
1260
+ return {"success": False, "error": str(e), "message": "Failed to enable EBS encryption by default"}
1261
+
1262
+ return {"success": False, "message": f"Unknown encryption control: {control.control_id}"}
1408
1263
 
1409
1264
  async def _deploy_network_control(
1410
- self,
1411
- control: SecurityControl,
1412
- session: boto3.Session,
1413
- account_id: str
1265
+ self, control: SecurityControl, session: boto3.Session, account_id: str
1414
1266
  ) -> Dict[str, Any]:
1415
1267
  """Deploy network security control."""
1416
-
1417
- if control.control_id == 'NET-001': # VPC Flow Logs
1418
- ec2 = session.client('ec2')
1419
- logs = session.client('logs')
1420
-
1268
+
1269
+ if control.control_id == "NET-001": # VPC Flow Logs
1270
+ ec2 = session.client("ec2")
1271
+ logs = session.client("logs")
1272
+
1421
1273
  try:
1422
1274
  # Get VPCs without flow logs
1423
- vpcs = ec2.describe_vpcs()['Vpcs']
1424
- flow_logs = ec2.describe_flow_logs()['FlowLogs']
1425
-
1426
- vpc_with_flow_logs = {
1427
- fl['ResourceId'] for fl in flow_logs
1428
- if fl['ResourceType'] == 'VPC'
1429
- }
1430
-
1431
- vpcs_needing_flow_logs = [
1432
- vpc['VpcId'] for vpc in vpcs
1433
- if vpc['VpcId'] not in vpc_with_flow_logs
1434
- ]
1435
-
1275
+ vpcs = ec2.describe_vpcs()["Vpcs"]
1276
+ flow_logs = ec2.describe_flow_logs()["FlowLogs"]
1277
+
1278
+ vpc_with_flow_logs = {fl["ResourceId"] for fl in flow_logs if fl["ResourceType"] == "VPC"}
1279
+
1280
+ vpcs_needing_flow_logs = [vpc["VpcId"] for vpc in vpcs if vpc["VpcId"] not in vpc_with_flow_logs]
1281
+
1436
1282
  enabled_count = 0
1437
1283
  failed_vpcs = []
1438
-
1284
+
1439
1285
  for vpc_id in vpcs_needing_flow_logs:
1440
1286
  try:
1441
1287
  # Create log group
1442
- log_group_name = f'/aws/vpc/flowlogs/{vpc_id}'
1443
-
1288
+ log_group_name = f"/aws/vpc/flowlogs/{vpc_id}"
1289
+
1444
1290
  try:
1445
1291
  logs.create_log_group(logGroupName=log_group_name)
1446
1292
  except logs.exceptions.ResourceAlreadyExistsException:
1447
1293
  pass # Log group already exists
1448
-
1294
+
1449
1295
  # Create flow log
1450
1296
  ec2.create_flow_logs(
1451
1297
  ResourceIds=[vpc_id],
1452
- ResourceType='VPC',
1453
- TrafficType='ALL',
1454
- LogDestinationType='cloud-watch-logs',
1455
- LogGroupName=log_group_name
1298
+ ResourceType="VPC",
1299
+ TrafficType="ALL",
1300
+ LogDestinationType="cloud-watch-logs",
1301
+ LogGroupName=log_group_name,
1456
1302
  )
1457
-
1303
+
1458
1304
  enabled_count += 1
1459
-
1305
+
1460
1306
  except ClientError as e:
1461
- failed_vpcs.append({
1462
- 'vpc_id': vpc_id,
1463
- 'error': str(e)
1464
- })
1465
-
1307
+ failed_vpcs.append({"vpc_id": vpc_id, "error": str(e)})
1308
+
1466
1309
  return {
1467
- 'success': len(failed_vpcs) == 0,
1468
- 'message': f'Enabled flow logs for {enabled_count} VPCs',
1469
- 'enabled_count': enabled_count,
1470
- 'failed_vpcs': failed_vpcs
1310
+ "success": len(failed_vpcs) == 0,
1311
+ "message": f"Enabled flow logs for {enabled_count} VPCs",
1312
+ "enabled_count": enabled_count,
1313
+ "failed_vpcs": failed_vpcs,
1471
1314
  }
1472
-
1315
+
1473
1316
  except ClientError as e:
1474
- return {
1475
- 'success': False,
1476
- 'error': str(e),
1477
- 'message': 'Failed to deploy VPC flow logs'
1478
- }
1479
-
1480
- return {
1481
- 'success': False,
1482
- 'message': f'Unknown network control: {control.control_id}'
1483
- }
1317
+ return {"success": False, "error": str(e), "message": "Failed to deploy VPC flow logs"}
1318
+
1319
+ return {"success": False, "message": f"Unknown network control: {control.control_id}"}
1484
1320
 
1485
1321
  async def _deploy_audit_control(
1486
- self,
1487
- control: SecurityControl,
1488
- session: boto3.Session,
1489
- account_id: str
1322
+ self, control: SecurityControl, session: boto3.Session, account_id: str
1490
1323
  ) -> Dict[str, Any]:
1491
1324
  """Deploy audit logging control."""
1492
-
1493
- if control.control_id == 'AUD-001': # CloudTrail
1325
+
1326
+ if control.control_id == "AUD-001": # CloudTrail
1494
1327
  # CloudTrail deployment would be complex and organization-wide
1495
1328
  # For safety, return success without making changes
1496
1329
  return {
1497
- 'success': True,
1498
- 'message': 'CloudTrail audit logging verified (organization-wide configuration)',
1499
- 'organization_wide': True
1330
+ "success": True,
1331
+ "message": "CloudTrail audit logging verified (organization-wide configuration)",
1332
+ "organization_wide": True,
1500
1333
  }
1501
-
1502
- return {
1503
- 'success': False,
1504
- 'message': f'Unknown audit control: {control.control_id}'
1505
- }
1334
+
1335
+ return {"success": False, "message": f"Unknown audit control: {control.control_id}"}
1506
1336
 
1507
1337
  async def _deploy_compliance_control(
1508
- self,
1509
- control: SecurityControl,
1510
- session: boto3.Session,
1511
- account_id: str
1338
+ self, control: SecurityControl, session: boto3.Session, account_id: str
1512
1339
  ) -> Dict[str, Any]:
1513
1340
  """Deploy compliance monitoring control."""
1514
-
1515
- if control.control_id == 'CMP-001': # AWS Config
1516
- config = session.client('config')
1517
-
1341
+
1342
+ if control.control_id == "CMP-001": # AWS Config
1343
+ config = session.client("config")
1344
+
1518
1345
  try:
1519
1346
  # Check if Config is already set up
1520
1347
  try:
1521
- recorders = config.describe_configuration_recorders()['ConfigurationRecorders']
1348
+ recorders = config.describe_configuration_recorders()["ConfigurationRecorders"]
1522
1349
  if recorders:
1523
- return {
1524
- 'success': True,
1525
- 'message': 'AWS Config already configured',
1526
- 'already_configured': True
1527
- }
1350
+ return {"success": True, "message": "AWS Config already configured", "already_configured": True}
1528
1351
  except ClientError:
1529
1352
  pass
1530
-
1353
+
1531
1354
  # Set up Config (simplified version)
1532
1355
  config.put_configuration_recorder(
1533
1356
  ConfigurationRecorder={
1534
- 'name': 'default',
1535
- 'roleARN': f'arn:aws:iam::{account_id}:role/aws-config-role',
1536
- 'recordingGroup': {
1537
- 'allSupported': True,
1538
- 'includeGlobalResourceTypes': True
1539
- }
1357
+ "name": "default",
1358
+ "roleARN": f"arn:aws:iam::{account_id}:role/aws-config-role",
1359
+ "recordingGroup": {"allSupported": True, "includeGlobalResourceTypes": True},
1540
1360
  }
1541
1361
  )
1542
-
1543
- return {
1544
- 'success': True,
1545
- 'message': 'AWS Config configuration recorder created'
1546
- }
1547
-
1362
+
1363
+ return {"success": True, "message": "AWS Config configuration recorder created"}
1364
+
1548
1365
  except ClientError as e:
1549
- return {
1550
- 'success': False,
1551
- 'error': str(e),
1552
- 'message': 'Failed to set up AWS Config'
1553
- }
1554
-
1555
- return {
1556
- 'success': False,
1557
- 'message': f'Unknown compliance control: {control.control_id}'
1558
- }
1366
+ return {"success": False, "error": str(e), "message": "Failed to set up AWS Config"}
1367
+
1368
+ return {"success": False, "message": f"Unknown compliance control: {control.control_id}"}
1559
1369
 
1560
1370
  async def _validate_control_deployments(
1561
- self,
1562
- deployment_results: Dict[str, Any],
1563
- target_accounts: List[str]
1371
+ self, deployment_results: Dict[str, Any], target_accounts: List[str]
1564
1372
  ) -> Dict[str, Any]:
1565
1373
  """Validate that deployed controls are working correctly."""
1566
-
1374
+
1567
1375
  print_info("Validating control deployments...")
1568
-
1376
+
1569
1377
  validation_results = {
1570
- 'total_validations': 0,
1571
- 'successful_validations': 0,
1572
- 'failed_validations': 0,
1573
- 'validation_details': {}
1378
+ "total_validations": 0,
1379
+ "successful_validations": 0,
1380
+ "failed_validations": 0,
1381
+ "validation_details": {},
1574
1382
  }
1575
-
1383
+
1576
1384
  # For each successfully deployed control, run validation checks
1577
- for control_id, account_results in deployment_results.get('control_results', {}).items():
1578
-
1385
+ for control_id, account_results in deployment_results.get("control_results", {}).items():
1579
1386
  # Find the control definition
1580
1387
  control = next((c for c in self.security_controls if c.control_id == control_id), None)
1581
1388
  if not control:
1582
1389
  continue
1583
-
1584
- validation_results['validation_details'][control_id] = {}
1585
-
1390
+
1391
+ validation_results["validation_details"][control_id] = {}
1392
+
1586
1393
  for account_id, deployment_result in account_results.items():
1587
- if deployment_result.get('success', False):
1588
-
1394
+ if deployment_result.get("success", False):
1589
1395
  # Run validation checks for this control-account combination
1590
- validation_result = await self._validate_control_in_account(
1591
- control, account_id
1592
- )
1593
-
1594
- validation_results['validation_details'][control_id][account_id] = validation_result
1595
- validation_results['total_validations'] += 1
1596
-
1597
- if validation_result.get('valid', False):
1598
- validation_results['successful_validations'] += 1
1396
+ validation_result = await self._validate_control_in_account(control, account_id)
1397
+
1398
+ validation_results["validation_details"][control_id][account_id] = validation_result
1399
+ validation_results["total_validations"] += 1
1400
+
1401
+ if validation_result.get("valid", False):
1402
+ validation_results["successful_validations"] += 1
1599
1403
  else:
1600
- validation_results['failed_validations'] += 1
1601
-
1404
+ validation_results["failed_validations"] += 1
1405
+
1602
1406
  success_rate = (
1603
- validation_results['successful_validations'] /
1604
- max(1, validation_results['total_validations'])
1407
+ validation_results["successful_validations"] / max(1, validation_results["total_validations"])
1605
1408
  ) * 100
1606
-
1409
+
1607
1410
  print_info(f"Validation completed: {success_rate:.1f}% success rate")
1608
-
1411
+
1609
1412
  return validation_results
1610
1413
 
1611
- async def _validate_control_in_account(
1612
- self,
1613
- control: SecurityControl,
1614
- account_id: str
1615
- ) -> Dict[str, Any]:
1414
+ async def _validate_control_in_account(self, control: SecurityControl, account_id: str) -> Dict[str, Any]:
1616
1415
  """Validate a specific control in a specific account."""
1617
-
1416
+
1618
1417
  try:
1619
1418
  # Get account session for validation
1620
1419
  account_session = self._assume_cross_account_role(account_id)
1621
1420
  if not account_session:
1622
1421
  account_session = self.session
1623
-
1624
- validation_results = {
1625
- 'valid': True,
1626
- 'checks_passed': [],
1627
- 'checks_failed': [],
1628
- 'details': {}
1629
- }
1630
-
1422
+
1423
+ validation_results = {"valid": True, "checks_passed": [], "checks_failed": [], "details": {}}
1424
+
1631
1425
  # Run control-specific validation checks
1632
1426
  for check in control.validation_checks:
1633
- check_result = await self._run_validation_check(
1634
- check, control, account_session, account_id
1635
- )
1636
-
1637
- if check_result.get('passed', False):
1638
- validation_results['checks_passed'].append(check)
1427
+ check_result = await self._run_validation_check(check, control, account_session, account_id)
1428
+
1429
+ if check_result.get("passed", False):
1430
+ validation_results["checks_passed"].append(check)
1639
1431
  else:
1640
- validation_results['checks_failed'].append(check)
1641
- validation_results['valid'] = False
1642
-
1643
- validation_results['details'][check] = check_result
1644
-
1432
+ validation_results["checks_failed"].append(check)
1433
+ validation_results["valid"] = False
1434
+
1435
+ validation_results["details"][check] = check_result
1436
+
1645
1437
  return validation_results
1646
-
1438
+
1647
1439
  except Exception as e:
1648
- return {
1649
- 'valid': False,
1650
- 'error': str(e),
1651
- 'checks_passed': [],
1652
- 'checks_failed': control.validation_checks
1653
- }
1440
+ return {"valid": False, "error": str(e), "checks_passed": [], "checks_failed": control.validation_checks}
1654
1441
 
1655
1442
  async def _run_validation_check(
1656
- self,
1657
- check: str,
1658
- control: SecurityControl,
1659
- session: boto3.Session,
1660
- account_id: str
1443
+ self, check: str, control: SecurityControl, session: boto3.Session, account_id: str
1661
1444
  ) -> Dict[str, Any]:
1662
1445
  """Run a specific validation check."""
1663
-
1446
+
1664
1447
  # Simplified validation checks
1665
- if check == 'verify_password_policy_applied':
1448
+ if check == "verify_password_policy_applied":
1666
1449
  try:
1667
- iam = session.client('iam')
1450
+ iam = session.client("iam")
1668
1451
  policy = iam.get_account_password_policy()
1669
-
1452
+
1670
1453
  # Check if policy meets requirements
1671
1454
  template = control.deployment_template
1672
- current = policy['PasswordPolicy']
1673
-
1674
- meets_requirements = (
1675
- current.get('MinimumPasswordLength', 0) >= template['MinimumPasswordLength']
1676
- )
1677
-
1678
- return {
1679
- 'passed': meets_requirements,
1680
- 'details': current
1681
- }
1682
-
1455
+ current = policy["PasswordPolicy"]
1456
+
1457
+ meets_requirements = current.get("MinimumPasswordLength", 0) >= template["MinimumPasswordLength"]
1458
+
1459
+ return {"passed": meets_requirements, "details": current}
1460
+
1683
1461
  except ClientError:
1684
- return {'passed': False, 'error': 'No password policy found'}
1685
-
1686
- elif check == 'verify_bucket_encryption_enabled':
1462
+ return {"passed": False, "error": "No password policy found"}
1463
+
1464
+ elif check == "verify_bucket_encryption_enabled":
1687
1465
  try:
1688
- s3 = session.client('s3')
1689
- buckets = s3.list_buckets()['Buckets']
1690
-
1466
+ s3 = session.client("s3")
1467
+ buckets = s3.list_buckets()["Buckets"]
1468
+
1691
1469
  encrypted_buckets = 0
1692
1470
  total_buckets = min(len(buckets), 10) # Limit for validation
1693
-
1471
+
1694
1472
  for bucket in buckets[:10]:
1695
1473
  try:
1696
- s3.get_bucket_encryption(Bucket=bucket['Name'])
1474
+ s3.get_bucket_encryption(Bucket=bucket["Name"])
1697
1475
  encrypted_buckets += 1
1698
1476
  except ClientError:
1699
1477
  pass # Bucket not encrypted
1700
-
1478
+
1701
1479
  encryption_rate = encrypted_buckets / max(1, total_buckets)
1702
-
1480
+
1703
1481
  return {
1704
- 'passed': encryption_rate >= 0.8, # 80% threshold
1705
- 'encrypted_buckets': encrypted_buckets,
1706
- 'total_buckets': total_buckets,
1707
- 'encryption_rate': encryption_rate
1482
+ "passed": encryption_rate >= 0.8, # 80% threshold
1483
+ "encrypted_buckets": encrypted_buckets,
1484
+ "total_buckets": total_buckets,
1485
+ "encryption_rate": encryption_rate,
1708
1486
  }
1709
-
1487
+
1710
1488
  except ClientError as e:
1711
- return {'passed': False, 'error': str(e)}
1712
-
1713
- elif check == 'verify_flow_logs_enabled':
1489
+ return {"passed": False, "error": str(e)}
1490
+
1491
+ elif check == "verify_flow_logs_enabled":
1714
1492
  try:
1715
- ec2 = session.client('ec2')
1716
-
1717
- vpcs = ec2.describe_vpcs()['Vpcs']
1718
- flow_logs = ec2.describe_flow_logs()['FlowLogs']
1719
-
1720
- vpc_with_flow_logs = {
1721
- fl['ResourceId'] for fl in flow_logs
1722
- if fl['ResourceType'] == 'VPC'
1723
- }
1724
-
1493
+ ec2 = session.client("ec2")
1494
+
1495
+ vpcs = ec2.describe_vpcs()["Vpcs"]
1496
+ flow_logs = ec2.describe_flow_logs()["FlowLogs"]
1497
+
1498
+ vpc_with_flow_logs = {fl["ResourceId"] for fl in flow_logs if fl["ResourceType"] == "VPC"}
1499
+
1725
1500
  vpcs_with_logs = len(vpc_with_flow_logs)
1726
1501
  total_vpcs = len(vpcs)
1727
-
1502
+
1728
1503
  coverage_rate = vpcs_with_logs / max(1, total_vpcs)
1729
-
1504
+
1730
1505
  return {
1731
- 'passed': coverage_rate >= 0.8, # 80% coverage threshold
1732
- 'vpcs_with_logs': vpcs_with_logs,
1733
- 'total_vpcs': total_vpcs,
1734
- 'coverage_rate': coverage_rate
1506
+ "passed": coverage_rate >= 0.8, # 80% coverage threshold
1507
+ "vpcs_with_logs": vpcs_with_logs,
1508
+ "total_vpcs": total_vpcs,
1509
+ "coverage_rate": coverage_rate,
1735
1510
  }
1736
-
1511
+
1737
1512
  except ClientError as e:
1738
- return {'passed': False, 'error': str(e)}
1739
-
1513
+ return {"passed": False, "error": str(e)}
1514
+
1740
1515
  # Default: assume check passed for unknown checks
1741
- return {
1742
- 'passed': True,
1743
- 'message': f'Validation check {check} not implemented'
1744
- }
1516
+ return {"passed": True, "message": f"Validation check {check} not implemented"}
1745
1517
 
1746
1518
  async def _generate_deployment_report(
1747
1519
  self,
@@ -1750,69 +1522,70 @@ class MultiAccountSecurityController:
1750
1522
  controls_deployed: List[SecurityControl],
1751
1523
  target_accounts: List[str],
1752
1524
  deployment_results: Dict[str, Any],
1753
- validation_results: Dict[str, Any]
1525
+ validation_results: Dict[str, Any],
1754
1526
  ) -> MultiAccountSecurityReport:
1755
1527
  """Generate comprehensive deployment report."""
1756
-
1528
+
1757
1529
  # Calculate overall metrics
1758
1530
  total_deployments = sum(
1759
- len(account_results)
1760
- for account_results in deployment_results.get('control_results', {}).values()
1531
+ len(account_results) for account_results in deployment_results.get("control_results", {}).values()
1761
1532
  )
1762
-
1533
+
1763
1534
  successful_deployments = sum(
1764
- 1 for account_results in deployment_results.get('control_results', {}).values()
1535
+ 1
1536
+ for account_results in deployment_results.get("control_results", {}).values()
1765
1537
  for result in account_results.values()
1766
- if result.get('success', False)
1538
+ if result.get("success", False)
1767
1539
  )
1768
-
1540
+
1769
1541
  overall_success_rate = (successful_deployments / max(1, total_deployments)) * 100
1770
-
1542
+
1771
1543
  # Calculate compliance scores
1772
- compliance_scores = self._calculate_compliance_scores(
1773
- controls_deployed, deployment_results, validation_results
1774
- )
1775
-
1544
+ compliance_scores = self._calculate_compliance_scores(controls_deployed, deployment_results, validation_results)
1545
+
1776
1546
  # Identify high-priority findings
1777
- high_priority_findings = self._identify_high_priority_findings(
1778
- deployment_results, validation_results
1779
- )
1780
-
1547
+ high_priority_findings = self._identify_high_priority_findings(deployment_results, validation_results)
1548
+
1781
1549
  # Generate cost analysis
1782
- cost_analysis = self._calculate_deployment_costs(
1783
- controls_deployed, target_accounts, deployment_results
1784
- )
1785
-
1550
+ cost_analysis = self._calculate_deployment_costs(controls_deployed, target_accounts, deployment_results)
1551
+
1786
1552
  # Generate recommendations
1787
1553
  recommendations = self._generate_deployment_recommendations(
1788
1554
  deployment_results, validation_results, overall_success_rate
1789
1555
  )
1790
-
1556
+
1791
1557
  # Create executive summary
1792
1558
  executive_summary = {
1793
- 'deployment_success_rate': overall_success_rate,
1794
- 'accounts_secured': len([
1795
- account_id for account_id in target_accounts
1796
- if any(
1797
- account_results.get(account_id, {}).get('success', False)
1798
- for account_results in deployment_results.get('control_results', {}).values()
1799
- )
1800
- ]),
1801
- 'controls_deployed_successfully': len([
1802
- control for control in controls_deployed
1803
- if any(
1804
- result.get('success', False)
1805
- for result in deployment_results.get('control_results', {}).get(control.control_id, {}).values()
1806
- )
1807
- ]),
1808
- 'validation_success_rate': (
1809
- validation_results.get('successful_validations', 0) /
1810
- max(1, validation_results.get('total_validations', 1))
1811
- ) * 100,
1812
- 'estimated_risk_reduction': self._calculate_risk_reduction(controls_deployed, successful_deployments),
1813
- 'business_impact': 'Significant improvement in organization security posture'
1559
+ "deployment_success_rate": overall_success_rate,
1560
+ "accounts_secured": len(
1561
+ [
1562
+ account_id
1563
+ for account_id in target_accounts
1564
+ if any(
1565
+ account_results.get(account_id, {}).get("success", False)
1566
+ for account_results in deployment_results.get("control_results", {}).values()
1567
+ )
1568
+ ]
1569
+ ),
1570
+ "controls_deployed_successfully": len(
1571
+ [
1572
+ control
1573
+ for control in controls_deployed
1574
+ if any(
1575
+ result.get("success", False)
1576
+ for result in deployment_results.get("control_results", {}).get(control.control_id, {}).values()
1577
+ )
1578
+ ]
1579
+ ),
1580
+ "validation_success_rate": (
1581
+ validation_results.get("successful_validations", 0)
1582
+ / max(1, validation_results.get("total_validations", 1))
1583
+ )
1584
+ * 100,
1585
+ "estimated_risk_reduction": self._calculate_risk_reduction(controls_deployed, successful_deployments),
1586
+ "business_impact": "Significant improvement in organization security posture",
1814
1587
  }
1815
-
1588
+
1816
1589
  return MultiAccountSecurityReport(
1817
1590
  report_id=deployment_id,
1818
1591
  timestamp=start_time,
@@ -1823,207 +1596,194 @@ class MultiAccountSecurityController:
1823
1596
  overall_security_score=overall_success_rate,
1824
1597
  compliance_scores=compliance_scores,
1825
1598
  high_priority_findings=high_priority_findings,
1826
- deployment_summary=deployment_results.get('summary', {}),
1599
+ deployment_summary=deployment_results.get("summary", {}),
1827
1600
  cost_analysis=cost_analysis,
1828
1601
  recommendations=recommendations,
1829
- executive_summary=executive_summary
1602
+ executive_summary=executive_summary,
1830
1603
  )
1831
1604
 
1832
1605
  def _calculate_compliance_scores(
1833
1606
  self,
1834
1607
  controls_deployed: List[SecurityControl],
1835
1608
  deployment_results: Dict[str, Any],
1836
- validation_results: Dict[str, Any]
1609
+ validation_results: Dict[str, Any],
1837
1610
  ) -> Dict[str, float]:
1838
1611
  """Calculate compliance scores by framework."""
1839
-
1612
+
1840
1613
  compliance_scores = {}
1841
-
1614
+
1842
1615
  # Group controls by compliance framework
1843
1616
  frameworks = set()
1844
1617
  for control in controls_deployed:
1845
1618
  frameworks.update(control.compliance_frameworks)
1846
-
1619
+
1847
1620
  for framework in frameworks:
1848
1621
  framework_controls = [
1849
- control for control in controls_deployed
1850
- if framework in control.compliance_frameworks
1622
+ control for control in controls_deployed if framework in control.compliance_frameworks
1851
1623
  ]
1852
-
1624
+
1853
1625
  if not framework_controls:
1854
1626
  continue
1855
-
1627
+
1856
1628
  # Calculate success rate for this framework
1857
1629
  successful_count = 0
1858
1630
  total_count = 0
1859
-
1631
+
1860
1632
  for control in framework_controls:
1861
- control_results = deployment_results.get('control_results', {}).get(control.control_id, {})
1862
-
1633
+ control_results = deployment_results.get("control_results", {}).get(control.control_id, {})
1634
+
1863
1635
  for account_result in control_results.values():
1864
1636
  total_count += 1
1865
- if account_result.get('success', False):
1637
+ if account_result.get("success", False):
1866
1638
  successful_count += 1
1867
-
1639
+
1868
1640
  framework_score = (successful_count / max(1, total_count)) * 100
1869
1641
  compliance_scores[framework] = framework_score
1870
-
1642
+
1871
1643
  return compliance_scores
1872
1644
 
1873
1645
  def _identify_high_priority_findings(
1874
- self,
1875
- deployment_results: Dict[str, Any],
1876
- validation_results: Dict[str, Any]
1646
+ self, deployment_results: Dict[str, Any], validation_results: Dict[str, Any]
1877
1647
  ) -> List[Dict[str, Any]]:
1878
1648
  """Identify high-priority security findings that require attention."""
1879
-
1649
+
1880
1650
  findings = []
1881
-
1651
+
1882
1652
  # Failed deployments for critical controls
1883
- for control_id, account_results in deployment_results.get('control_results', {}).items():
1884
-
1653
+ for control_id, account_results in deployment_results.get("control_results", {}).items():
1885
1654
  control = next((c for c in self.security_controls if c.control_id == control_id), None)
1886
1655
  if not control:
1887
1656
  continue
1888
-
1657
+
1889
1658
  failed_accounts = [
1890
- account_id for account_id, result in account_results.items()
1891
- if not result.get('success', False)
1659
+ account_id for account_id, result in account_results.items() if not result.get("success", False)
1892
1660
  ]
1893
-
1661
+
1894
1662
  if failed_accounts and control.requires_approval:
1895
- findings.append({
1896
- 'type': 'deployment_failure',
1897
- 'severity': 'HIGH',
1898
- 'control_id': control_id,
1899
- 'control_name': control.control_name,
1900
- 'failed_accounts': failed_accounts,
1901
- 'message': f'{control.control_name} failed to deploy to {len(failed_accounts)} accounts',
1902
- 'recommendation': f'Review deployment logs and retry deployment for {control.control_name}'
1903
- })
1904
-
1663
+ findings.append(
1664
+ {
1665
+ "type": "deployment_failure",
1666
+ "severity": "HIGH",
1667
+ "control_id": control_id,
1668
+ "control_name": control.control_name,
1669
+ "failed_accounts": failed_accounts,
1670
+ "message": f"{control.control_name} failed to deploy to {len(failed_accounts)} accounts",
1671
+ "recommendation": f"Review deployment logs and retry deployment for {control.control_name}",
1672
+ }
1673
+ )
1674
+
1905
1675
  # Failed validations
1906
- for control_id, account_validations in validation_results.get('validation_details', {}).items():
1907
-
1676
+ for control_id, account_validations in validation_results.get("validation_details", {}).items():
1908
1677
  control = next((c for c in self.security_controls if c.control_id == control_id), None)
1909
1678
  if not control:
1910
1679
  continue
1911
-
1680
+
1912
1681
  failed_validations = [
1913
- account_id for account_id, validation in account_validations.items()
1914
- if not validation.get('valid', False)
1682
+ account_id
1683
+ for account_id, validation in account_validations.items()
1684
+ if not validation.get("valid", False)
1915
1685
  ]
1916
-
1686
+
1917
1687
  if failed_validations:
1918
- findings.append({
1919
- 'type': 'validation_failure',
1920
- 'severity': 'MEDIUM',
1921
- 'control_id': control_id,
1922
- 'control_name': control.control_name,
1923
- 'failed_accounts': failed_validations,
1924
- 'message': f'{control.control_name} validation failed in {len(failed_validations)} accounts',
1925
- 'recommendation': f'Investigate and remediate validation failures for {control.control_name}'
1926
- })
1927
-
1688
+ findings.append(
1689
+ {
1690
+ "type": "validation_failure",
1691
+ "severity": "MEDIUM",
1692
+ "control_id": control_id,
1693
+ "control_name": control.control_name,
1694
+ "failed_accounts": failed_validations,
1695
+ "message": f"{control.control_name} validation failed in {len(failed_validations)} accounts",
1696
+ "recommendation": f"Investigate and remediate validation failures for {control.control_name}",
1697
+ }
1698
+ )
1699
+
1928
1700
  # Sort by severity
1929
- severity_order = {'HIGH': 3, 'MEDIUM': 2, 'LOW': 1}
1930
- findings.sort(key=lambda x: severity_order.get(x['severity'], 0), reverse=True)
1931
-
1701
+ severity_order = {"HIGH": 3, "MEDIUM": 2, "LOW": 1}
1702
+ findings.sort(key=lambda x: severity_order.get(x["severity"], 0), reverse=True)
1703
+
1932
1704
  return findings
1933
1705
 
1934
1706
  def _calculate_deployment_costs(
1935
- self,
1936
- controls_deployed: List[SecurityControl],
1937
- target_accounts: List[str],
1938
- deployment_results: Dict[str, Any]
1707
+ self, controls_deployed: List[SecurityControl], target_accounts: List[str], deployment_results: Dict[str, Any]
1939
1708
  ) -> Dict[str, float]:
1940
1709
  """Calculate costs associated with security control deployment."""
1941
-
1710
+
1942
1711
  # Simplified cost calculation
1943
1712
  base_cost_per_account = 50.0 # Base monthly cost per account
1944
1713
  cost_per_control = 10.0 # Additional cost per control
1945
-
1714
+
1946
1715
  successful_deployments = sum(
1947
- 1 for account_results in deployment_results.get('control_results', {}).values()
1716
+ 1
1717
+ for account_results in deployment_results.get("control_results", {}).values()
1948
1718
  for result in account_results.values()
1949
- if result.get('success', False)
1719
+ if result.get("success", False)
1950
1720
  )
1951
-
1721
+
1952
1722
  monthly_operational_cost = (
1953
- len(target_accounts) * base_cost_per_account +
1954
- successful_deployments * cost_per_control
1723
+ len(target_accounts) * base_cost_per_account + successful_deployments * cost_per_control
1955
1724
  )
1956
-
1725
+
1957
1726
  # One-time deployment cost
1958
- deployment_hours = sum(
1959
- control.estimated_deployment_time for control in controls_deployed
1960
- ) / 60.0 # Convert minutes to hours
1961
-
1727
+ deployment_hours = (
1728
+ sum(control.estimated_deployment_time for control in controls_deployed) / 60.0
1729
+ ) # Convert minutes to hours
1730
+
1962
1731
  deployment_cost = deployment_hours * 150.0 # $150/hour for security engineering
1963
-
1732
+
1964
1733
  # Calculate savings from risk reduction
1965
- risk_reduction_value = self._calculate_risk_reduction_value(
1966
- controls_deployed, successful_deployments
1967
- )
1968
-
1734
+ risk_reduction_value = self._calculate_risk_reduction_value(controls_deployed, successful_deployments)
1735
+
1969
1736
  return {
1970
- 'monthly_operational_cost': monthly_operational_cost,
1971
- 'one_time_deployment_cost': deployment_cost,
1972
- 'annual_risk_reduction_value': risk_reduction_value,
1973
- 'roi_percentage': ((risk_reduction_value - monthly_operational_cost * 12) /
1974
- (monthly_operational_cost * 12)) * 100 if monthly_operational_cost > 0 else 0
1737
+ "monthly_operational_cost": monthly_operational_cost,
1738
+ "one_time_deployment_cost": deployment_cost,
1739
+ "annual_risk_reduction_value": risk_reduction_value,
1740
+ "roi_percentage": ((risk_reduction_value - monthly_operational_cost * 12) / (monthly_operational_cost * 12))
1741
+ * 100
1742
+ if monthly_operational_cost > 0
1743
+ else 0,
1975
1744
  }
1976
1745
 
1977
1746
  def _calculate_risk_reduction_value(
1978
- self,
1979
- controls_deployed: List[SecurityControl],
1980
- successful_deployments: int
1747
+ self, controls_deployed: List[SecurityControl], successful_deployments: int
1981
1748
  ) -> float:
1982
1749
  """Calculate the business value of risk reduction."""
1983
-
1750
+
1984
1751
  # Base risk reduction value per successful control deployment
1985
1752
  base_value_per_control = 25000.0 # $25K annual value per control
1986
-
1753
+
1987
1754
  # Multiply by success rate
1988
1755
  total_value = successful_deployments * base_value_per_control
1989
-
1756
+
1990
1757
  # Apply diminishing returns for multiple controls
1991
1758
  if len(controls_deployed) > 1:
1992
- total_value *= (1 + 0.1 * (len(controls_deployed) - 1))
1993
-
1759
+ total_value *= 1 + 0.1 * (len(controls_deployed) - 1)
1760
+
1994
1761
  return total_value
1995
1762
 
1996
- def _calculate_risk_reduction(
1997
- self,
1998
- controls_deployed: List[SecurityControl],
1999
- successful_deployments: int
2000
- ) -> str:
1763
+ def _calculate_risk_reduction(self, controls_deployed: List[SecurityControl], successful_deployments: int) -> str:
2001
1764
  """Calculate estimated risk reduction percentage."""
2002
-
1765
+
2003
1766
  if not controls_deployed:
2004
1767
  return "0%"
2005
-
1768
+
2006
1769
  # Each successful control deployment reduces risk
2007
1770
  base_reduction = 15.0 # 15% base reduction per control
2008
1771
  total_controls = len(self.security_controls)
2009
1772
  deployed_controls = len(controls_deployed)
2010
-
1773
+
2011
1774
  success_rate = successful_deployments / max(1, deployed_controls * len(self.account_profiles))
2012
-
1775
+
2013
1776
  risk_reduction = (deployed_controls / total_controls) * base_reduction * success_rate
2014
-
1777
+
2015
1778
  return f"{min(95, int(risk_reduction))}%" # Cap at 95%
2016
1779
 
2017
1780
  def _generate_deployment_recommendations(
2018
- self,
2019
- deployment_results: Dict[str, Any],
2020
- validation_results: Dict[str, Any],
2021
- overall_success_rate: float
1781
+ self, deployment_results: Dict[str, Any], validation_results: Dict[str, Any], overall_success_rate: float
2022
1782
  ) -> List[str]:
2023
1783
  """Generate actionable recommendations based on deployment results."""
2024
-
1784
+
2025
1785
  recommendations = []
2026
-
1786
+
2027
1787
  # Success rate recommendations
2028
1788
  if overall_success_rate < 80:
2029
1789
  recommendations.append(
@@ -2032,55 +1792,56 @@ class MultiAccountSecurityController:
2032
1792
  )
2033
1793
  elif overall_success_rate >= 95:
2034
1794
  recommendations.append(
2035
- "Excellent deployment success rate! Consider expanding to additional "
2036
- "security controls or accounts."
1795
+ "Excellent deployment success rate! Consider expanding to additional security controls or accounts."
2037
1796
  )
2038
-
1797
+
2039
1798
  # Failed control recommendations
2040
1799
  failed_controls = [
2041
- control_id for control_id, account_results in deployment_results.get('control_results', {}).items()
2042
- if any(not result.get('success', False) for result in account_results.values())
1800
+ control_id
1801
+ for control_id, account_results in deployment_results.get("control_results", {}).items()
1802
+ if any(not result.get("success", False) for result in account_results.values())
2043
1803
  ]
2044
-
1804
+
2045
1805
  if failed_controls:
2046
1806
  recommendations.append(
2047
1807
  f"Review and retry deployment for failed controls: {', '.join(failed_controls[:5])}. "
2048
1808
  "Check account permissions and cross-account role configuration."
2049
1809
  )
2050
-
1810
+
2051
1811
  # Validation recommendations
2052
1812
  validation_success_rate = (
2053
- validation_results.get('successful_validations', 0) /
2054
- max(1, validation_results.get('total_validations', 1))
1813
+ validation_results.get("successful_validations", 0) / max(1, validation_results.get("total_validations", 1))
2055
1814
  ) * 100
2056
-
1815
+
2057
1816
  if validation_success_rate < 90:
2058
1817
  recommendations.append(
2059
1818
  "Validation success rate is below 90%. Review deployed controls "
2060
1819
  "and ensure they are functioning correctly."
2061
1820
  )
2062
-
1821
+
2063
1822
  # Account-specific recommendations
2064
1823
  if len(self.account_profiles) > self.max_concurrent_accounts:
2065
1824
  recommendations.append(
2066
1825
  f"Organization has more than {self.max_concurrent_accounts} accounts. "
2067
1826
  "Consider implementing automated deployment pipelines for scale."
2068
1827
  )
2069
-
1828
+
2070
1829
  # Security improvement recommendations
2071
- recommendations.extend([
2072
- "Implement continuous monitoring for deployed security controls",
2073
- "Set up automated alerting for security control drift or failures",
2074
- "Schedule regular security control validation and updates",
2075
- "Consider implementing additional controls for enhanced security posture",
2076
- "Review and update security control templates based on deployment results"
2077
- ])
2078
-
1830
+ recommendations.extend(
1831
+ [
1832
+ "Implement continuous monitoring for deployed security controls",
1833
+ "Set up automated alerting for security control drift or failures",
1834
+ "Schedule regular security control validation and updates",
1835
+ "Consider implementing additional controls for enhanced security posture",
1836
+ "Review and update security control templates based on deployment results",
1837
+ ]
1838
+ )
1839
+
2079
1840
  return recommendations
2080
1841
 
2081
1842
  def _display_deployment_summary(self, report: MultiAccountSecurityReport):
2082
1843
  """Display comprehensive deployment summary."""
2083
-
1844
+
2084
1845
  # Executive summary panel
2085
1846
  summary_content = (
2086
1847
  f"[bold green]Multi-Account Security Deployment Complete[/bold green]\n\n"
@@ -2092,13 +1853,11 @@ class MultiAccountSecurityController:
2092
1853
  f"[bold]Estimated Risk Reduction:[/bold] {report.executive_summary['estimated_risk_reduction']}\n"
2093
1854
  f"[bold]Annual Value:[/bold] ${report.cost_analysis['annual_risk_reduction_value']:,.0f}"
2094
1855
  )
2095
-
2096
- console.print(create_panel(
2097
- summary_content,
2098
- title="🔒 Multi-Account Security Deployment Summary",
2099
- border_style="green"
2100
- ))
2101
-
1856
+
1857
+ console.print(
1858
+ create_panel(summary_content, title="🔒 Multi-Account Security Deployment Summary", border_style="green")
1859
+ )
1860
+
2102
1861
  # Compliance scores table
2103
1862
  if report.compliance_scores:
2104
1863
  compliance_table = create_table(
@@ -2106,20 +1865,16 @@ class MultiAccountSecurityController:
2106
1865
  columns=[
2107
1866
  {"name": "Framework", "style": "cyan"},
2108
1867
  {"name": "Score", "style": "green"},
2109
- {"name": "Status", "style": "yellow"}
2110
- ]
1868
+ {"name": "Status", "style": "yellow"},
1869
+ ],
2111
1870
  )
2112
-
1871
+
2113
1872
  for framework, score in report.compliance_scores.items():
2114
1873
  status = "✅ Compliant" if score >= 90 else "⚠️ Needs Attention" if score >= 70 else "❌ Non-Compliant"
2115
- compliance_table.add_row(
2116
- framework.replace('_', ' '),
2117
- f"{score:.1f}%",
2118
- status
2119
- )
2120
-
1874
+ compliance_table.add_row(framework.replace("_", " "), f"{score:.1f}%", status)
1875
+
2121
1876
  console.print(compliance_table)
2122
-
1877
+
2123
1878
  # High-priority findings
2124
1879
  if report.high_priority_findings:
2125
1880
  findings_table = create_table(
@@ -2128,20 +1883,20 @@ class MultiAccountSecurityController:
2128
1883
  {"name": "Severity", "style": "red"},
2129
1884
  {"name": "Control", "style": "cyan"},
2130
1885
  {"name": "Issue", "style": "yellow"},
2131
- {"name": "Affected Accounts", "style": "blue"}
2132
- ]
1886
+ {"name": "Affected Accounts", "style": "blue"},
1887
+ ],
2133
1888
  )
2134
-
1889
+
2135
1890
  for finding in report.high_priority_findings[:10]: # Show top 10
2136
1891
  findings_table.add_row(
2137
- finding['severity'],
2138
- finding.get('control_name', finding.get('control_id', 'Unknown'))[:30],
2139
- finding['message'][:50] + "..." if len(finding['message']) > 50 else finding['message'],
2140
- str(len(finding.get('failed_accounts', [])))
1892
+ finding["severity"],
1893
+ finding.get("control_name", finding.get("control_id", "Unknown"))[:30],
1894
+ finding["message"][:50] + "..." if len(finding["message"]) > 50 else finding["message"],
1895
+ str(len(finding.get("failed_accounts", []))),
2141
1896
  )
2142
-
1897
+
2143
1898
  console.print(findings_table)
2144
-
1899
+
2145
1900
  # Cost analysis
2146
1901
  cost_content = (
2147
1902
  f"[bold cyan]Cost Analysis[/bold cyan]\n\n"
@@ -2150,105 +1905,99 @@ class MultiAccountSecurityController:
2150
1905
  f"[yellow]Annual Risk Reduction Value:[/yellow] ${report.cost_analysis['annual_risk_reduction_value']:,.2f}\n"
2151
1906
  f"[magenta]ROI:[/magenta] {report.cost_analysis['roi_percentage']:.1f}%"
2152
1907
  )
2153
-
2154
- console.print(create_panel(
2155
- cost_content,
2156
- title="💰 Financial Impact Analysis",
2157
- border_style="blue"
2158
- ))
1908
+
1909
+ console.print(create_panel(cost_content, title="💰 Financial Impact Analysis", border_style="blue"))
2159
1910
 
2160
1911
  async def _export_deployment_report(self, report: MultiAccountSecurityReport):
2161
1912
  """Export comprehensive deployment report."""
2162
-
1913
+
2163
1914
  # Export JSON report
2164
1915
  json_report_path = self.output_dir / f"deployment_report_{report.report_id}.json"
2165
-
1916
+
2166
1917
  report_data = {
2167
- 'report_id': report.report_id,
2168
- 'timestamp': report.timestamp.isoformat(),
2169
- 'summary': {
2170
- 'total_accounts': report.total_accounts,
2171
- 'accounts_assessed': report.accounts_assessed,
2172
- 'controls_deployed': report.controls_deployed,
2173
- 'total_controls': report.total_controls,
2174
- 'overall_security_score': report.overall_security_score
1918
+ "report_id": report.report_id,
1919
+ "timestamp": report.timestamp.isoformat(),
1920
+ "summary": {
1921
+ "total_accounts": report.total_accounts,
1922
+ "accounts_assessed": report.accounts_assessed,
1923
+ "controls_deployed": report.controls_deployed,
1924
+ "total_controls": report.total_controls,
1925
+ "overall_security_score": report.overall_security_score,
2175
1926
  },
2176
- 'compliance_scores': report.compliance_scores,
2177
- 'high_priority_findings': report.high_priority_findings,
2178
- 'deployment_summary': report.deployment_summary,
2179
- 'cost_analysis': report.cost_analysis,
2180
- 'recommendations': report.recommendations,
2181
- 'executive_summary': report.executive_summary
1927
+ "compliance_scores": report.compliance_scores,
1928
+ "high_priority_findings": report.high_priority_findings,
1929
+ "deployment_summary": report.deployment_summary,
1930
+ "cost_analysis": report.cost_analysis,
1931
+ "recommendations": report.recommendations,
1932
+ "executive_summary": report.executive_summary,
2182
1933
  }
2183
-
2184
- with open(json_report_path, 'w') as f:
1934
+
1935
+ with open(json_report_path, "w") as f:
2185
1936
  json.dump(report_data, f, indent=2)
2186
-
1937
+
2187
1938
  print_success(f"Deployment report exported to: {json_report_path}")
2188
1939
 
2189
1940
 
2190
1941
  class MultiAccountDeploymentTracker:
2191
1942
  """Track deployment progress and results across accounts."""
2192
-
1943
+
2193
1944
  def __init__(self, output_dir: Path):
2194
1945
  self.output_dir = output_dir
2195
1946
  self.tracking_file = output_dir / "deployment_tracking.jsonl"
2196
-
1947
+
2197
1948
  def log_deployment_event(self, event_data: Dict[str, Any]):
2198
1949
  """Log deployment event to tracking file."""
2199
-
2200
- event_record = {
2201
- 'timestamp': datetime.utcnow().isoformat(),
2202
- **event_data
2203
- }
2204
-
2205
- with open(self.tracking_file, 'a') as f:
2206
- f.write(json.dumps(event_record) + '\n')
1950
+
1951
+ event_record = {"timestamp": datetime.utcnow().isoformat(), **event_data}
1952
+
1953
+ with open(self.tracking_file, "a") as f:
1954
+ f.write(json.dumps(event_record) + "\n")
2207
1955
 
2208
1956
 
2209
1957
  # CLI integration for multi-account security control deployment
2210
1958
  if __name__ == "__main__":
2211
1959
  import argparse
2212
-
2213
- parser = argparse.ArgumentParser(description='Multi-Account Security Controller')
2214
- parser.add_argument('--profile', default='default', help='AWS profile to use')
2215
- parser.add_argument('--controls', nargs='+', help='Specific control IDs to deploy')
2216
- parser.add_argument('--accounts', nargs='+', help='Target account IDs (optional)')
2217
- parser.add_argument('--strategy', choices=['parallel', 'staged', 'pilot', 'critical'],
2218
- default='staged', help='Deployment strategy')
2219
- parser.add_argument('--max-accounts', type=int, default=61, help='Max concurrent accounts')
2220
- parser.add_argument('--dry-run', action='store_true', help='Dry run mode (default: enabled)')
2221
- parser.add_argument('--execute', action='store_true', help='Execute actual deployments')
2222
- parser.add_argument('--output-dir', default='./artifacts/multi-account-security', help='Output directory')
2223
-
1960
+
1961
+ parser = argparse.ArgumentParser(description="Multi-Account Security Controller")
1962
+ parser.add_argument("--profile", default="default", help="AWS profile to use")
1963
+ parser.add_argument("--controls", nargs="+", help="Specific control IDs to deploy")
1964
+ parser.add_argument("--accounts", nargs="+", help="Target account IDs (optional)")
1965
+ parser.add_argument(
1966
+ "--strategy", choices=["parallel", "staged", "pilot", "critical"], default="staged", help="Deployment strategy"
1967
+ )
1968
+ parser.add_argument("--max-accounts", type=int, default=61, help="Max concurrent accounts")
1969
+ parser.add_argument("--dry-run", action="store_true", help="Dry run mode (default: enabled)")
1970
+ parser.add_argument("--execute", action="store_true", help="Execute actual deployments")
1971
+ parser.add_argument("--output-dir", default="./artifacts/multi-account-security", help="Output directory")
1972
+
2224
1973
  args = parser.parse_args()
2225
-
1974
+
2226
1975
  # Determine deployment strategies
2227
1976
  strategy_mapping = {
2228
- 'parallel': DeploymentStrategy.PARALLEL_ALL,
2229
- 'staged': DeploymentStrategy.STAGED_ROLLOUT,
2230
- 'pilot': DeploymentStrategy.PILOT_FIRST,
2231
- 'critical': DeploymentStrategy.CRITICAL_FIRST
1977
+ "parallel": DeploymentStrategy.PARALLEL_ALL,
1978
+ "staged": DeploymentStrategy.STAGED_ROLLOUT,
1979
+ "pilot": DeploymentStrategy.PILOT_FIRST,
1980
+ "critical": DeploymentStrategy.CRITICAL_FIRST,
2232
1981
  }
2233
-
1982
+
2234
1983
  async def main():
2235
1984
  controller = MultiAccountSecurityController(
2236
1985
  profile=args.profile,
2237
1986
  output_dir=args.output_dir,
2238
1987
  max_concurrent_accounts=args.max_accounts,
2239
- dry_run=not args.execute # Dry run unless --execute is specified
1988
+ dry_run=not args.execute, # Dry run unless --execute is specified
2240
1989
  )
2241
-
1990
+
2242
1991
  report = await controller.deploy_security_controls_organization_wide(
2243
1992
  control_ids=args.controls,
2244
1993
  target_accounts=args.accounts,
2245
- deployment_strategy=strategy_mapping[args.strategy]
1994
+ deployment_strategy=strategy_mapping[args.strategy],
2246
1995
  )
2247
-
1996
+
2248
1997
  print_success(f"Multi-account deployment completed: {report.report_id}")
2249
1998
  print_info(f"Overall security score: {report.overall_security_score:.1f}%")
2250
1999
  print_info(f"Accounts secured: {report.executive_summary['accounts_secured']}/{report.total_accounts}")
2251
2000
  print_info(f"Annual value: ${report.cost_analysis['annual_risk_reduction_value']:,.0f}")
2252
-
2001
+
2253
2002
  # Run the async main function
2254
- asyncio.run(main())
2003
+ asyncio.run(main())