runbooks 1.1.3__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 (247) 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/WEIGHT_CONFIG_README.md +1 -1
  8. runbooks/cfat/assessment/compliance.py +8 -8
  9. runbooks/cfat/assessment/runner.py +1 -0
  10. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  11. runbooks/cfat/models.py +6 -2
  12. runbooks/cfat/tests/__init__.py +6 -1
  13. runbooks/cli/__init__.py +13 -0
  14. runbooks/cli/commands/cfat.py +274 -0
  15. runbooks/cli/commands/finops.py +1164 -0
  16. runbooks/cli/commands/inventory.py +379 -0
  17. runbooks/cli/commands/operate.py +239 -0
  18. runbooks/cli/commands/security.py +248 -0
  19. runbooks/cli/commands/validation.py +825 -0
  20. runbooks/cli/commands/vpc.py +310 -0
  21. runbooks/cli/registry.py +107 -0
  22. runbooks/cloudops/__init__.py +23 -30
  23. runbooks/cloudops/base.py +96 -107
  24. runbooks/cloudops/cost_optimizer.py +549 -547
  25. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  26. runbooks/cloudops/interfaces.py +226 -227
  27. runbooks/cloudops/lifecycle_manager.py +5 -4
  28. runbooks/cloudops/mcp_cost_validation.py +252 -235
  29. runbooks/cloudops/models.py +78 -53
  30. runbooks/cloudops/monitoring_automation.py +5 -4
  31. runbooks/cloudops/notebook_framework.py +179 -215
  32. runbooks/cloudops/security_enforcer.py +125 -159
  33. runbooks/common/accuracy_validator.py +11 -0
  34. runbooks/common/aws_pricing.py +349 -326
  35. runbooks/common/aws_pricing_api.py +211 -212
  36. runbooks/common/aws_profile_manager.py +341 -0
  37. runbooks/common/aws_utils.py +75 -80
  38. runbooks/common/business_logic.py +127 -105
  39. runbooks/common/cli_decorators.py +36 -60
  40. runbooks/common/comprehensive_cost_explorer_integration.py +456 -464
  41. runbooks/common/cross_account_manager.py +198 -205
  42. runbooks/common/date_utils.py +27 -39
  43. runbooks/common/decorators.py +235 -0
  44. runbooks/common/dry_run_examples.py +173 -208
  45. runbooks/common/dry_run_framework.py +157 -155
  46. runbooks/common/enhanced_exception_handler.py +15 -4
  47. runbooks/common/enhanced_logging_example.py +50 -64
  48. runbooks/common/enhanced_logging_integration_example.py +65 -37
  49. runbooks/common/env_utils.py +16 -16
  50. runbooks/common/error_handling.py +40 -38
  51. runbooks/common/lazy_loader.py +41 -23
  52. runbooks/common/logging_integration_helper.py +79 -86
  53. runbooks/common/mcp_cost_explorer_integration.py +478 -495
  54. runbooks/common/mcp_integration.py +63 -74
  55. runbooks/common/memory_optimization.py +140 -118
  56. runbooks/common/module_cli_base.py +37 -58
  57. runbooks/common/organizations_client.py +176 -194
  58. runbooks/common/patterns.py +204 -0
  59. runbooks/common/performance_monitoring.py +67 -71
  60. runbooks/common/performance_optimization_engine.py +283 -274
  61. runbooks/common/profile_utils.py +248 -39
  62. runbooks/common/rich_utils.py +643 -92
  63. runbooks/common/sre_performance_suite.py +177 -186
  64. runbooks/enterprise/__init__.py +1 -1
  65. runbooks/enterprise/logging.py +144 -106
  66. runbooks/enterprise/security.py +187 -204
  67. runbooks/enterprise/validation.py +43 -56
  68. runbooks/finops/__init__.py +29 -33
  69. runbooks/finops/account_resolver.py +1 -1
  70. runbooks/finops/advanced_optimization_engine.py +980 -0
  71. runbooks/finops/automation_core.py +268 -231
  72. runbooks/finops/business_case_config.py +184 -179
  73. runbooks/finops/cli.py +660 -139
  74. runbooks/finops/commvault_ec2_analysis.py +157 -164
  75. runbooks/finops/compute_cost_optimizer.py +336 -320
  76. runbooks/finops/config.py +20 -20
  77. runbooks/finops/cost_optimizer.py +488 -622
  78. runbooks/finops/cost_processor.py +332 -214
  79. runbooks/finops/dashboard_runner.py +1006 -172
  80. runbooks/finops/ebs_cost_optimizer.py +991 -657
  81. runbooks/finops/elastic_ip_optimizer.py +317 -257
  82. runbooks/finops/enhanced_mcp_integration.py +340 -0
  83. runbooks/finops/enhanced_progress.py +40 -37
  84. runbooks/finops/enhanced_trend_visualization.py +3 -2
  85. runbooks/finops/enterprise_wrappers.py +230 -292
  86. runbooks/finops/executive_export.py +203 -160
  87. runbooks/finops/helpers.py +130 -288
  88. runbooks/finops/iam_guidance.py +1 -1
  89. runbooks/finops/infrastructure/__init__.py +80 -0
  90. runbooks/finops/infrastructure/commands.py +506 -0
  91. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  92. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  93. runbooks/finops/markdown_exporter.py +338 -175
  94. runbooks/finops/mcp_validator.py +1952 -0
  95. runbooks/finops/nat_gateway_optimizer.py +1513 -482
  96. runbooks/finops/network_cost_optimizer.py +657 -587
  97. runbooks/finops/notebook_utils.py +226 -188
  98. runbooks/finops/optimization_engine.py +1136 -0
  99. runbooks/finops/optimizer.py +25 -29
  100. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  101. runbooks/finops/reservation_optimizer.py +427 -363
  102. runbooks/finops/scenario_cli_integration.py +77 -78
  103. runbooks/finops/scenarios.py +1278 -439
  104. runbooks/finops/schemas.py +218 -182
  105. runbooks/finops/snapshot_manager.py +2289 -0
  106. runbooks/finops/tests/test_finops_dashboard.py +3 -3
  107. runbooks/finops/tests/test_reference_images_validation.py +2 -2
  108. runbooks/finops/tests/test_single_account_features.py +17 -17
  109. runbooks/finops/tests/validate_test_suite.py +1 -1
  110. runbooks/finops/types.py +3 -3
  111. runbooks/finops/validation_framework.py +263 -269
  112. runbooks/finops/vpc_cleanup_exporter.py +191 -146
  113. runbooks/finops/vpc_cleanup_optimizer.py +593 -575
  114. runbooks/finops/workspaces_analyzer.py +171 -182
  115. runbooks/hitl/enhanced_workflow_engine.py +1 -1
  116. runbooks/integration/__init__.py +89 -0
  117. runbooks/integration/mcp_integration.py +1920 -0
  118. runbooks/inventory/CLAUDE.md +816 -0
  119. runbooks/inventory/README.md +3 -3
  120. runbooks/inventory/Tests/common_test_data.py +30 -30
  121. runbooks/inventory/__init__.py +2 -2
  122. runbooks/inventory/cloud_foundations_integration.py +144 -149
  123. runbooks/inventory/collectors/aws_comprehensive.py +28 -11
  124. runbooks/inventory/collectors/aws_networking.py +111 -101
  125. runbooks/inventory/collectors/base.py +4 -0
  126. runbooks/inventory/core/collector.py +495 -313
  127. runbooks/inventory/discovery.md +2 -2
  128. runbooks/inventory/drift_detection_cli.py +69 -96
  129. runbooks/inventory/find_ec2_security_groups.py +1 -1
  130. runbooks/inventory/inventory_mcp_cli.py +48 -46
  131. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  132. runbooks/inventory/mcp_inventory_validator.py +549 -465
  133. runbooks/inventory/mcp_vpc_validator.py +359 -442
  134. runbooks/inventory/organizations_discovery.py +56 -52
  135. runbooks/inventory/rich_inventory_display.py +33 -32
  136. runbooks/inventory/unified_validation_engine.py +278 -251
  137. runbooks/inventory/vpc_analyzer.py +733 -696
  138. runbooks/inventory/vpc_architecture_validator.py +293 -348
  139. runbooks/inventory/vpc_dependency_analyzer.py +382 -378
  140. runbooks/inventory/vpc_flow_analyzer.py +3 -3
  141. runbooks/main.py +152 -9147
  142. runbooks/main_final.py +91 -60
  143. runbooks/main_minimal.py +22 -10
  144. runbooks/main_optimized.py +131 -100
  145. runbooks/main_ultra_minimal.py +7 -2
  146. runbooks/mcp/__init__.py +36 -0
  147. runbooks/mcp/integration.py +679 -0
  148. runbooks/metrics/dora_metrics_engine.py +2 -2
  149. runbooks/monitoring/performance_monitor.py +9 -4
  150. runbooks/operate/dynamodb_operations.py +3 -1
  151. runbooks/operate/ec2_operations.py +145 -137
  152. runbooks/operate/iam_operations.py +146 -152
  153. runbooks/operate/mcp_integration.py +1 -1
  154. runbooks/operate/networking_cost_heatmap.py +33 -10
  155. runbooks/operate/privatelink_operations.py +1 -1
  156. runbooks/operate/rds_operations.py +223 -254
  157. runbooks/operate/s3_operations.py +107 -118
  158. runbooks/operate/vpc_endpoints.py +1 -1
  159. runbooks/operate/vpc_operations.py +648 -618
  160. runbooks/remediation/base.py +1 -1
  161. runbooks/remediation/commons.py +10 -7
  162. runbooks/remediation/commvault_ec2_analysis.py +71 -67
  163. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  164. runbooks/remediation/multi_account.py +24 -21
  165. runbooks/remediation/rds_snapshot_list.py +91 -65
  166. runbooks/remediation/remediation_cli.py +92 -146
  167. runbooks/remediation/universal_account_discovery.py +83 -79
  168. runbooks/remediation/workspaces_list.py +49 -44
  169. runbooks/security/__init__.py +19 -0
  170. runbooks/security/assessment_runner.py +1150 -0
  171. runbooks/security/baseline_checker.py +812 -0
  172. runbooks/security/cloudops_automation_security_validator.py +509 -535
  173. runbooks/security/compliance_automation_engine.py +17 -17
  174. runbooks/security/config/__init__.py +2 -2
  175. runbooks/security/config/compliance_config.py +50 -50
  176. runbooks/security/config_template_generator.py +63 -76
  177. runbooks/security/enterprise_security_framework.py +1 -1
  178. runbooks/security/executive_security_dashboard.py +519 -508
  179. runbooks/security/integration_test_enterprise_security.py +5 -3
  180. runbooks/security/multi_account_security_controls.py +959 -1210
  181. runbooks/security/real_time_security_monitor.py +422 -444
  182. runbooks/security/run_script.py +1 -1
  183. runbooks/security/security_baseline_tester.py +1 -1
  184. runbooks/security/security_cli.py +143 -112
  185. runbooks/security/test_2way_validation.py +439 -0
  186. runbooks/security/two_way_validation_framework.py +852 -0
  187. runbooks/sre/mcp_reliability_engine.py +6 -6
  188. runbooks/sre/production_monitoring_framework.py +167 -177
  189. runbooks/tdd/__init__.py +15 -0
  190. runbooks/tdd/cli.py +1071 -0
  191. runbooks/utils/__init__.py +14 -17
  192. runbooks/utils/logger.py +7 -2
  193. runbooks/utils/version_validator.py +51 -48
  194. runbooks/validation/__init__.py +6 -6
  195. runbooks/validation/cli.py +9 -3
  196. runbooks/validation/comprehensive_2way_validator.py +754 -708
  197. runbooks/validation/mcp_validator.py +906 -228
  198. runbooks/validation/terraform_citations_validator.py +104 -115
  199. runbooks/validation/terraform_drift_detector.py +447 -451
  200. runbooks/vpc/README.md +617 -0
  201. runbooks/vpc/__init__.py +8 -1
  202. runbooks/vpc/analyzer.py +577 -0
  203. runbooks/vpc/cleanup_wrapper.py +476 -413
  204. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  205. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  206. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  207. runbooks/vpc/config.py +92 -97
  208. runbooks/vpc/cost_engine.py +411 -148
  209. runbooks/vpc/cost_explorer_integration.py +553 -0
  210. runbooks/vpc/cross_account_session.py +101 -106
  211. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  212. runbooks/vpc/eni_gate_validator.py +961 -0
  213. runbooks/vpc/heatmap_engine.py +190 -162
  214. runbooks/vpc/mcp_no_eni_validator.py +681 -640
  215. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  216. runbooks/vpc/networking_wrapper.py +15 -8
  217. runbooks/vpc/pdca_remediation_planner.py +528 -0
  218. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  219. runbooks/vpc/runbooks_adapter.py +1167 -241
  220. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  221. runbooks/vpc/test_data_loader.py +358 -0
  222. runbooks/vpc/tests/conftest.py +314 -4
  223. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  224. runbooks/vpc/tests/test_cost_engine.py +0 -2
  225. runbooks/vpc/topology_generator.py +326 -0
  226. runbooks/vpc/unified_scenarios.py +1302 -1129
  227. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  228. runbooks-1.1.5.dist-info/METADATA +328 -0
  229. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/RECORD +233 -200
  230. runbooks/finops/README.md +0 -414
  231. runbooks/finops/accuracy_cross_validator.py +0 -647
  232. runbooks/finops/business_cases.py +0 -950
  233. runbooks/finops/dashboard_router.py +0 -922
  234. runbooks/finops/ebs_optimizer.py +0 -956
  235. runbooks/finops/embedded_mcp_validator.py +0 -1629
  236. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  237. runbooks/finops/finops_dashboard.py +0 -584
  238. runbooks/finops/finops_scenarios.py +0 -1218
  239. runbooks/finops/legacy_migration.py +0 -730
  240. runbooks/finops/multi_dashboard.py +0 -1519
  241. runbooks/finops/single_dashboard.py +0 -1113
  242. runbooks/finops/unlimited_scenarios.py +0 -393
  243. runbooks-1.1.3.dist-info/METADATA +0 -799
  244. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/WHEEL +0 -0
  245. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/entry_points.txt +0 -0
  246. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/licenses/LICENSE +0 -0
  247. {runbooks-1.1.3.dist-info → runbooks-1.1.5.dist-info}/top_level.txt +0 -0
