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.
Files changed (228) 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 +138 -35
  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 +11 -0
  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 +63 -74
  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 +201 -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/cloud_foundations_integration.py +144 -149
  113. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  114. runbooks/inventory/collectors/aws_networking.py +109 -99
  115. runbooks/inventory/collectors/base.py +4 -0
  116. runbooks/inventory/core/collector.py +495 -313
  117. runbooks/inventory/drift_detection_cli.py +69 -96
  118. runbooks/inventory/inventory_mcp_cli.py +48 -46
  119. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  120. runbooks/inventory/mcp_inventory_validator.py +549 -465
  121. runbooks/inventory/mcp_vpc_validator.py +359 -442
  122. runbooks/inventory/organizations_discovery.py +55 -51
  123. runbooks/inventory/rich_inventory_display.py +33 -32
  124. runbooks/inventory/unified_validation_engine.py +278 -251
  125. runbooks/inventory/vpc_analyzer.py +732 -695
  126. runbooks/inventory/vpc_architecture_validator.py +293 -348
  127. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  128. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  129. runbooks/main.py +49 -34
  130. runbooks/main_final.py +91 -60
  131. runbooks/main_minimal.py +22 -10
  132. runbooks/main_optimized.py +131 -100
  133. runbooks/main_ultra_minimal.py +7 -2
  134. runbooks/mcp/__init__.py +36 -0
  135. runbooks/mcp/integration.py +679 -0
  136. runbooks/monitoring/performance_monitor.py +9 -4
  137. runbooks/operate/dynamodb_operations.py +3 -1
  138. runbooks/operate/ec2_operations.py +145 -137
  139. runbooks/operate/iam_operations.py +146 -152
  140. runbooks/operate/networking_cost_heatmap.py +29 -8
  141. runbooks/operate/rds_operations.py +223 -254
  142. runbooks/operate/s3_operations.py +107 -118
  143. runbooks/operate/vpc_operations.py +646 -616
  144. runbooks/remediation/base.py +1 -1
  145. runbooks/remediation/commons.py +10 -7
  146. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  147. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  148. runbooks/remediation/multi_account.py +24 -21
  149. runbooks/remediation/rds_snapshot_list.py +86 -60
  150. runbooks/remediation/remediation_cli.py +92 -146
  151. runbooks/remediation/universal_account_discovery.py +83 -79
  152. runbooks/remediation/workspaces_list.py +46 -41
  153. runbooks/security/__init__.py +19 -0
  154. runbooks/security/assessment_runner.py +1150 -0
  155. runbooks/security/baseline_checker.py +812 -0
  156. runbooks/security/cloudops_automation_security_validator.py +509 -535
  157. runbooks/security/compliance_automation_engine.py +17 -17
  158. runbooks/security/config/__init__.py +2 -2
  159. runbooks/security/config/compliance_config.py +50 -50
  160. runbooks/security/config_template_generator.py +63 -76
  161. runbooks/security/enterprise_security_framework.py +1 -1
  162. runbooks/security/executive_security_dashboard.py +519 -508
  163. runbooks/security/multi_account_security_controls.py +959 -1210
  164. runbooks/security/real_time_security_monitor.py +422 -444
  165. runbooks/security/security_baseline_tester.py +1 -1
  166. runbooks/security/security_cli.py +143 -112
  167. runbooks/security/test_2way_validation.py +439 -0
  168. runbooks/security/two_way_validation_framework.py +852 -0
  169. runbooks/sre/production_monitoring_framework.py +167 -177
  170. runbooks/tdd/__init__.py +15 -0
  171. runbooks/tdd/cli.py +1071 -0
  172. runbooks/utils/__init__.py +14 -17
  173. runbooks/utils/logger.py +7 -2
  174. runbooks/utils/version_validator.py +50 -47
  175. runbooks/validation/__init__.py +6 -6
  176. runbooks/validation/cli.py +9 -3
  177. runbooks/validation/comprehensive_2way_validator.py +745 -704
  178. runbooks/validation/mcp_validator.py +906 -228
  179. runbooks/validation/terraform_citations_validator.py +104 -115
  180. runbooks/validation/terraform_drift_detector.py +447 -451
  181. runbooks/vpc/README.md +617 -0
  182. runbooks/vpc/__init__.py +8 -1
  183. runbooks/vpc/analyzer.py +577 -0
  184. runbooks/vpc/cleanup_wrapper.py +476 -413
  185. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  186. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  187. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  188. runbooks/vpc/config.py +92 -97
  189. runbooks/vpc/cost_engine.py +411 -148
  190. runbooks/vpc/cost_explorer_integration.py +553 -0
  191. runbooks/vpc/cross_account_session.py +101 -106
  192. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  193. runbooks/vpc/eni_gate_validator.py +961 -0
  194. runbooks/vpc/heatmap_engine.py +185 -160
  195. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  196. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  197. runbooks/vpc/networking_wrapper.py +15 -8
  198. runbooks/vpc/pdca_remediation_planner.py +528 -0
  199. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  200. runbooks/vpc/runbooks_adapter.py +1167 -241
  201. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  202. runbooks/vpc/test_data_loader.py +358 -0
  203. runbooks/vpc/tests/conftest.py +314 -4
  204. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  205. runbooks/vpc/tests/test_cost_engine.py +0 -2
  206. runbooks/vpc/topology_generator.py +326 -0
  207. runbooks/vpc/unified_scenarios.py +1297 -1124
  208. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  209. runbooks-1.1.5.dist-info/METADATA +328 -0
  210. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/RECORD +214 -193
  211. runbooks/finops/README.md +0 -414
  212. runbooks/finops/accuracy_cross_validator.py +0 -647
  213. runbooks/finops/business_cases.py +0 -950
  214. runbooks/finops/dashboard_router.py +0 -922
  215. runbooks/finops/ebs_optimizer.py +0 -973
  216. runbooks/finops/embedded_mcp_validator.py +0 -1629
  217. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  218. runbooks/finops/finops_dashboard.py +0 -584
  219. runbooks/finops/finops_scenarios.py +0 -1218
  220. runbooks/finops/legacy_migration.py +0 -730
  221. runbooks/finops/multi_dashboard.py +0 -1519
  222. runbooks/finops/single_dashboard.py +0 -1113
  223. runbooks/finops/unlimited_scenarios.py +0 -393
  224. runbooks-1.1.4.dist-info/METADATA +0 -800
  225. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  226. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  227. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  228. {runbooks-1.1.4.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -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, print_header, print_success, print_warning, print_error,
32
- create_table, create_progress_bar, create_panel
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('organizations')
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('list_accounts')
140
-
146
+ paginator = orgs_client.get_paginator("list_accounts")
147
+
141
148
  for page in paginator.paginate():
142
- for account_data in page['Accounts']:
149
+ for account_data in page["Accounts"]:
143
150
  # Filter active accounts only (decommissioning filter)
144
- if account_data['Status'] == 'ACTIVE':
151
+ if account_data["Status"] == "ACTIVE":
145
152
  account_info = EnhancedAccountInfo(
146
- account_id=account_data['Id'],
147
- account_name=account_data['Name'],
148
- account_status=account_data['Status'],
149
- account_email=account_data['Email'],
150
- joined_method=account_data['JoinedMethod'],
151
- account_type='management' if account_data['Id'] == self._get_management_account_id() else 'member'
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['Id']] = account_info
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['Error']['Code'] == 'AWSOrganizationsNotInUseException':
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('organizations')
168
-
176
+ orgs_client = self.base_session.client("organizations")
177
+
169
178
  # Get root and traverse OU structure
170
- roots = orgs_client.list_roots()['Roots']
179
+ roots = orgs_client.list_roots()["Roots"]
171
180
  ou_structure = {}
172
-
181
+
173
182
  for root in roots:
174
- root_id = root['Id']
183
+ root_id = root["Id"]
175
184
  ou_structure[root_id] = {
176
- 'Name': root['Name'],
177
- 'Type': 'ROOT',
178
- 'Children': await self._get_ou_children(orgs_client, root_id)
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('list_organizational_units_for_parent')
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['OrganizationalUnits']:
203
+ for ou in page["OrganizationalUnits"]:
195
204
  child_info = {
196
- 'Id': ou['Id'],
197
- 'Name': ou['Name'],
198
- 'Type': 'ORGANIZATIONAL_UNIT',
199
- 'Children': await self._get_ou_children(orgs_client, ou['Id'])
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(self,
209
- accounts: List[EnhancedAccountInfo],
210
- ou_structure: Dict[str, Dict[str, Any]]) -> Dict[str, List[EnhancedAccountInfo]]:
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('organizations')
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)['Parents']
221
-
229
+ parents = orgs_client.list_parents(ChildId=account.account_id)["Parents"]
230
+
222
231
  for parent in parents:
223
- parent_id = parent['Id']
224
- parent_type = parent['Type']
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 'ungrouped' not in mapped_accounts:
238
- mapped_accounts['ungrouped'] = []
239
- mapped_accounts['ungrouped'].append(account)
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(self,
247
- target_account_id: str,
248
- role_name: str = "OrganizationAccountAccessRole") -> Optional[boto3.Session]:
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 (session_key in self.session_cache and
258
- not self._is_session_expired(self.session_cache[session_key])):
259
- return self.session_cache[session_key]['session']
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('sts')
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['Credentials']
279
+
280
+ credentials = response["Credentials"]
273
281
  cross_account_session = boto3.Session(
274
- aws_access_key_id=credentials['AccessKeyId'],
275
- aws_secret_access_key=credentials['SecretAccessKey'],
276
- aws_session_token=credentials['SessionToken']
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
- 'session': cross_account_session,
282
- 'expires_at': credentials['Expiration'],
283
- 'created_at': time.time()
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 'AccessDenied' in str(e):
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 'expires_at' in session_info:
298
- return time.time() >= session_info['expires_at'].timestamp()
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('organizations')
312
+ orgs_client = self.base_session.client("organizations")
305
313
  org_info = orgs_client.describe_organization()
306
- return org_info['Organization']['MasterAccountId']
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 == 'management':
345
+ if account.account_type == "management":
339
346
  management_accounts += 1
340
- account_types.append('Management')
347
+ account_types.append("Management")
341
348
  else:
342
349
  member_accounts += 1
343
- account_types.append('Member')
344
-
345
- if account.account_status == 'ACTIVE':
350
+ account_types.append("Member")
351
+
352
+ if account.account_status == "ACTIVE":
346
353
  active_count += 1
347
-
348
- ou_display = ou_key.replace('ORGANIZATIONAL_UNIT:', 'OU: ').replace('ROOT:', 'Root: ')
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
- description="Cloud Foundations Integration - Enhanced Multi-Account Discovery"
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['Id'] for acc in org_accounts]
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: