runbooks 1.1.4__py3-none-any.whl → 1.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runbooks/__init__.py +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/assessment/compliance.py +1 -1
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cli/__init__.py +1 -1
- runbooks/cli/commands/cfat.py +64 -23
- runbooks/cli/commands/finops.py +1005 -54
- runbooks/cli/commands/inventory.py +138 -35
- runbooks/cli/commands/operate.py +9 -36
- runbooks/cli/commands/security.py +42 -18
- runbooks/cli/commands/validation.py +432 -18
- runbooks/cli/commands/vpc.py +81 -17
- runbooks/cli/registry.py +22 -10
- runbooks/cloudops/__init__.py +20 -27
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +544 -542
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +224 -225
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +177 -213
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +11 -0
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +40 -36
- runbooks/common/aws_utils.py +74 -79
- runbooks/common/business_logic.py +126 -104
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
- runbooks/common/cross_account_manager.py +197 -204
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +29 -19
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +476 -493
- runbooks/common/mcp_integration.py +63 -74
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +175 -193
- runbooks/common/patterns.py +23 -25
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +111 -37
- runbooks/common/rich_utils.py +201 -141
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +26 -30
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +484 -618
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +32 -29
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +223 -285
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +337 -174
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1512 -481
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +19 -23
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +64 -65
- runbooks/finops/scenarios.py +1277 -438
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +259 -265
- runbooks/finops/vpc_cleanup_exporter.py +189 -144
- runbooks/finops/vpc_cleanup_optimizer.py +591 -573
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +1 -1
- runbooks/inventory/collectors/aws_networking.py +109 -99
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/mcp_inventory_validator.py +549 -465
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +55 -51
- runbooks/inventory/rich_inventory_display.py +33 -32
- runbooks/inventory/unified_validation_engine.py +278 -251
- runbooks/inventory/vpc_analyzer.py +732 -695
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +382 -378
- runbooks/inventory/vpc_flow_analyzer.py +1 -1
- runbooks/main.py +49 -34
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/networking_cost_heatmap.py +29 -8
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_operations.py +646 -616
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +70 -66
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +86 -60
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +46 -41
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +50 -47
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +745 -704
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +447 -451
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +185 -160
- runbooks/vpc/mcp_no_eni_validator.py +680 -639
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1297 -1124
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.5.dist-info/METADATA +328 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/RECORD +214 -193
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -973
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.4.dist-info/METADATA +0 -800
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
runbooks/inventory/__init__.py
CHANGED
@@ -51,7 +51,7 @@ from runbooks import __version__
|
|
51
51
|
__all__ = [
|
52
52
|
# Core functionality
|
53
53
|
"InventoryCollector",
|
54
|
-
"InventoryFormatter",
|
54
|
+
"InventoryFormatter",
|
55
55
|
# Enhanced functionality with proven finops patterns
|
56
56
|
"EnhancedInventoryCollector",
|
57
57
|
# Base classes for extension
|
@@ -72,7 +72,7 @@ __all__ = [
|
|
72
72
|
"validate_aws_credentials",
|
73
73
|
# VPC Module Migration Integration
|
74
74
|
"VPCAnalyzer",
|
75
|
-
"VPCDiscoveryResult",
|
75
|
+
"VPCDiscoveryResult",
|
76
76
|
"AWSOAnalysis",
|
77
77
|
# Version
|
78
78
|
"__version__",
|
@@ -5,7 +5,7 @@ Integrates proven patterns into runbooks inventory module using enterprise finop
|
|
5
5
|
|
6
6
|
This module extracts and integrates valuable patterns while maintaining runbooks architecture:
|
7
7
|
- Enhanced multi-account session management
|
8
|
-
- Specialized service discovery capabilities
|
8
|
+
- Specialized service discovery capabilities
|
9
9
|
- Enterprise-grade error handling and performance
|
10
10
|
- Rich CLI integration with runbooks standards
|
11
11
|
|
@@ -28,8 +28,14 @@ from botocore.exceptions import ClientError, ProfileNotFound
|
|
28
28
|
|
29
29
|
# Import runbooks enterprise standards
|
30
30
|
from runbooks.common.rich_utils import (
|
31
|
-
console,
|
32
|
-
|
31
|
+
console,
|
32
|
+
print_header,
|
33
|
+
print_success,
|
34
|
+
print_warning,
|
35
|
+
print_error,
|
36
|
+
create_table,
|
37
|
+
create_progress_bar,
|
38
|
+
create_panel,
|
33
39
|
)
|
34
40
|
from runbooks.common.profile_utils import get_profile_for_operation
|
35
41
|
|
@@ -42,6 +48,7 @@ class EnhancedAccountInfo:
|
|
42
48
|
Enhanced account information based on runbooks inventory account patterns
|
43
49
|
Supports enterprise multi-account operations with session management
|
44
50
|
"""
|
51
|
+
|
45
52
|
account_id: str
|
46
53
|
account_name: str
|
47
54
|
account_status: str
|
@@ -51,11 +58,11 @@ class EnhancedAccountInfo:
|
|
51
58
|
account_type: str = "member" # management, member, suspended
|
52
59
|
session_cache: Dict[str, Any] = field(default_factory=dict)
|
53
60
|
last_accessed: Optional[float] = None
|
54
|
-
|
61
|
+
|
55
62
|
def __post_init__(self):
|
56
63
|
"""Initialize session cache with TTL management"""
|
57
64
|
self.last_accessed = time.time()
|
58
|
-
|
65
|
+
|
59
66
|
@property
|
60
67
|
def is_session_expired(self) -> bool:
|
61
68
|
"""Check if session cache is expired (4-hour TTL)"""
|
@@ -67,23 +74,23 @@ class EnhancedAccountInfo:
|
|
67
74
|
class CloudFoundationsAccountManager:
|
68
75
|
"""
|
69
76
|
Enhanced Account Manager integrating proven runbooks inventory patterns
|
70
|
-
|
77
|
+
|
71
78
|
Key Features:
|
72
79
|
- Multi-account organization discovery with filtering
|
73
|
-
- 4-hour TTL session management for 60+ account operations
|
80
|
+
- 4-hour TTL session management for 60+ account operations
|
74
81
|
- Enhanced error handling and graceful degradation
|
75
82
|
- Rich CLI integration with runbooks standards
|
76
83
|
- Cross-account role assumption with caching
|
77
|
-
|
84
|
+
|
78
85
|
Enhanced from: runbooks.inventory patterns with proven finops integration
|
79
86
|
"""
|
80
|
-
|
87
|
+
|
81
88
|
def __init__(self, profile: Optional[str] = None):
|
82
89
|
"""Initialize account manager with profile management"""
|
83
90
|
self.profile = get_profile_for_operation("management", profile)
|
84
91
|
self.accounts: Dict[str, EnhancedAccountInfo] = {}
|
85
92
|
self.session_cache: Dict[str, Dict[str, Any]] = {}
|
86
|
-
|
93
|
+
|
87
94
|
# Initialize base session
|
88
95
|
try:
|
89
96
|
self.base_session = boto3.Session(profile_name=self.profile)
|
@@ -91,270 +98,267 @@ class CloudFoundationsAccountManager:
|
|
91
98
|
except ProfileNotFound as e:
|
92
99
|
print_error(f"Profile not found: {self.profile}")
|
93
100
|
raise
|
94
|
-
|
101
|
+
|
95
102
|
async def discover_organization_structure(self) -> Dict[str, List[EnhancedAccountInfo]]:
|
96
103
|
"""
|
97
104
|
Enhanced organization discovery with structure analysis
|
98
|
-
|
105
|
+
|
99
106
|
Returns:
|
100
107
|
Dictionary organized by organizational units with account lists
|
101
|
-
|
108
|
+
|
102
109
|
Enhanced from: runbooks inventory organization discovery patterns with improved filtering and structure
|
103
110
|
"""
|
104
111
|
print_header("Organization Discovery", __version__)
|
105
|
-
|
112
|
+
|
106
113
|
try:
|
107
114
|
with create_progress_bar() as progress:
|
108
115
|
discovery_task = progress.add_task("Discovering organization structure...", total=100)
|
109
|
-
|
116
|
+
|
110
117
|
# Step 1: Discover organization accounts (40%)
|
111
118
|
accounts = await self._discover_accounts()
|
112
119
|
progress.update(discovery_task, advance=40)
|
113
|
-
|
114
|
-
# Step 2: Get organizational unit structure (30%)
|
120
|
+
|
121
|
+
# Step 2: Get organizational unit structure (30%)
|
115
122
|
ou_structure = await self._discover_organizational_units()
|
116
123
|
progress.update(discovery_task, advance=30)
|
117
|
-
|
124
|
+
|
118
125
|
# Step 3: Map accounts to OUs (30%)
|
119
126
|
structured_accounts = await self._map_accounts_to_ous(accounts, ou_structure)
|
120
127
|
progress.update(discovery_task, advance=30)
|
121
|
-
|
128
|
+
|
122
129
|
print_success(f"Discovered {len(accounts)} accounts across {len(structured_accounts)} organizational units")
|
123
130
|
return structured_accounts
|
124
|
-
|
131
|
+
|
125
132
|
except ClientError as e:
|
126
133
|
print_error(f"Organization discovery failed: {e}")
|
127
134
|
raise
|
128
|
-
|
135
|
+
|
129
136
|
async def _discover_accounts(self) -> List[EnhancedAccountInfo]:
|
130
137
|
"""
|
131
138
|
Discover all organization accounts with enhanced filtering
|
132
139
|
Based on proven runbooks inventory patterns
|
133
140
|
"""
|
134
141
|
try:
|
135
|
-
orgs_client = self.base_session.client(
|
142
|
+
orgs_client = self.base_session.client("organizations")
|
136
143
|
accounts = []
|
137
|
-
|
144
|
+
|
138
145
|
# Use paginator for large organizations (60+ accounts)
|
139
|
-
paginator = orgs_client.get_paginator(
|
140
|
-
|
146
|
+
paginator = orgs_client.get_paginator("list_accounts")
|
147
|
+
|
141
148
|
for page in paginator.paginate():
|
142
|
-
for account_data in page[
|
149
|
+
for account_data in page["Accounts"]:
|
143
150
|
# Filter active accounts only (decommissioning filter)
|
144
|
-
if account_data[
|
151
|
+
if account_data["Status"] == "ACTIVE":
|
145
152
|
account_info = EnhancedAccountInfo(
|
146
|
-
account_id=account_data[
|
147
|
-
account_name=account_data[
|
148
|
-
account_status=account_data[
|
149
|
-
account_email=account_data[
|
150
|
-
joined_method=account_data[
|
151
|
-
account_type=
|
153
|
+
account_id=account_data["Id"],
|
154
|
+
account_name=account_data["Name"],
|
155
|
+
account_status=account_data["Status"],
|
156
|
+
account_email=account_data["Email"],
|
157
|
+
joined_method=account_data["JoinedMethod"],
|
158
|
+
account_type="management"
|
159
|
+
if account_data["Id"] == self._get_management_account_id()
|
160
|
+
else "member",
|
152
161
|
)
|
153
162
|
accounts.append(account_info)
|
154
|
-
self.accounts[account_data[
|
155
|
-
|
163
|
+
self.accounts[account_data["Id"]] = account_info
|
164
|
+
|
156
165
|
return accounts
|
157
|
-
|
166
|
+
|
158
167
|
except ClientError as e:
|
159
|
-
if e.response[
|
168
|
+
if e.response["Error"]["Code"] == "AWSOrganizationsNotInUseException":
|
160
169
|
print_warning("Account is not part of an AWS Organization")
|
161
170
|
return []
|
162
171
|
raise
|
163
|
-
|
172
|
+
|
164
173
|
async def _discover_organizational_units(self) -> Dict[str, Dict[str, Any]]:
|
165
174
|
"""Discover organizational unit structure"""
|
166
175
|
try:
|
167
|
-
orgs_client = self.base_session.client(
|
168
|
-
|
176
|
+
orgs_client = self.base_session.client("organizations")
|
177
|
+
|
169
178
|
# Get root and traverse OU structure
|
170
|
-
roots = orgs_client.list_roots()[
|
179
|
+
roots = orgs_client.list_roots()["Roots"]
|
171
180
|
ou_structure = {}
|
172
|
-
|
181
|
+
|
173
182
|
for root in roots:
|
174
|
-
root_id = root[
|
183
|
+
root_id = root["Id"]
|
175
184
|
ou_structure[root_id] = {
|
176
|
-
|
177
|
-
|
178
|
-
|
185
|
+
"Name": root["Name"],
|
186
|
+
"Type": "ROOT",
|
187
|
+
"Children": await self._get_ou_children(orgs_client, root_id),
|
179
188
|
}
|
180
|
-
|
189
|
+
|
181
190
|
return ou_structure
|
182
|
-
|
191
|
+
|
183
192
|
except ClientError as e:
|
184
193
|
print_warning(f"Could not discover OU structure: {e}")
|
185
194
|
return {}
|
186
|
-
|
195
|
+
|
187
196
|
async def _get_ou_children(self, orgs_client, parent_id: str) -> List[Dict[str, Any]]:
|
188
197
|
"""Recursively get OU children"""
|
189
198
|
children = []
|
190
|
-
|
199
|
+
|
191
200
|
try:
|
192
|
-
paginator = orgs_client.get_paginator(
|
201
|
+
paginator = orgs_client.get_paginator("list_organizational_units_for_parent")
|
193
202
|
for page in paginator.paginate(ParentId=parent_id):
|
194
|
-
for ou in page[
|
203
|
+
for ou in page["OrganizationalUnits"]:
|
195
204
|
child_info = {
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
205
|
+
"Id": ou["Id"],
|
206
|
+
"Name": ou["Name"],
|
207
|
+
"Type": "ORGANIZATIONAL_UNIT",
|
208
|
+
"Children": await self._get_ou_children(orgs_client, ou["Id"]),
|
200
209
|
}
|
201
210
|
children.append(child_info)
|
202
|
-
|
211
|
+
|
203
212
|
except ClientError as e:
|
204
213
|
print_warning(f"Could not get children for {parent_id}: {e}")
|
205
|
-
|
214
|
+
|
206
215
|
return children
|
207
|
-
|
208
|
-
async def _map_accounts_to_ous(
|
209
|
-
|
210
|
-
|
216
|
+
|
217
|
+
async def _map_accounts_to_ous(
|
218
|
+
self, accounts: List[EnhancedAccountInfo], ou_structure: Dict[str, Dict[str, Any]]
|
219
|
+
) -> Dict[str, List[EnhancedAccountInfo]]:
|
211
220
|
"""Map accounts to their organizational units"""
|
212
221
|
mapped_accounts = {}
|
213
|
-
|
222
|
+
|
214
223
|
try:
|
215
|
-
orgs_client = self.base_session.client(
|
216
|
-
|
224
|
+
orgs_client = self.base_session.client("organizations")
|
225
|
+
|
217
226
|
for account in accounts:
|
218
227
|
try:
|
219
228
|
# Find which OU this account belongs to
|
220
|
-
parents = orgs_client.list_parents(ChildId=account.account_id)[
|
221
|
-
|
229
|
+
parents = orgs_client.list_parents(ChildId=account.account_id)["Parents"]
|
230
|
+
|
222
231
|
for parent in parents:
|
223
|
-
parent_id = parent[
|
224
|
-
parent_type = parent[
|
225
|
-
|
232
|
+
parent_id = parent["Id"]
|
233
|
+
parent_type = parent["Type"]
|
234
|
+
|
226
235
|
# Create OU key for grouping
|
227
236
|
ou_key = f"{parent_type}:{parent_id}"
|
228
237
|
if ou_key not in mapped_accounts:
|
229
238
|
mapped_accounts[ou_key] = []
|
230
|
-
|
239
|
+
|
231
240
|
account.organizational_unit = parent_id
|
232
241
|
mapped_accounts[ou_key].append(account)
|
233
|
-
|
242
|
+
|
234
243
|
except ClientError as e:
|
235
244
|
print_warning(f"Could not map account {account.account_id} to OU: {e}")
|
236
245
|
# Add to ungrouped accounts
|
237
|
-
if
|
238
|
-
mapped_accounts[
|
239
|
-
mapped_accounts[
|
240
|
-
|
246
|
+
if "ungrouped" not in mapped_accounts:
|
247
|
+
mapped_accounts["ungrouped"] = []
|
248
|
+
mapped_accounts["ungrouped"].append(account)
|
249
|
+
|
241
250
|
except Exception as e:
|
242
251
|
print_warning(f"Account to OU mapping encountered issues: {e}")
|
243
|
-
|
252
|
+
|
244
253
|
return mapped_accounts
|
245
|
-
|
246
|
-
def get_cross_account_session(
|
247
|
-
|
248
|
-
|
254
|
+
|
255
|
+
def get_cross_account_session(
|
256
|
+
self, target_account_id: str, role_name: str = "OrganizationAccountAccessRole"
|
257
|
+
) -> Optional[boto3.Session]:
|
249
258
|
"""
|
250
259
|
Get cross-account session with enhanced caching and TTL management
|
251
|
-
|
260
|
+
|
252
261
|
Based on runbooks inventory patterns with 4-hour TTL optimization
|
253
262
|
"""
|
254
263
|
session_key = f"{target_account_id}_{role_name}"
|
255
|
-
|
264
|
+
|
256
265
|
# Check session cache with TTL validation
|
257
|
-
if
|
258
|
-
|
259
|
-
|
260
|
-
|
266
|
+
if session_key in self.session_cache and not self._is_session_expired(self.session_cache[session_key]):
|
267
|
+
return self.session_cache[session_key]["session"]
|
268
|
+
|
261
269
|
# Create new cross-account session
|
262
270
|
try:
|
263
|
-
sts_client = self.base_session.client(
|
271
|
+
sts_client = self.base_session.client("sts")
|
264
272
|
role_arn = f"arn:aws:iam::{target_account_id}:role/{role_name}"
|
265
|
-
|
273
|
+
|
266
274
|
response = sts_client.assume_role(
|
267
275
|
RoleArn=role_arn,
|
268
276
|
RoleSessionName=f"CloudOpsRunbooks-CF-{target_account_id}",
|
269
|
-
DurationSeconds=14400 # 4 hours maximum
|
277
|
+
DurationSeconds=14400, # 4 hours maximum
|
270
278
|
)
|
271
|
-
|
272
|
-
credentials = response[
|
279
|
+
|
280
|
+
credentials = response["Credentials"]
|
273
281
|
cross_account_session = boto3.Session(
|
274
|
-
aws_access_key_id=credentials[
|
275
|
-
aws_secret_access_key=credentials[
|
276
|
-
aws_session_token=credentials[
|
282
|
+
aws_access_key_id=credentials["AccessKeyId"],
|
283
|
+
aws_secret_access_key=credentials["SecretAccessKey"],
|
284
|
+
aws_session_token=credentials["SessionToken"],
|
277
285
|
)
|
278
|
-
|
286
|
+
|
279
287
|
# Cache session with TTL
|
280
288
|
self.session_cache[session_key] = {
|
281
|
-
|
282
|
-
|
283
|
-
|
289
|
+
"session": cross_account_session,
|
290
|
+
"expires_at": credentials["Expiration"],
|
291
|
+
"created_at": time.time(),
|
284
292
|
}
|
285
|
-
|
293
|
+
|
286
294
|
return cross_account_session
|
287
|
-
|
295
|
+
|
288
296
|
except ClientError as e:
|
289
|
-
if
|
297
|
+
if "AccessDenied" in str(e):
|
290
298
|
print_warning(f"Cross-account access denied for {target_account_id} (role: {role_name})")
|
291
299
|
else:
|
292
300
|
print_warning(f"Cross-account session creation failed for {target_account_id}: {e}")
|
293
301
|
return None
|
294
|
-
|
302
|
+
|
295
303
|
def _is_session_expired(self, session_info: Dict[str, Any]) -> bool:
|
296
304
|
"""Check if cached session is expired"""
|
297
|
-
if
|
298
|
-
return time.time() >= session_info[
|
305
|
+
if "expires_at" in session_info:
|
306
|
+
return time.time() >= session_info["expires_at"].timestamp()
|
299
307
|
return True
|
300
|
-
|
308
|
+
|
301
309
|
def _get_management_account_id(self) -> Optional[str]:
|
302
310
|
"""Get the management account ID"""
|
303
311
|
try:
|
304
|
-
orgs_client = self.base_session.client(
|
312
|
+
orgs_client = self.base_session.client("organizations")
|
305
313
|
org_info = orgs_client.describe_organization()
|
306
|
-
return org_info[
|
314
|
+
return org_info["Organization"]["MasterAccountId"]
|
307
315
|
except ClientError:
|
308
316
|
return None
|
309
|
-
|
317
|
+
|
310
318
|
def display_organization_summary(self, structured_accounts: Dict[str, List[EnhancedAccountInfo]]):
|
311
319
|
"""
|
312
320
|
Display organization summary with Rich CLI formatting
|
313
321
|
Enterprise-ready visualization of multi-account structure
|
314
322
|
"""
|
315
323
|
print_header("Organization Structure Summary", __version__)
|
316
|
-
|
324
|
+
|
317
325
|
# Create summary table
|
318
326
|
table = create_table(
|
319
|
-
title="Multi-Account Organization Structure",
|
320
|
-
caption=f"Discovered via profile: {self.profile}"
|
327
|
+
title="Multi-Account Organization Structure", caption=f"Discovered via profile: {self.profile}"
|
321
328
|
)
|
322
|
-
|
329
|
+
|
323
330
|
table.add_column("Organizational Unit", style="cyan", no_wrap=True)
|
324
331
|
table.add_column("Account Count", justify="right", style="green")
|
325
332
|
table.add_column("Account Types", style="blue")
|
326
333
|
table.add_column("Status", style="yellow")
|
327
|
-
|
334
|
+
|
328
335
|
total_accounts = 0
|
329
336
|
management_accounts = 0
|
330
337
|
member_accounts = 0
|
331
|
-
|
338
|
+
|
332
339
|
for ou_key, accounts in structured_accounts.items():
|
333
340
|
account_types = []
|
334
341
|
active_count = 0
|
335
|
-
|
342
|
+
|
336
343
|
for account in accounts:
|
337
344
|
total_accounts += 1
|
338
|
-
if account.account_type ==
|
345
|
+
if account.account_type == "management":
|
339
346
|
management_accounts += 1
|
340
|
-
account_types.append(
|
347
|
+
account_types.append("Management")
|
341
348
|
else:
|
342
349
|
member_accounts += 1
|
343
|
-
account_types.append(
|
344
|
-
|
345
|
-
if account.account_status ==
|
350
|
+
account_types.append("Member")
|
351
|
+
|
352
|
+
if account.account_status == "ACTIVE":
|
346
353
|
active_count += 1
|
347
|
-
|
348
|
-
ou_display = ou_key.replace(
|
354
|
+
|
355
|
+
ou_display = ou_key.replace("ORGANIZATIONAL_UNIT:", "OU: ").replace("ROOT:", "Root: ")
|
349
356
|
table.add_row(
|
350
|
-
ou_display,
|
351
|
-
str(len(accounts)),
|
352
|
-
', '.join(set(account_types)),
|
353
|
-
f"{active_count}/{len(accounts)} Active"
|
357
|
+
ou_display, str(len(accounts)), ", ".join(set(account_types)), f"{active_count}/{len(accounts)} Active"
|
354
358
|
)
|
355
|
-
|
359
|
+
|
356
360
|
console.print(table)
|
357
|
-
|
361
|
+
|
358
362
|
# Summary panel
|
359
363
|
summary_text = f"""
|
360
364
|
Total Accounts: {total_accounts}
|
@@ -363,12 +367,8 @@ Member Accounts: {member_accounts}
|
|
363
367
|
Organizational Units: {len(structured_accounts)}
|
364
368
|
Session Cache: {len(self.session_cache)} active sessions
|
365
369
|
"""
|
366
|
-
|
367
|
-
summary_panel = create_panel(
|
368
|
-
summary_text,
|
369
|
-
title="Organization Summary",
|
370
|
-
style="green"
|
371
|
-
)
|
370
|
+
|
371
|
+
summary_panel = create_panel(summary_text, title="Organization Summary", style="green")
|
372
372
|
console.print(summary_panel)
|
373
373
|
|
374
374
|
|
@@ -378,32 +378,27 @@ async def main():
|
|
378
378
|
Shows enhanced multi-account discovery capabilities
|
379
379
|
"""
|
380
380
|
import argparse
|
381
|
-
|
382
|
-
parser = argparse.ArgumentParser(
|
383
|
-
|
384
|
-
)
|
385
|
-
parser.add_argument(
|
386
|
-
'--profile',
|
387
|
-
help='AWS profile to use (defaults to management profile detection)'
|
388
|
-
)
|
381
|
+
|
382
|
+
parser = argparse.ArgumentParser(description="Cloud Foundations Integration - Enhanced Multi-Account Discovery")
|
383
|
+
parser.add_argument("--profile", help="AWS profile to use (defaults to management profile detection)")
|
389
384
|
args = parser.parse_args()
|
390
|
-
|
385
|
+
|
391
386
|
# Initialize enhanced account manager
|
392
387
|
try:
|
393
388
|
account_manager = CloudFoundationsAccountManager(profile=args.profile)
|
394
|
-
|
389
|
+
|
395
390
|
# Discover organization structure
|
396
391
|
structured_accounts = await account_manager.discover_organization_structure()
|
397
|
-
|
392
|
+
|
398
393
|
# Display results with Rich formatting
|
399
394
|
account_manager.display_organization_summary(structured_accounts)
|
400
|
-
|
395
|
+
|
401
396
|
print_success("Cloud Foundations integration demonstration completed successfully")
|
402
|
-
|
397
|
+
|
403
398
|
except Exception as e:
|
404
399
|
print_error(f"Integration demonstration failed: {e}")
|
405
400
|
raise
|
406
401
|
|
407
402
|
|
408
403
|
if __name__ == "__main__":
|
409
|
-
asyncio.run(main())
|
404
|
+
asyncio.run(main())
|
@@ -307,7 +307,7 @@ class ComprehensiveCollector(BaseResourceCollector):
|
|
307
307
|
org_accounts = profile_manager.discover_organization_accounts()
|
308
308
|
|
309
309
|
if org_accounts:
|
310
|
-
account_ids = [acc[
|
310
|
+
account_ids = [acc["Id"] for acc in org_accounts]
|
311
311
|
print(f"🏢 Organization Discovery: {len(account_ids)} accounts found")
|
312
312
|
return account_ids
|
313
313
|
except Exception as e:
|