@@ -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())
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Comprehensive AWS Resource Collector for Multi-Account Organizations
3
- Sprint 1: Discovery & Assessment - Enhanced for parallel processing
3
+ Phase 1: Discovery & Assessment - Enhanced for parallel processing
4
4
  Supports any organization size: 10, 60, 200+ accounts dynamically
5
5
  """
6
6
 
@@ -14,12 +14,13 @@ import boto3
14
14
  from botocore.exceptions import ClientError, NoCredentialsError
15
15
 
16
16
  from runbooks.inventory.collectors.base import BaseResourceCollector
17
+ from runbooks.common.aws_profile_manager import AWSProfileManager, get_current_account_id
17
18
 
18
19
 
19
20
  class ComprehensiveCollector(BaseResourceCollector):
20
21
  """
21
22
  Collect all AWS resources across multi-account organization with parallel processing.
22
- Optimized for Sprint 1 discovery goals.
23
+ Optimized for Phase 1 discovery goals.
23
24
  """
24
25
 
25
26
  def __init__(self, profile: str = None, parallel_workers: int = 10):
@@ -100,7 +101,7 @@ class ComprehensiveCollector(BaseResourceCollector):
100
101
  # Generate summary statistics
101
102
  results["summary"] = self._generate_summary(results["resources"])
102
103
 
103
- # Save results to Sprint 1 artifacts
104
+ # Save results to Phase 1 artifacts
104
105
  self._save_results(results)
105
106
 
106
107
  return results
@@ -215,7 +216,7 @@ class ComprehensiveCollector(BaseResourceCollector):
215
216
  """
