runbooks 0.9.9__py3-none-any.whl → 1.0.1__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 (111) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/WEIGHT_CONFIG_README.md +368 -0
  3. runbooks/cfat/app.ts +27 -19
  4. runbooks/cfat/assessment/runner.py +6 -5
  5. runbooks/cfat/cloud_foundations_assessment.py +626 -0
  6. runbooks/cfat/tests/test_weight_configuration.ts +449 -0
  7. runbooks/cfat/weight_config.ts +574 -0
  8. runbooks/cloudops/cost_optimizer.py +95 -33
  9. runbooks/common/__init__.py +26 -9
  10. runbooks/common/aws_pricing.py +1353 -0
  11. runbooks/common/aws_pricing_api.py +205 -0
  12. runbooks/common/aws_utils.py +2 -2
  13. runbooks/common/comprehensive_cost_explorer_integration.py +979 -0
  14. runbooks/common/cross_account_manager.py +606 -0
  15. runbooks/common/date_utils.py +115 -0
  16. runbooks/common/enhanced_exception_handler.py +14 -7
  17. runbooks/common/env_utils.py +96 -0
  18. runbooks/common/mcp_cost_explorer_integration.py +5 -4
  19. runbooks/common/mcp_integration.py +49 -2
  20. runbooks/common/organizations_client.py +579 -0
  21. runbooks/common/profile_utils.py +127 -72
  22. runbooks/common/rich_utils.py +3 -3
  23. runbooks/finops/cost_optimizer.py +2 -1
  24. runbooks/finops/dashboard_runner.py +47 -28
  25. runbooks/finops/ebs_optimizer.py +56 -9
  26. runbooks/finops/elastic_ip_optimizer.py +13 -9
  27. runbooks/finops/embedded_mcp_validator.py +31 -0
  28. runbooks/finops/enhanced_trend_visualization.py +10 -4
  29. runbooks/finops/finops_dashboard.py +6 -5
  30. runbooks/finops/iam_guidance.py +6 -1
  31. runbooks/finops/markdown_exporter.py +217 -2
  32. runbooks/finops/nat_gateway_optimizer.py +76 -20
  33. runbooks/finops/tests/test_integration.py +3 -1
  34. runbooks/finops/vpc_cleanup_exporter.py +28 -26
  35. runbooks/finops/vpc_cleanup_optimizer.py +363 -16
  36. runbooks/inventory/__init__.py +10 -1
  37. runbooks/inventory/cloud_foundations_integration.py +409 -0
  38. runbooks/inventory/core/collector.py +1177 -94
  39. runbooks/inventory/discovery.md +339 -0
  40. runbooks/inventory/drift_detection_cli.py +327 -0
  41. runbooks/inventory/inventory_mcp_cli.py +171 -0
  42. runbooks/inventory/inventory_modules.py +6 -9
  43. runbooks/inventory/list_ec2_instances.py +3 -3
  44. runbooks/inventory/mcp_inventory_validator.py +2149 -0
  45. runbooks/inventory/mcp_vpc_validator.py +23 -6
  46. runbooks/inventory/organizations_discovery.py +104 -9
  47. runbooks/inventory/rich_inventory_display.py +129 -1
  48. runbooks/inventory/unified_validation_engine.py +1279 -0
  49. runbooks/inventory/verify_ec2_security_groups.py +3 -1
  50. runbooks/inventory/vpc_analyzer.py +825 -7
  51. runbooks/inventory/vpc_flow_analyzer.py +36 -42
  52. runbooks/main.py +708 -47
  53. runbooks/monitoring/performance_monitor.py +11 -7
  54. runbooks/operate/base.py +9 -6
  55. runbooks/operate/deployment_framework.py +5 -4
  56. runbooks/operate/deployment_validator.py +6 -5
  57. runbooks/operate/dynamodb_operations.py +6 -5
  58. runbooks/operate/ec2_operations.py +3 -2
  59. runbooks/operate/mcp_integration.py +6 -5
  60. runbooks/operate/networking_cost_heatmap.py +21 -16
  61. runbooks/operate/s3_operations.py +13 -12
  62. runbooks/operate/vpc_operations.py +100 -12
  63. runbooks/remediation/base.py +4 -2
  64. runbooks/remediation/commons.py +5 -5
  65. runbooks/remediation/commvault_ec2_analysis.py +68 -15
  66. runbooks/remediation/config/accounts_example.json +31 -0
  67. runbooks/remediation/ec2_unattached_ebs_volumes.py +6 -3
  68. runbooks/remediation/multi_account.py +120 -7
  69. runbooks/remediation/rds_snapshot_list.py +5 -3
  70. runbooks/remediation/remediation_cli.py +710 -0
  71. runbooks/remediation/universal_account_discovery.py +377 -0
  72. runbooks/security/compliance_automation_engine.py +99 -20
  73. runbooks/security/config/__init__.py +24 -0
  74. runbooks/security/config/compliance_config.py +255 -0
  75. runbooks/security/config/compliance_weights_example.json +22 -0
  76. runbooks/security/config_template_generator.py +500 -0
  77. runbooks/security/security_cli.py +377 -0
  78. runbooks/validation/__init__.py +21 -1
  79. runbooks/validation/cli.py +8 -7
  80. runbooks/validation/comprehensive_2way_validator.py +2007 -0
  81. runbooks/validation/mcp_validator.py +965 -101
  82. runbooks/validation/terraform_citations_validator.py +363 -0
  83. runbooks/validation/terraform_drift_detector.py +1098 -0
  84. runbooks/vpc/cleanup_wrapper.py +231 -10
  85. runbooks/vpc/config.py +346 -73
  86. runbooks/vpc/cross_account_session.py +312 -0
  87. runbooks/vpc/heatmap_engine.py +115 -41
  88. runbooks/vpc/manager_interface.py +9 -9
  89. runbooks/vpc/mcp_no_eni_validator.py +1630 -0
  90. runbooks/vpc/networking_wrapper.py +14 -8
  91. runbooks/vpc/runbooks_adapter.py +33 -12
  92. runbooks/vpc/tests/conftest.py +4 -2
  93. runbooks/vpc/tests/test_cost_engine.py +4 -2
  94. runbooks/vpc/unified_scenarios.py +73 -3
  95. runbooks/vpc/vpc_cleanup_integration.py +512 -78
  96. {runbooks-0.9.9.dist-info → runbooks-1.0.1.dist-info}/METADATA +94 -52
  97. {runbooks-0.9.9.dist-info → runbooks-1.0.1.dist-info}/RECORD +101 -81
  98. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  99. runbooks/finops/runbooks.security.report_generator.log +0 -0
  100. runbooks/finops/runbooks.security.run_script.log +0 -0
  101. runbooks/finops/runbooks.security.security_export.log +0 -0
  102. runbooks/finops/tests/results_test_finops_dashboard.xml +0 -1
  103. runbooks/inventory/artifacts/scale-optimize-status.txt +0 -12
  104. runbooks/inventory/runbooks.inventory.organizations_discovery.log +0 -0
  105. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  106. runbooks/inventory/runbooks.security.run_script.log +0 -0
  107. runbooks/inventory/runbooks.security.security_export.log +0 -0
  108. {runbooks-0.9.9.dist-info → runbooks-1.0.1.dist-info}/WHEEL +0 -0
  109. {runbooks-0.9.9.dist-info → runbooks-1.0.1.dist-info}/entry_points.txt +0 -0
  110. {runbooks-0.9.9.dist-info → runbooks-1.0.1.dist-info}/licenses/LICENSE +0 -0
  111. {runbooks-0.9.9.dist-info → runbooks-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,377 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Universal Account Discovery for Remediation Operations
4
+ =====================================================
5
+
6
+ This module provides truly universal AWS account discovery that works with ANY AWS setup:
7
+ - Single account setups
8
+ - Multi-account Organizations setups
9
+ - Mixed environments
10
+ - Any profile naming convention
11
+
12
+ Features:
13
+ - Dynamic account discovery (no hardcoded account arrays)
14
+ - Profile-based account resolution
15
+ - Environment variable configuration
16
+ - Configuration file support
17
+ - Universal compatibility across all AWS setups
18
+
19
+ Author: DevOps Security Engineer (Claude Code Enterprise Team)
20
+ Version: 1.0.0 - Universal Account Discovery
21
+ """
22
+
23
+ import json
24
+ import os
25
+ from dataclasses import dataclass
26
+ from typing import Dict, List, Optional, Set
27
+
28
+ import boto3
29
+ from botocore.exceptions import ClientError
30
+
31
+ from runbooks.common.profile_utils import get_profile_for_operation
32
+ from runbooks.common.rich_utils import console, print_error, print_info, print_warning
33
+
34
+
35
+ @dataclass
36
+ class AWSAccount:
37
+ """Universal AWS account representation."""
38
+
39
+ account_id: str
40
+ account_name: Optional[str] = None
41
+ status: str = "ACTIVE"
42
+ email: Optional[str] = None
43
+ joined_method: Optional[str] = None
44
+ profile_name: Optional[str] = None
45
+
46
+
47
+ class UniversalAccountDiscovery:
48
+ """
49
+ Universal AWS account discovery that works with ANY AWS setup.
50
+
51
+ Discovery methods (in priority order):
52
+ 1. Environment variables (REMEDIATION_TARGET_ACCOUNTS)
53
+ 2. Configuration file (REMEDIATION_ACCOUNT_CONFIG)
54
+ 3. AWS Organizations API (if available)
55
+ 4. Current account (single account mode)
56
+
57
+ No hardcoded account arrays - fully dynamic discovery.
58
+ """
59
+
60
+ def __init__(self, profile: Optional[str] = None):
61
+ """Initialize universal account discovery."""
62
+ self.profile = profile
63
+ self.resolved_profile = get_profile_for_operation("management", profile)
64
+ self.session = self._create_session()
65
+
66
+ def _create_session(self) -> boto3.Session:
67
+ """Create AWS session using universal profile management."""
68
+ return boto3.Session(profile_name=self.resolved_profile)
69
+
70
+ def discover_target_accounts(self, include_current: bool = True) -> List[AWSAccount]:
71
+ """
72
+ Discover target accounts for remediation using universal approach.
73
+
74
+ Args:
75
+ include_current: Include current account in results
76
+
77
+ Returns:
78
+ List[AWSAccount]: Discovered target accounts
79
+ """
80
+ console.log("[cyan]🔍 Starting universal account discovery...[/]")
81
+
82
+ discovered_accounts = []
83
+
84
+ # Method 1: Environment variables (highest priority)
85
+ env_accounts = self._get_accounts_from_environment()
86
+ if env_accounts:
87
+ console.log(f"[green]✓ Found {len(env_accounts)} accounts from environment variables[/]")
88
+ discovered_accounts.extend(env_accounts)
89
+ return discovered_accounts
90
+
91
+ # Method 2: Configuration file
92
+ config_accounts = self._get_accounts_from_config()
93
+ if config_accounts:
94
+ console.log(f"[green]✓ Found {len(config_accounts)} accounts from configuration file[/]")
95
+ discovered_accounts.extend(config_accounts)
96
+ return discovered_accounts
97
+
98
+ # Method 3: AWS Organizations API (if available)
99
+ org_accounts = self._get_accounts_from_organizations()
100
+ if org_accounts:
101
+ console.log(f"[green]✓ Found {len(org_accounts)} accounts from AWS Organizations[/]")
102
+ discovered_accounts.extend(org_accounts)
103
+ return discovered_accounts
104
+
105
+ # Method 4: Current account fallback (single account mode)
106
+ if include_current:
107
+ current_account = self._get_current_account()
108
+ if current_account:
109
+ console.log("[yellow]🔍 Single account mode: Using current account[/]")
110
+ discovered_accounts.append(current_account)
111
+
112
+ if not discovered_accounts:
113
+ print_warning("No target accounts discovered. Check configuration or permissions.")
114
+
115
+ return discovered_accounts
116
+
117
+ def _get_accounts_from_environment(self) -> List[AWSAccount]:
118
+ """Get accounts from environment variables."""
119
+ env_accounts = os.getenv("REMEDIATION_TARGET_ACCOUNTS")
120
+ if not env_accounts:
121
+ return []
122
+
123
+ try:
124
+ account_ids = [acc.strip() for acc in env_accounts.split(",")]
125
+ return [
126
+ AWSAccount(
127
+ account_id=account_id,
128
+ account_name=f"Env-Account-{account_id}",
129
+ profile_name=self.resolved_profile
130
+ )
131
+ for account_id in account_ids
132
+ if account_id
133
+ ]
134
+ except Exception as e:
135
+ print_warning(f"Failed to parse REMEDIATION_TARGET_ACCOUNTS: {e}")
136
+ return []
137
+
138
+ def _get_accounts_from_config(self) -> List[AWSAccount]:
139
+ """Get accounts from configuration file."""
140
+ config_path = os.getenv("REMEDIATION_ACCOUNT_CONFIG")
141
+ if not config_path or not os.path.exists(config_path):
142
+ return []
143
+
144
+ try:
145
+ with open(config_path, 'r') as f:
146
+ config = json.load(f)
147
+
148
+ accounts = []
149
+ for account_config in config.get("target_accounts", []):
150
+ account = AWSAccount(
151
+ account_id=account_config["account_id"],
152
+ account_name=account_config.get("account_name"),
153
+ status=account_config.get("status", "ACTIVE"),
154
+ email=account_config.get("email"),
155
+ profile_name=account_config.get("profile_name", self.resolved_profile)
156
+ )
157
+ accounts.append(account)
158
+
159
+ console.log(f"[dim cyan]Loaded account configuration from: {config_path}[/]")
160
+ return accounts
161
+
162
+ except Exception as e:
163
+ print_warning(f"Failed to load account configuration from {config_path}: {e}")
164
+ return []
165
+
166
+ def _get_accounts_from_organizations(self) -> List[AWSAccount]:
167
+ """Get accounts from AWS Organizations API."""
168
+ try:
169
+ # Check if Organizations API is available
170
+ orgs_client = self.session.client("organizations")
171
+
172
+ # Try to list accounts
173
+ paginator = orgs_client.get_paginator("list_accounts")
174
+ accounts = []
175
+
176
+ for page in paginator.paginate():
177
+ for account in page["Accounts"]:
178
+ aws_account = AWSAccount(
179
+ account_id=account["Id"],
180
+ account_name=account.get("Name"),
181
+ status=account.get("Status", "ACTIVE"),
182
+ email=account.get("Email"),
183
+ joined_method=account.get("JoinedMethod"),
184
+ profile_name=self.resolved_profile
185
+ )
186
+ accounts.append(aws_account)
187
+
188
+ # Filter to active accounts only
189
+ active_accounts = [acc for acc in accounts if acc.status == "ACTIVE"]
190
+ console.log(f"[dim cyan]Discovered {len(active_accounts)} active accounts via Organizations API[/]")
191
+
192
+ return active_accounts
193
+
194
+ except ClientError as e:
195
+ error_code = e.response.get("Error", {}).get("Code", "Unknown")
196
+ if error_code in ["AccessDenied", "AWSOrganizationsNotInUseException"]:
197
+ console.log("[dim yellow]Organizations API not available (not in use or no permissions)[/]")
198
+ else:
199
+ print_warning(f"Organizations API error: {error_code}")
200
+ return []
201
+ except Exception as e:
202
+ print_warning(f"Failed to access Organizations API: {e}")
203
+ return []
204
+
205
+ def _get_current_account(self) -> Optional[AWSAccount]:
206
+ """Get current account as fallback."""
207
+ try:
208
+ sts_client = self.session.client("sts")
209
+ identity = sts_client.get_caller_identity()
210
+
211
+ return AWSAccount(
212
+ account_id=identity["Account"],
213
+ account_name=f"Current-Account-{identity['Account']}",
214
+ status="ACTIVE",
215
+ profile_name=self.resolved_profile
216
+ )
217
+
218
+ except Exception as e:
219
+ print_error(f"Failed to get current account identity: {e}")
220
+ return None
221
+
222
+ def filter_accounts_by_criteria(
223
+ self,
224
+ accounts: List[AWSAccount],
225
+ include_patterns: Optional[List[str]] = None,
226
+ exclude_patterns: Optional[List[str]] = None,
227
+ max_accounts: Optional[int] = None
228
+ ) -> List[AWSAccount]:
229
+ """
230
+ Filter discovered accounts by various criteria.
231
+
232
+ Args:
233
+ accounts: List of discovered accounts
234
+ include_patterns: Account ID or name patterns to include
235
+ exclude_patterns: Account ID or name patterns to exclude
236
+ max_accounts: Maximum number of accounts to return
237
+
238
+ Returns:
239
+ List[AWSAccount]: Filtered accounts
240
+ """
241
+ filtered_accounts = accounts.copy()
242
+
243
+ # Apply include patterns
244
+ if include_patterns:
245
+ filtered_accounts = [
246
+ acc for acc in filtered_accounts
247
+ if any(pattern in acc.account_id or (acc.account_name and pattern in acc.account_name)
248
+ for pattern in include_patterns)
249
+ ]
250
+
251
+ # Apply exclude patterns
252
+ if exclude_patterns:
253
+ filtered_accounts = [
254
+ acc for acc in filtered_accounts
255
+ if not any(pattern in acc.account_id or (acc.account_name and pattern in acc.account_name)
256
+ for pattern in exclude_patterns)
257
+ ]
258
+
259
+ # Apply max accounts limit
260
+ if max_accounts and len(filtered_accounts) > max_accounts:
261
+ console.log(f"[yellow]Limiting to {max_accounts} accounts (found {len(filtered_accounts)})[/]")
262
+ filtered_accounts = filtered_accounts[:max_accounts]
263
+
264
+ return filtered_accounts
265
+
266
+ def validate_account_access(self, accounts: List[AWSAccount]) -> List[AWSAccount]:
267
+ """
268
+ Validate access to discovered accounts.
269
+
270
+ Args:
271
+ accounts: List of accounts to validate
272
+
273
+ Returns:
274
+ List[AWSAccount]: Accounts with validated access
275
+ """
276
+ validated_accounts = []
277
+
278
+ for account in accounts:
279
+ try:
280
+ # Try to get caller identity to validate access
281
+ session = boto3.Session(profile_name=account.profile_name or self.resolved_profile)
282
+ sts_client = session.client("sts")
283
+ identity = sts_client.get_caller_identity()
284
+
285
+ # Verify account ID matches
286
+ if identity["Account"] == account.account_id:
287
+ validated_accounts.append(account)
288
+ console.log(f"[green]✓ Validated access to account: {account.account_id}[/]")
289
+ else:
290
+ print_warning(f"Account ID mismatch for {account.account_id}: got {identity['Account']}")
291
+
292
+ except Exception as e:
293
+ print_warning(f"Failed to validate access to account {account.account_id}: {e}")
294
+
295
+ return validated_accounts
296
+
297
+ def export_account_config_template(self, output_path: str) -> None:
298
+ """
299
+ Export account configuration template for enterprise customization.
300
+
301
+ Args:
302
+ output_path: Path to save the configuration template
303
+ """
304
+ template = {
305
+ "target_accounts": [
306
+ {
307
+ "account_id": "111122223333",
308
+ "account_name": "Production Environment",
309
+ "status": "ACTIVE",
310
+ "email": "prod@company.com",
311
+ "profile_name": "prod-profile"
312
+ },
313
+ {
314
+ "account_id": "444455556666",
315
+ "account_name": "Staging Environment",
316
+ "status": "ACTIVE",
317
+ "email": "staging@company.com",
318
+ "profile_name": "staging-profile"
319
+ }
320
+ ],
321
+ "discovery_settings": {
322
+ "max_concurrent_accounts": 10,
323
+ "validation_timeout_seconds": 30,
324
+ "include_suspended_accounts": False
325
+ }
326
+ }
327
+
328
+ try:
329
+ with open(output_path, 'w') as f:
330
+ json.dump(template, f, indent=2)
331
+ console.log(f"[green]Account configuration template exported to: {output_path}[/]")
332
+ except Exception as e:
333
+ print_error(f"Failed to export account configuration template: {e}")
334
+
335
+
336
+ def discover_remediation_accounts(profile: Optional[str] = None) -> List[AWSAccount]:
337
+ """
338
+ Convenience function for universal account discovery.
339
+
340
+ Args:
341
+ profile: AWS profile to use for discovery
342
+
343
+ Returns:
344
+ List[AWSAccount]: Discovered accounts for remediation
345
+ """
346
+ discovery = UniversalAccountDiscovery(profile=profile)
347
+ return discovery.discover_target_accounts()
348
+
349
+
350
+ def get_account_by_id(account_id: str, profile: Optional[str] = None) -> Optional[AWSAccount]:
351
+ """
352
+ Get specific account by ID using universal discovery.
353
+
354
+ Args:
355
+ account_id: Target account ID
356
+ profile: AWS profile to use
357
+
358
+ Returns:
359
+ Optional[AWSAccount]: Account if found, None otherwise
360
+ """
361
+ discovery = UniversalAccountDiscovery(profile=profile)
362
+ accounts = discovery.discover_target_accounts()
363
+
364
+ for account in accounts:
365
+ if account.account_id == account_id:
366
+ return account
367
+
368
+ return None
369
+
370
+
371
+ # Export public interface
372
+ __all__ = [
373
+ "AWSAccount",
374
+ "UniversalAccountDiscovery",
375
+ "discover_remediation_accounts",
376
+ "get_account_by_id",
377
+ ]
@@ -47,6 +47,7 @@ from .enterprise_security_framework import (
47
47
  SecurityFinding,
48
48
  SecuritySeverity,
49
49
  )
50
+ from .config import get_universal_compliance_config
50
51
 
51
52
 
52
53
  class ComplianceStatus(Enum):
@@ -143,6 +144,9 @@ class ComplianceAutomationEngine:
143
144
  self.profile = profile
144
145
  self.output_dir = Path(output_dir)
145
146
  self.output_dir.mkdir(parents=True, exist_ok=True)
147
+
148
+ # Initialize universal compliance configuration
149
+ self.compliance_config = get_universal_compliance_config()
146
150
 
147
151
  # Initialize AWS session
148
152
  self.session = self._create_session()
@@ -170,6 +174,60 @@ class ComplianceAutomationEngine:
170
174
  """Create secure AWS session using enterprise profile management."""
171
175
  # Use management profile for compliance operations requiring cross-account access
172
176
  return create_management_session(profile=self.profile)
177
+
178
+ def _get_compliance_weight(self, control_id: str, default_weight: float) -> float:
179
+ """
180
+ Get compliance weight for control using universal configuration system.
181
+
182
+ Uses the universal compliance configuration with priority:
183
+ 1. Environment variables: COMPLIANCE_WEIGHT_<CONTROL_ID>
184
+ 2. Configuration file: COMPLIANCE_CONFIG_PATH
185
+ 3. Framework-specific defaults
186
+
187
+ Args:
188
+ control_id: Control identifier
189
+ default_weight: Framework-specific default weight
190
+
191
+ Returns:
192
+ float: Compliance weight for the control
193
+ """
194
+ return self.compliance_config.get_control_weight(control_id, default_weight)
195
+
196
+ def _get_compliance_threshold(self, framework: ComplianceFramework) -> float:
197
+ """
198
+ Get compliance threshold for framework using universal configuration system.
199
+
200
+ Uses the universal compliance configuration with framework-specific defaults:
201
+ - PCI DSS: 100.0% (requires perfect compliance)
202
+ - HIPAA: 95.0% (healthcare requires high compliance)
203
+ - SOC2 Type II: 95.0% (service organization controls)
204
+ - AWS Well-Architected: 90.0% (recommended practices)
205
+ - ISO 27001: 90.0% (information security management)
206
+ - NIST Cybersecurity: 85.0% (cybersecurity framework)
207
+ - CIS Benchmarks: 85.0% (security benchmarks)
208
+
209
+ Args:
210
+ framework: Compliance framework
211
+
212
+ Returns:
213
+ float: Compliance threshold for the framework
214
+ """
215
+ # Framework-specific defaults based on industry standards
216
+ framework_defaults = {
217
+ ComplianceFramework.PCI_DSS: 100.0, # PCI DSS requires 100% compliance
218
+ ComplianceFramework.HIPAA: 95.0, # HIPAA requires high compliance
219
+ ComplianceFramework.SOC2_TYPE_II: 95.0, # SOC2 requires high compliance
220
+ ComplianceFramework.AWS_WELL_ARCHITECTED: 90.0,
221
+ ComplianceFramework.ISO27001: 90.0,
222
+ ComplianceFramework.NIST_CYBERSECURITY: 85.0,
223
+ ComplianceFramework.CIS_BENCHMARKS: 85.0,
224
+ }
225
+
226
+ # Get framework name for configuration lookup
227
+ framework_name = framework.value.lower().replace(' ', '-').replace('_', '-')
228
+ default_threshold = framework_defaults.get(framework, 90.0)
229
+
230
+ return self.compliance_config.get_framework_threshold(framework_name, default_threshold)
173
231
 
174
232
  def _load_framework_controls(self) -> Dict[ComplianceFramework, List[ComplianceControl]]:
175
233
  """Load compliance framework control definitions."""
@@ -189,7 +247,7 @@ class ComplianceAutomationEngine:
189
247
  automated_assessment=True,
190
248
  assessment_method="iam_policy_analysis",
191
249
  remediation_available=True,
192
- compliance_score_weight=2.0,
250
+ compliance_score_weight=self._get_compliance_weight("SEC-1", 2.0),
193
251
  evidence_requirements=["iam_policies", "access_logs", "mfa_status"],
194
252
  testing_frequency="monthly",
195
253
  ),
@@ -203,7 +261,7 @@ class ComplianceAutomationEngine:
203
261
  automated_assessment=True,
204
262
  assessment_method="multi_layer_security_check",
205
263
  remediation_available=True,
206
- compliance_score_weight=1.5,
264
+ compliance_score_weight=self._get_compliance_weight("SEC-2", 1.5),
207
265
  evidence_requirements=["security_groups", "nacls", "waf_rules"],
208
266
  testing_frequency="monthly",
209
267
  ),
@@ -222,7 +280,7 @@ class ComplianceAutomationEngine:
222
280
  automated_assessment=True,
223
281
  assessment_method="access_control_assessment",
224
282
  remediation_available=True,
225
- compliance_score_weight=3.0,
283
+ compliance_score_weight=self._get_compliance_weight("CC6.1", 3.0),
226
284
  evidence_requirements=["access_logs", "user_provisioning", "termination_procedures"],
227
285
  testing_frequency="quarterly",
228
286
  ),
@@ -236,7 +294,7 @@ class ComplianceAutomationEngine:
236
294
  automated_assessment=True,
237
295
  assessment_method="authentication_assessment",
238
296
  remediation_available=True,
239
- compliance_score_weight=2.5,
297
+ compliance_score_weight=self._get_compliance_weight("CC6.2", 2.5),
240
298
  evidence_requirements=["authentication_logs", "mfa_usage", "password_policies"],
241
299
  testing_frequency="quarterly",
242
300
  ),
@@ -255,7 +313,7 @@ class ComplianceAutomationEngine:
255
313
  automated_assessment=True,
256
314
  assessment_method="firewall_configuration_check",
257
315
  remediation_available=True,
258
- compliance_score_weight=2.0,
316
+ compliance_score_weight=self._get_compliance_weight("PCI-1", 2.0),
259
317
  evidence_requirements=["firewall_rules", "change_logs", "review_procedures"],
260
318
  testing_frequency="quarterly",
261
319
  ),
@@ -274,7 +332,7 @@ class ComplianceAutomationEngine:
274
332
  automated_assessment=True,
275
333
  assessment_method="hipaa_access_control_check",
276
334
  remediation_available=True,
277
- compliance_score_weight=2.5,
335
+ compliance_score_weight=self._get_compliance_weight("HIPAA-164.312(a)(1)", 2.5),
278
336
  evidence_requirements=["access_procedures", "user_access_logs", "phi_access_controls"],
279
337
  testing_frequency="annually",
280
338
  ),
@@ -410,18 +468,8 @@ class ComplianceAutomationEngine:
410
468
  def _determine_compliance_status(self, score: float, framework: ComplianceFramework) -> ComplianceStatus:
411
469
  """Determine compliance status based on score and framework requirements."""
412
470
 
413
- # Framework-specific thresholds
414
- framework_thresholds = {
415
- ComplianceFramework.PCI_DSS: 100.0, # PCI DSS requires 100% compliance
416
- ComplianceFramework.HIPAA: 95.0, # HIPAA requires high compliance
417
- ComplianceFramework.SOC2_TYPE_II: 95.0, # SOC2 requires high compliance
418
- ComplianceFramework.AWS_WELL_ARCHITECTED: 90.0,
419
- ComplianceFramework.ISO27001: 90.0,
420
- ComplianceFramework.NIST_CYBERSECURITY: 85.0,
421
- ComplianceFramework.CIS_BENCHMARKS: 85.0,
422
- }
423
-
424
- threshold = framework_thresholds.get(framework, 90.0)
471
+ # Use dynamic threshold configuration
472
+ threshold = self._get_compliance_threshold(framework)
425
473
 
426
474
  if score >= threshold:
427
475
  return ComplianceStatus.COMPLIANT
@@ -611,9 +659,38 @@ class ComplianceAutomationEngine:
611
659
  return summary
612
660
 
613
661
  async def _discover_target_accounts(self) -> List[str]:
614
- """Discover target accounts for compliance assessment."""
662
+ """
663
+ Discover target accounts for compliance assessment using configuration-driven approach.
664
+
665
+ Priority:
666
+ 1. Environment variable: COMPLIANCE_TARGET_ACCOUNTS (comma-separated)
667
+ 2. Configuration file: COMPLIANCE_ACCOUNTS_CONFIG
668
+ 3. AWS Organizations API discovery
669
+ 4. Current account fallback
670
+ """
671
+ # Try environment variable first
672
+ env_accounts = os.getenv("COMPLIANCE_TARGET_ACCOUNTS")
673
+ if env_accounts:
674
+ account_ids = [acc.strip() for acc in env_accounts.split(",")]
675
+ print_info(f"Using {len(account_ids)} accounts from COMPLIANCE_TARGET_ACCOUNTS environment variable")
676
+ return account_ids
677
+
678
+ # Try configuration file
679
+ config_path = os.getenv("COMPLIANCE_ACCOUNTS_CONFIG")
680
+ if config_path and os.path.exists(config_path):
681
+ try:
682
+ with open(config_path, 'r') as f:
683
+ config = json.load(f)
684
+ account_ids = config.get("target_accounts", [])
685
+ if account_ids:
686
+ print_info(f"Using {len(account_ids)} accounts from configuration file: {config_path}")
687
+ return account_ids
688
+ except Exception as e:
689
+ print_warning(f"Failed to load account configuration from {config_path}: {e}")
690
+
691
+ # Fall back to Organizations API discovery
615
692
  try:
616
- # Use Organizations API to discover accounts
693
+ print_info("Discovering accounts via AWS Organizations API...")
617
694
  org_client = self.session.client("organizations")
618
695
  paginator = org_client.get_paginator("list_accounts")
619
696
 
@@ -623,6 +700,7 @@ class ComplianceAutomationEngine:
623
700
  if account["Status"] == "ACTIVE":
624
701
  accounts.append(account["Id"])
625
702
 
703
+ print_info(f"Discovered {len(accounts)} active accounts via Organizations API")
626
704
  return accounts
627
705
 
628
706
  except ClientError as e:
@@ -630,6 +708,7 @@ class ComplianceAutomationEngine:
630
708
  print_warning(f"Could not discover organization accounts: {str(e)}")
631
709
  sts_client = self.session.client("sts")
632
710
  current_account = sts_client.get_caller_identity()["Account"]
711
+ print_info(f"Using current account for assessment: {current_account}")
633
712
  return [current_account]
634
713
 
635
714
  async def _export_compliance_report(self, report: ComplianceReport):
@@ -0,0 +1,24 @@
1
+ """
2
+ Security Configuration Package
3
+ ==============================
4
+
5
+ Universal configuration management for enterprise security and compliance operations.
6
+ Provides dynamic configuration with no hardcoded values.
7
+
8
+ Modules:
9
+ - compliance_config: Universal compliance configuration management
10
+ """
11
+
12
+ from .compliance_config import (
13
+ ComplianceConfiguration,
14
+ UniversalComplianceConfig,
15
+ get_universal_compliance_config,
16
+ reset_compliance_config,
17
+ )
18
+
19
+ __all__ = [
20
+ "ComplianceConfiguration",
21
+ "UniversalComplianceConfig",
22
+ "get_universal_compliance_config",
23
+ "reset_compliance_config",
24
+ ]