216
217
  html_content = self._create_visualization_html(results)
217
218
 
218
- output_path = "artifacts/sprint-1/inventory/visualization.html"
219
+ output_path = "artifacts/phase-1/inventory/visualization.html"
219
220
  with open(output_path, "w") as f:
220
221
  f.write(html_content)
221
222
 
@@ -228,7 +229,7 @@ class ComprehensiveCollector(BaseResourceCollector):
228
229
  <!DOCTYPE html>
229
230
  <html>
230
231
  <head>
231
- <title>AWS Multi-Account Inventory - Sprint 1</title>
232
+ <title>AWS Multi-Account Inventory - Phase 1</title>
232
233
  <script src="https://d3js.org/d3.v7.min.js"></script>
233
234
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
234
235
  <style>
@@ -243,7 +244,7 @@ class ComprehensiveCollector(BaseResourceCollector):
243
244
  </head>
244
245
  <body>
245
246
  <h1>🏗️ AWS Organization Inventory Dashboard</h1>
246
- <h2>Sprint 1: Discovery & Assessment</h2>
247
+ <h2>Phase 1: Discovery & Assessment</h2>
247
248
 
248
249
  <div class="metrics">
249
250
  <div class="metric">
@@ -292,16 +293,32 @@ class ComprehensiveCollector(BaseResourceCollector):
292
293
  return html
293
294
 
294
295
  def _save_results(self, results: Dict):
295
- """Save results to Sprint 1 artifacts directory."""
296
- output_path = "artifacts/sprint-1/inventory/resources.json"
296
+ """Save results to Phase 1 artifacts directory."""
297
+ output_path = "artifacts/phase-1/inventory/resources.json"
297
298
  with open(output_path, "w") as f:
298
299
  json.dump(results, f, indent=2, default=str)
299
300
  print(f"Inventory saved: {output_path}")
300
301
 
301
302
  def _discover_all_accounts(self) -> List[str]:
302
303
  """Discover all accounts in the organization (enhanced for multi-account org)."""
303
- # Enhanced mock for comprehensive organization discovery
304
- base_accounts = ["123456789012", "234567890123", "345678901234"]
304
+ try:
305
+ # Try real organization discovery first
306
+ profile_manager = AWSProfileManager(self.profile)
307
+ org_accounts = profile_manager.discover_organization_accounts()
308
+
309
+ if org_accounts:
310
+ account_ids = [acc["Id"] for acc in org_accounts]
311
+ print(f"🏢 Organization Discovery: {len(account_ids)} accounts found")
312
+ return account_ids
313
+ except Exception as e:
314
+ print(f"⚠️ Organization discovery failed, using mock: {e}")
315
+
316
+ # Fallback to mock organization for testing
317
+ profile_manager = AWSProfileManager(self.profile)
318
+ current_account = profile_manager.get_account_id()
319
+
320
+ # Include current account plus simulated additional accounts
321
+ base_accounts = [current_account, "234567890123", "345678901234"]
305
322
 
306
323
  # Generate additional accounts to simulate large organization
307
324
  additional_accounts = []
@@ -310,7 +327,7 @@ class ComprehensiveCollector(BaseResourceCollector):
310
327
  additional_accounts.append(account_id)
311
328
 
312
329
  all_accounts = base_accounts + additional_accounts
313
- print(f"🏢 Organization Discovery: {len(all_accounts)} accounts found")
330
+ print(f"🏢 Mock Organization Discovery: {len(all_accounts)} accounts simulated")
314
331
  return all_accounts
315
332
 
316
333
  def _get_account_session(self, account_id: str):