runbooks 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. runbooks/__init__.py +31 -2
  2. runbooks/__init___optimized.py +18 -4
  3. runbooks/_platform/__init__.py +1 -5
  4. runbooks/_platform/core/runbooks_wrapper.py +141 -138
  5. runbooks/aws2/accuracy_validator.py +812 -0
  6. runbooks/base.py +7 -0
  7. runbooks/cfat/assessment/compliance.py +1 -1
  8. runbooks/cfat/assessment/runner.py +1 -0
  9. runbooks/cfat/cloud_foundations_assessment.py +227 -239
  10. runbooks/cli/__init__.py +1 -1
  11. runbooks/cli/commands/cfat.py +64 -23
  12. runbooks/cli/commands/finops.py +1005 -54
  13. runbooks/cli/commands/inventory.py +135 -91
  14. runbooks/cli/commands/operate.py +9 -36
  15. runbooks/cli/commands/security.py +42 -18
  16. runbooks/cli/commands/validation.py +432 -18
  17. runbooks/cli/commands/vpc.py +81 -17
  18. runbooks/cli/registry.py +22 -10
  19. runbooks/cloudops/__init__.py +20 -27
  20. runbooks/cloudops/base.py +96 -107
  21. runbooks/cloudops/cost_optimizer.py +544 -542
  22. runbooks/cloudops/infrastructure_optimizer.py +5 -4
  23. runbooks/cloudops/interfaces.py +224 -225
  24. runbooks/cloudops/lifecycle_manager.py +5 -4
  25. runbooks/cloudops/mcp_cost_validation.py +252 -235
  26. runbooks/cloudops/models.py +78 -53
  27. runbooks/cloudops/monitoring_automation.py +5 -4
  28. runbooks/cloudops/notebook_framework.py +177 -213
  29. runbooks/cloudops/security_enforcer.py +125 -159
  30. runbooks/common/accuracy_validator.py +17 -12
  31. runbooks/common/aws_pricing.py +349 -326
  32. runbooks/common/aws_pricing_api.py +211 -212
  33. runbooks/common/aws_profile_manager.py +40 -36
  34. runbooks/common/aws_utils.py +74 -79
  35. runbooks/common/business_logic.py +126 -104
  36. runbooks/common/cli_decorators.py +36 -60
  37. runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
  38. runbooks/common/cross_account_manager.py +197 -204
  39. runbooks/common/date_utils.py +27 -39
  40. runbooks/common/decorators.py +29 -19
  41. runbooks/common/dry_run_examples.py +173 -208
  42. runbooks/common/dry_run_framework.py +157 -155
  43. runbooks/common/enhanced_exception_handler.py +15 -4
  44. runbooks/common/enhanced_logging_example.py +50 -64
  45. runbooks/common/enhanced_logging_integration_example.py +65 -37
  46. runbooks/common/env_utils.py +16 -16
  47. runbooks/common/error_handling.py +40 -38
  48. runbooks/common/lazy_loader.py +41 -23
  49. runbooks/common/logging_integration_helper.py +79 -86
  50. runbooks/common/mcp_cost_explorer_integration.py +476 -493
  51. runbooks/common/mcp_integration.py +99 -79
  52. runbooks/common/memory_optimization.py +140 -118
  53. runbooks/common/module_cli_base.py +37 -58
  54. runbooks/common/organizations_client.py +175 -193
  55. runbooks/common/patterns.py +23 -25
  56. runbooks/common/performance_monitoring.py +67 -71
  57. runbooks/common/performance_optimization_engine.py +283 -274
  58. runbooks/common/profile_utils.py +111 -37
  59. runbooks/common/rich_utils.py +315 -141
  60. runbooks/common/sre_performance_suite.py +177 -186
  61. runbooks/enterprise/__init__.py +1 -1
  62. runbooks/enterprise/logging.py +144 -106
  63. runbooks/enterprise/security.py +187 -204
  64. runbooks/enterprise/validation.py +43 -56
  65. runbooks/finops/__init__.py +26 -30
  66. runbooks/finops/account_resolver.py +1 -1
  67. runbooks/finops/advanced_optimization_engine.py +980 -0
  68. runbooks/finops/automation_core.py +268 -231
  69. runbooks/finops/business_case_config.py +184 -179
  70. runbooks/finops/cli.py +660 -139
  71. runbooks/finops/commvault_ec2_analysis.py +157 -164
  72. runbooks/finops/compute_cost_optimizer.py +336 -320
  73. runbooks/finops/config.py +20 -20
  74. runbooks/finops/cost_optimizer.py +484 -618
  75. runbooks/finops/cost_processor.py +332 -214
  76. runbooks/finops/dashboard_runner.py +1006 -172
  77. runbooks/finops/ebs_cost_optimizer.py +991 -657
  78. runbooks/finops/elastic_ip_optimizer.py +317 -257
  79. runbooks/finops/enhanced_mcp_integration.py +340 -0
  80. runbooks/finops/enhanced_progress.py +32 -29
  81. runbooks/finops/enhanced_trend_visualization.py +3 -2
  82. runbooks/finops/enterprise_wrappers.py +223 -285
  83. runbooks/finops/executive_export.py +203 -160
  84. runbooks/finops/helpers.py +130 -288
  85. runbooks/finops/iam_guidance.py +1 -1
  86. runbooks/finops/infrastructure/__init__.py +80 -0
  87. runbooks/finops/infrastructure/commands.py +506 -0
  88. runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
  89. runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
  90. runbooks/finops/markdown_exporter.py +337 -174
  91. runbooks/finops/mcp_validator.py +1952 -0
  92. runbooks/finops/nat_gateway_optimizer.py +1512 -481
  93. runbooks/finops/network_cost_optimizer.py +657 -587
  94. runbooks/finops/notebook_utils.py +226 -188
  95. runbooks/finops/optimization_engine.py +1136 -0
  96. runbooks/finops/optimizer.py +19 -23
  97. runbooks/finops/rds_snapshot_optimizer.py +367 -411
  98. runbooks/finops/reservation_optimizer.py +427 -363
  99. runbooks/finops/scenario_cli_integration.py +64 -65
  100. runbooks/finops/scenarios.py +1277 -438
  101. runbooks/finops/schemas.py +218 -182
  102. runbooks/finops/snapshot_manager.py +2289 -0
  103. runbooks/finops/types.py +3 -3
  104. runbooks/finops/validation_framework.py +259 -265
  105. runbooks/finops/vpc_cleanup_exporter.py +189 -144
  106. runbooks/finops/vpc_cleanup_optimizer.py +591 -573
  107. runbooks/finops/workspaces_analyzer.py +171 -182
  108. runbooks/integration/__init__.py +89 -0
  109. runbooks/integration/mcp_integration.py +1920 -0
  110. runbooks/inventory/CLAUDE.md +816 -0
  111. runbooks/inventory/__init__.py +2 -2
  112. runbooks/inventory/aws_decorators.py +2 -3
  113. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  114. runbooks/inventory/check_controltower_readiness.py +152 -151
  115. runbooks/inventory/check_landingzone_readiness.py +85 -84
  116. runbooks/inventory/cloud_foundations_integration.py +144 -149
  117. runbooks/inventory/collectors/aws_comprehensive.py +1 -1
  118. runbooks/inventory/collectors/aws_networking.py +109 -99
  119. runbooks/inventory/collectors/base.py +4 -0
  120. runbooks/inventory/core/collector.py +495 -313
  121. runbooks/inventory/core/formatter.py +11 -0
  122. runbooks/inventory/draw_org_structure.py +8 -9
  123. runbooks/inventory/drift_detection_cli.py +69 -96
  124. runbooks/inventory/ec2_vpc_utils.py +2 -2
  125. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  126. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  127. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  128. runbooks/inventory/find_ec2_security_groups.py +48 -42
  129. runbooks/inventory/find_landingzone_versions.py +4 -6
  130. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  131. runbooks/inventory/inventory_mcp_cli.py +48 -46
  132. runbooks/inventory/inventory_modules.py +103 -91
  133. runbooks/inventory/list_cfn_stacks.py +9 -10
  134. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  135. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  136. runbooks/inventory/list_cfn_stacksets.py +8 -10
  137. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  138. runbooks/inventory/list_ds_directories.py +65 -53
  139. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  140. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  141. runbooks/inventory/list_ec2_instances.py +23 -28
  142. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  143. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  144. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  145. runbooks/inventory/list_guardduty_detectors.py +2 -4
  146. runbooks/inventory/list_iam_policies.py +2 -4
  147. runbooks/inventory/list_iam_roles.py +5 -7
  148. runbooks/inventory/list_iam_saml_providers.py +4 -6
  149. runbooks/inventory/list_lambda_functions.py +38 -38
  150. runbooks/inventory/list_org_accounts.py +6 -8
  151. runbooks/inventory/list_org_accounts_users.py +55 -44
  152. runbooks/inventory/list_rds_db_instances.py +31 -33
  153. runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
  154. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  155. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  156. runbooks/inventory/list_sns_topics.py +2 -4
  157. runbooks/inventory/list_ssm_parameters.py +4 -7
  158. runbooks/inventory/list_vpc_subnets.py +2 -4
  159. runbooks/inventory/list_vpcs.py +7 -10
  160. runbooks/inventory/mcp_inventory_validator.py +554 -468
  161. runbooks/inventory/mcp_vpc_validator.py +359 -442
  162. runbooks/inventory/organizations_discovery.py +63 -55
  163. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  164. runbooks/inventory/requirements.txt +0 -1
  165. runbooks/inventory/rich_inventory_display.py +35 -34
  166. runbooks/inventory/run_on_multi_accounts.py +3 -5
  167. runbooks/inventory/unified_validation_engine.py +281 -253
  168. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  169. runbooks/inventory/vpc_analyzer.py +735 -697
  170. runbooks/inventory/vpc_architecture_validator.py +293 -348
  171. runbooks/inventory/vpc_dependency_analyzer.py +384 -380
  172. runbooks/inventory/vpc_flow_analyzer.py +1 -1
  173. runbooks/main.py +49 -34
  174. runbooks/main_final.py +91 -60
  175. runbooks/main_minimal.py +22 -10
  176. runbooks/main_optimized.py +131 -100
  177. runbooks/main_ultra_minimal.py +7 -2
  178. runbooks/mcp/__init__.py +36 -0
  179. runbooks/mcp/integration.py +679 -0
  180. runbooks/monitoring/performance_monitor.py +9 -4
  181. runbooks/operate/dynamodb_operations.py +3 -1
  182. runbooks/operate/ec2_operations.py +145 -137
  183. runbooks/operate/iam_operations.py +146 -152
  184. runbooks/operate/networking_cost_heatmap.py +29 -8
  185. runbooks/operate/rds_operations.py +223 -254
  186. runbooks/operate/s3_operations.py +107 -118
  187. runbooks/operate/vpc_operations.py +646 -616
  188. runbooks/remediation/base.py +1 -1
  189. runbooks/remediation/commons.py +10 -7
  190. runbooks/remediation/commvault_ec2_analysis.py +70 -66
  191. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
  192. runbooks/remediation/multi_account.py +24 -21
  193. runbooks/remediation/rds_snapshot_list.py +86 -60
  194. runbooks/remediation/remediation_cli.py +92 -146
  195. runbooks/remediation/universal_account_discovery.py +83 -79
  196. runbooks/remediation/workspaces_list.py +46 -41
  197. runbooks/security/__init__.py +19 -0
  198. runbooks/security/assessment_runner.py +1150 -0
  199. runbooks/security/baseline_checker.py +812 -0
  200. runbooks/security/cloudops_automation_security_validator.py +509 -535
  201. runbooks/security/compliance_automation_engine.py +17 -17
  202. runbooks/security/config/__init__.py +2 -2
  203. runbooks/security/config/compliance_config.py +50 -50
  204. runbooks/security/config_template_generator.py +63 -76
  205. runbooks/security/enterprise_security_framework.py +1 -1
  206. runbooks/security/executive_security_dashboard.py +519 -508
  207. runbooks/security/multi_account_security_controls.py +959 -1210
  208. runbooks/security/real_time_security_monitor.py +422 -444
  209. runbooks/security/security_baseline_tester.py +1 -1
  210. runbooks/security/security_cli.py +143 -112
  211. runbooks/security/test_2way_validation.py +439 -0
  212. runbooks/security/two_way_validation_framework.py +852 -0
  213. runbooks/sre/production_monitoring_framework.py +167 -177
  214. runbooks/tdd/__init__.py +15 -0
  215. runbooks/tdd/cli.py +1071 -0
  216. runbooks/utils/__init__.py +14 -17
  217. runbooks/utils/logger.py +7 -2
  218. runbooks/utils/version_validator.py +50 -47
  219. runbooks/validation/__init__.py +6 -6
  220. runbooks/validation/cli.py +9 -3
  221. runbooks/validation/comprehensive_2way_validator.py +745 -704
  222. runbooks/validation/mcp_validator.py +906 -228
  223. runbooks/validation/terraform_citations_validator.py +104 -115
  224. runbooks/validation/terraform_drift_detector.py +461 -454
  225. runbooks/vpc/README.md +617 -0
  226. runbooks/vpc/__init__.py +8 -1
  227. runbooks/vpc/analyzer.py +577 -0
  228. runbooks/vpc/cleanup_wrapper.py +476 -413
  229. runbooks/vpc/cli_cloudtrail_commands.py +339 -0
  230. runbooks/vpc/cli_mcp_validation_commands.py +480 -0
  231. runbooks/vpc/cloudtrail_audit_integration.py +717 -0
  232. runbooks/vpc/config.py +92 -97
  233. runbooks/vpc/cost_engine.py +411 -148
  234. runbooks/vpc/cost_explorer_integration.py +553 -0
  235. runbooks/vpc/cross_account_session.py +101 -106
  236. runbooks/vpc/enhanced_mcp_validation.py +917 -0
  237. runbooks/vpc/eni_gate_validator.py +961 -0
  238. runbooks/vpc/heatmap_engine.py +185 -160
  239. runbooks/vpc/mcp_no_eni_validator.py +680 -639
  240. runbooks/vpc/nat_gateway_optimizer.py +358 -0
  241. runbooks/vpc/networking_wrapper.py +15 -8
  242. runbooks/vpc/pdca_remediation_planner.py +528 -0
  243. runbooks/vpc/performance_optimized_analyzer.py +219 -231
  244. runbooks/vpc/runbooks_adapter.py +1167 -241
  245. runbooks/vpc/tdd_red_phase_stubs.py +601 -0
  246. runbooks/vpc/test_data_loader.py +358 -0
  247. runbooks/vpc/tests/conftest.py +314 -4
  248. runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
  249. runbooks/vpc/tests/test_cost_engine.py +0 -2
  250. runbooks/vpc/topology_generator.py +326 -0
  251. runbooks/vpc/unified_scenarios.py +1297 -1124
  252. runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
  253. runbooks-1.1.6.dist-info/METADATA +327 -0
  254. runbooks-1.1.6.dist-info/RECORD +489 -0
  255. runbooks/finops/README.md +0 -414
  256. runbooks/finops/accuracy_cross_validator.py +0 -647
  257. runbooks/finops/business_cases.py +0 -950
  258. runbooks/finops/dashboard_router.py +0 -922
  259. runbooks/finops/ebs_optimizer.py +0 -973
  260. runbooks/finops/embedded_mcp_validator.py +0 -1629
  261. runbooks/finops/enhanced_dashboard_runner.py +0 -527
  262. runbooks/finops/finops_dashboard.py +0 -584
  263. runbooks/finops/finops_scenarios.py +0 -1218
  264. runbooks/finops/legacy_migration.py +0 -730
  265. runbooks/finops/multi_dashboard.py +0 -1519
  266. runbooks/finops/single_dashboard.py +0 -1113
  267. runbooks/finops/unlimited_scenarios.py +0 -393
  268. runbooks-1.1.4.dist-info/METADATA +0 -800
  269. runbooks-1.1.4.dist-info/RECORD +0 -468
  270. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  271. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  272. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  273. {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -24,47 +24,53 @@ from typing import Dict, List, Optional, Set, Tuple
24
24
 
25
25
  import boto3
26
26
  from botocore.exceptions import ClientError, NoCredentialsError
27
- from rich.console import Console
28
27
  from rich.panel import Panel
29
- from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
28
+ from rich.progress import BarColumn, SpinnerColumn, TextColumn, TimeElapsedColumn
30
29
  from rich.status import Status
31
30
  from rich.table import Table
32
31
 
33
- # Initialize Rich console
34
- console = Console()
32
+ # Test Mode Support: Use centralized rich_utils for console and Progress (test-aware)
33
+ # Issue: Rich Console/Progress write to StringIO buffer that Click CliRunner closes, causing ValueError
34
+ # Solution: rich_utils provides MockConsole/MockProgress in test mode, Rich components in production
35
+ import os
36
+ import sys
37
+ import re
35
38
 
36
39
  from ..utils.logger import configure_logger
37
40
  from ..common.performance_optimization_engine import get_optimization_engine
41
+ from ..common.rich_utils import console, Progress
38
42
 
39
43
  logger = configure_logger(__name__)
40
44
 
41
45
  # Global Organizations cache to prevent duplicate API calls across all instances
42
- _GLOBAL_ORGS_CACHE = {
43
- 'data': None,
44
- 'timestamp': None,
45
- 'ttl_minutes': 30
46
- }
46
+ _GLOBAL_ORGS_CACHE = {"data": None, "timestamp": None, "ttl_minutes": 30}
47
+
47
48
 
48
49
  def _get_global_organizations_cache():
49
50
  """Get cached Organizations data if valid (module-level cache)."""
50
- if not _GLOBAL_ORGS_CACHE['timestamp']:
51
+ if not _GLOBAL_ORGS_CACHE["timestamp"]:
51
52
  return None
52
-
53
- cache_age_minutes = (datetime.now(timezone.utc) - _GLOBAL_ORGS_CACHE['timestamp']).total_seconds() / 60
54
- if cache_age_minutes < _GLOBAL_ORGS_CACHE['ttl_minutes']:
53
+
54
+ cache_age_minutes = (datetime.now(timezone.utc) - _GLOBAL_ORGS_CACHE["timestamp"]).total_seconds() / 60
55
+ if cache_age_minutes < _GLOBAL_ORGS_CACHE["ttl_minutes"]:
55
56
  console.print("[blue]🚀 Global performance optimization: Using cached Organizations data[/blue]")
56
- return _GLOBAL_ORGS_CACHE['data']
57
+ return _GLOBAL_ORGS_CACHE["data"]
57
58
  return None
58
59
 
60
+
59
61
  def _set_global_organizations_cache(data):
60
62
  """Cache Organizations data globally (module-level cache)."""
61
- _GLOBAL_ORGS_CACHE['data'] = data
62
- _GLOBAL_ORGS_CACHE['timestamp'] = datetime.now(timezone.utc)
63
- accounts_count = len(data.get('accounts', {}).get('discovered_accounts', [])) if data else 0
64
- console.print(f"[green]✅ Global Organizations cache: {accounts_count} accounts (TTL: {_GLOBAL_ORGS_CACHE['ttl_minutes']}min)[/green]")
63
+ _GLOBAL_ORGS_CACHE["data"] = data
64
+ _GLOBAL_ORGS_CACHE["timestamp"] = datetime.now(timezone.utc)
65
+ accounts_count = len(data.get("accounts", {}).get("discovered_accounts", [])) if data else 0
66
+ console.print(
67
+ f"[green]✅ Global Organizations cache: {accounts_count} accounts (TTL: {_GLOBAL_ORGS_CACHE['ttl_minutes']}min)[/green]"
68
+ )
69
+
65
70
 
66
71
  # Universal AWS Environment Profile Support (Compatible with ANY AWS Setup)
67
72
  import os
73
+
68
74
  ENTERPRISE_PROFILES = {
69
75
  "BILLING_PROFILE": os.getenv("BILLING_PROFILE", "default"), # Universal compatibility
70
76
  "MANAGEMENT_PROFILE": os.getenv("MANAGEMENT_PROFILE", "default"), # Works with any profile
@@ -246,40 +252,44 @@ class EnhancedOrganizationsDiscovery:
246
252
  """Check if Organizations cache is still valid."""
247
253
  if not self._organizations_cache_timestamp:
248
254
  return False
249
-
255
+
250
256
  from datetime import datetime, timedelta
257
+
251
258
  cache_age_minutes = (datetime.now() - self._organizations_cache_timestamp).total_seconds() / 60
252
259
  return cache_age_minutes < self._cache_ttl_minutes
253
260
 
254
261
  async def discover_all_accounts(self) -> Dict:
255
262
  """
256
263
  Cached wrapper for Organizations discovery to prevent duplicate API calls.
257
-
258
- This method implements both global and instance-level caching to avoid the
259
- performance penalty of duplicate Organizations API calls when multiple
264
+
265
+ This method implements both global and instance-level caching to avoid the
266
+ performance penalty of duplicate Organizations API calls when multiple
260
267
  components need the same account data.
261
268
  """
262
269
  # Check global cache first (shared across all instances)
263
270
  global_cached_result = _get_global_organizations_cache()
264
271
  if global_cached_result:
265
272
  return global_cached_result
266
-
273
+
267
274
  # Check instance cache
268
275
  if self._is_organizations_cache_valid() and self._organizations_cache:
269
276
  console.print("[blue]🚀 Performance optimization: Using cached Organizations data[/blue]")
270
277
  return self._organizations_cache
271
-
278
+
272
279
  # Cache miss - perform discovery
273
280
  console.print("[cyan]🔍 Performing Organizations discovery (cache miss)[/cyan]")
274
281
  results = await self.discover_organization_structure()
275
-
282
+
276
283
  # Cache the results
277
- if results and results.get('accounts'):
284
+ if results and results.get("accounts"):
278
285
  self._organizations_cache = results
279
286
  from datetime import datetime
287
+
280
288
  self._organizations_cache_timestamp = datetime.now()
281
- console.print(f"[green]✅ Cached Organizations data: {len(results.get('accounts', {}).get('discovered_accounts', []))} accounts (TTL: {self._cache_ttl_minutes}min)[/green]")
282
-
289
+ console.print(
290
+ f"[green]✅ Cached Organizations data: {len(results.get('accounts', {}).get('discovered_accounts', []))} accounts (TTL: {self._cache_ttl_minutes}min)[/green]"
291
+ )
292
+
283
293
  return results
284
294
 
285
295
  def initialize_sessions(self) -> Dict[str, str]:
@@ -417,13 +427,13 @@ class EnhancedOrganizationsDiscovery:
417
427
  """
418
428
  # Get performance optimization engine
419
429
  optimization_engine = get_optimization_engine(
420
- max_workers=self.max_workers,
421
- cache_ttl_minutes=30,
422
- memory_limit_mb=2048
430
+ max_workers=self.max_workers, cache_ttl_minutes=30, memory_limit_mb=2048
423
431
  )
424
-
432
+
425
433
  # Use optimized discovery with performance monitoring
426
- with optimization_engine.optimize_operation("organization_structure_discovery", self.performance_target_seconds):
434
+ with optimization_engine.optimize_operation(
435
+ "organization_structure_discovery", self.performance_target_seconds
436
+ ):
427
437
  return await self._discover_organization_structure_optimized(optimization_engine)
428
438
 
429
439
  async def _discover_organization_structure_optimized(self, optimization_engine) -> Dict:
@@ -436,7 +446,7 @@ class EnhancedOrganizationsDiscovery:
436
446
  )
437
447
 
438
448
  logger.info("🏢 Starting optimized organization structure discovery with SRE automation patterns")
439
-
449
+
440
450
  # Check global cache first to prevent duplicate calls
441
451
  cached_result = _get_global_organizations_cache()
442
452
  if cached_result:
@@ -446,7 +456,7 @@ class EnhancedOrganizationsDiscovery:
446
456
  self.discovery_metrics["end_time"] = self.current_benchmark.end_time
447
457
  self.discovery_metrics["duration_seconds"] = self.current_benchmark.duration_seconds
448
458
  return cached_result
449
-
459
+
450
460
  self.discovery_metrics["start_time"] = self.current_benchmark.start_time
451
461
 
452
462
  with Status("Initializing enterprise discovery...", console=console, spinner="dots"):
@@ -561,10 +571,10 @@ class EnhancedOrganizationsDiscovery:
561
571
  "performance_benchmark": performance_benchmark_dict,
562
572
  "timestamp": datetime.now().isoformat(),
563
573
  }
564
-
574
+
565
575
  # Cache the successful result to prevent duplicate calls
566
576
  _set_global_organizations_cache(discovery_result)
567
-
577
+
568
578
  return discovery_result
569
579
 
570
580
  except Exception as e:
@@ -606,7 +616,7 @@ class EnhancedOrganizationsDiscovery:
606
616
  async def _discover_accounts_optimized(self, optimization_engine) -> Dict:
607
617
  """
608
618
  Optimized account discovery using performance optimization engine
609
-
619
+
610
620
  Addresses: Organization Discovery Performance (52.3s -> <30s target)
611
621
  Features:
612
622
  - Intelligent caching with TTL management
@@ -615,46 +625,44 @@ class EnhancedOrganizationsDiscovery:
615
625
  - Memory-efficient processing
616
626
  """
617
627
  logger.info("📊 Discovering organization accounts with SRE optimization patterns")
618
-
628
+
619
629
  # Use optimization engine for discovery
620
630
  optimized_discover_accounts = optimization_engine.optimize_organization_discovery(
621
- management_profile=self.management_profile,
622
- use_parallel_processing=True,
623
- batch_size=20
631
+ management_profile=self.management_profile, use_parallel_processing=True, batch_size=20
624
632
  )
625
-
633
+
626
634
  # Execute optimized discovery
627
635
  try:
628
636
  result = optimized_discover_accounts()
629
-
637
+
630
638
  # Convert to expected format
631
- accounts_data = result.get('accounts', [])
632
-
639
+ accounts_data = result.get("accounts", [])
640
+
633
641
  # Create AWSAccount objects for compatibility
634
642
  for account_data in accounts_data:
635
643
  account = AWSAccount(
636
644
  account_id=account_data["Id"],
637
- name=account_data["Name"],
645
+ name=account_data["Name"],
638
646
  email=account_data["Email"],
639
647
  status=account_data["Status"],
640
648
  joined_method=account_data["JoinedMethod"],
641
649
  joined_timestamp=account_data.get("JoinedTimestamp"),
642
- tags=account_data.get("Tags", {})
650
+ tags=account_data.get("Tags", {}),
643
651
  )
644
652
  self.accounts_cache[account.account_id] = account
645
-
653
+
646
654
  # Update metrics
647
655
  self.discovery_metrics["accounts_discovered"] = len(accounts_data)
648
-
656
+
649
657
  # Enhanced account categorization
650
658
  active_accounts = [a for a in accounts_data if a.get("Status") == "ACTIVE"]
651
- suspended_accounts = [a for a in accounts_data if a.get("Status") == "SUSPENDED"]
659
+ suspended_accounts = [a for a in accounts_data if a.get("Status") == "SUSPENDED"]
652
660
  closed_accounts = [a for a in accounts_data if a.get("Status") == "CLOSED"]
653
-
654
- optimization_info = result.get('optimizations_applied', [])
661
+
662
+ optimization_info = result.get("optimizations_applied", [])
655
663
  logger.info(f"✅ Optimized discovery: {len(accounts_data)} accounts ({len(active_accounts)} active)")
656
664
  logger.info(f"🚀 Optimizations applied: {', '.join(optimization_info)}")
657
-
665
+
658
666
  return {
659
667
  "total_accounts": len(accounts_data),
660
668
  "active_accounts": len(active_accounts),
@@ -665,7 +673,7 @@ class EnhancedOrganizationsDiscovery:
665
673
  "profile_used": "management",
666
674
  "optimizations_applied": optimization_info,
667
675
  }
668
-
676
+
669
677
  except Exception as e:
670
678
  logger.error(f"Optimized account discovery failed: {e}")
671
679
  # Fallback to original method
@@ -10,7 +10,7 @@ import Inventory_Modules
10
10
  import simplejson as json
11
11
  from account_class import aws_acct_access
12
12
  from ArgumentsClass import CommonArguments
13
- from colorama import Fore, init
13
+ from runbooks.common.rich_utils import console
14
14
  from Inventory_Modules import get_credentials_for_accounts_in_org
15
15
 
16
16
  """
@@ -18,7 +18,6 @@ This script was created to help solve a testing problem for the "move_stack_inst
18
18
  Originally, that script didn't have built-in recovery, so we needed this script to "recover" those stack-instance ids that might have been lost during the move_stack_instances.py run. However, that script now has built-in recovery, so this script isn't really needed. However, it can still be used to find any stack-instances that have been orphaned from their original stack-set, if that happens.
19
19
  """
20
20
 
21
- init()
22
21
  __version__ = "2024.05.18"
23
22
 
24
23
 
@@ -70,9 +69,9 @@ def setup_auth_and_regions(fProfile: str = None) -> (object, list, list):
70
69
  ChildAccounts = Inventory_Modules.RemoveCoreAccounts(ChildAccounts, pAccountsToSkip)
71
70
  AccountList = [account["AccountId"] for account in ChildAccounts]
72
71
 
73
- print(f"You asked to find stacks with this fragment list: {Fore.RED}{pFragments}{Fore.RESET}")
74
- print(f"\t\tin these accounts: {Fore.RED}{AccountList}{Fore.RESET}")
75
- print(f"\t\tin these regions: {Fore.RED}{RegionList}{Fore.RESET}")
72
+ print(f"You asked to find stacks with this fragment list: [red]{pFragments}")
73
+ print(f"\t\tin these accounts: [red]{AccountList}")
74
+ print(f"\t\tin these regions: [red]{RegionList}")
76
75
  return aws_acct, AccountList, RegionList
77
76
 
78
77
 
@@ -129,7 +128,7 @@ if __name__ == "__main__":
129
128
  Stacks = Inventory_Modules.find_stacks2(cred, region, pFragments)
130
129
  logging.warning(f"Account: {cred['AccountId']} | Region: {region} | Found {len(Stacks)} Stacks")
131
130
  print(
132
- f"{ERASE_LINE}{Fore.RED}Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks{Fore.RESET} ({item_counter} of {len(AccountList) * len(RegionList)})",
131
+ f"{ERASE_LINE}[red]Account: {cred['AccountId']} Region: {region} Found {len(Stacks)} Stacks ({item_counter} of {len(AccountList) * len(RegionList)})",
133
132
  end="\r",
134
133
  )
135
134
  except Exception as my_Error:
@@ -162,7 +161,7 @@ if __name__ == "__main__":
162
161
  lAccountsAndRegions.append((StacksFound[i]["Account"], StacksFound[i]["Region"]))
163
162
  print(ERASE_LINE)
164
163
  print(
165
- f"{Fore.RED}Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions{Fore.RESET}"
164
+ f"[red]Looked through {len(StacksFound)} Stacks across {len(AccountList)} accounts across {len(RegionList)} regions"
166
165
  )
167
166
  print()
168
167
  if args.loglevel < 21: # INFO level
@@ -198,7 +197,7 @@ if __name__ == "__main__":
198
197
 
199
198
  if pTiming:
200
199
  print(ERASE_LINE)
201
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
200
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
202
201
 
203
202
  print()
204
203
  print("Thanks for using this script...")
@@ -1,7 +1,6 @@
1
1
  boto3>=1.26.84
2
2
  botocore>=1.29.84
3
3
  colorama>=0.4.6
4
- prettytable>=3.6.0
5
4
  simplejson>=3.18.3
6
5
  urllib3>=1.26.14
7
6
  randominfo>=2.0.2
@@ -21,9 +21,9 @@ from datetime import datetime
21
21
  from typing import Any, Dict, List, Optional
22
22
 
23
23
  from rich import box
24
- from rich.console import Console
25
24
  from rich.panel import Panel
26
- from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn
25
+ from rich.progress import BarColumn, SpinnerColumn, TaskProgressColumn, TextColumn
26
+ from runbooks.common.rich_utils import Progress
27
27
  from rich.table import Table
28
28
  from rich.text import Text
29
29
  from rich.tree import Tree
@@ -349,18 +349,18 @@ def display_account_tree(accounts_data: Dict[str, Dict]) -> None:
349
349
 
350
350
 
351
351
  def display_results_rich(
352
- results_list: List[Dict[str, Any]],
353
- fdisplay_dict: Dict[str, Dict],
354
- defaultAction: Any = None,
355
- file_to_save: Optional[str] = None,
352
+ results_list: List[Dict[str, Any]],
353
+ fdisplay_dict: Dict[str, Dict],
354
+ defaultAction: Any = None,
355
+ file_to_save: Optional[str] = None,
356
356
  subdisplay: bool = False,
357
- title: str = "Inventory Results"
357
+ title: str = "Inventory Results",
358
358
  ) -> None:
359
359
  """
360
360
  Rich CLI replacement for legacy display_results function.
361
-
361
+
362
362
  Provides backwards-compatible interface while using Rich formatting.
363
-
363
+
364
364
  Args:
365
365
  results_list: List of dictionaries with resource data
366
366
  fdisplay_dict: Display configuration dictionary with format:
@@ -369,7 +369,7 @@ def display_results_rich(
369
369
  file_to_save: Optional filename to save results
370
370
  subdisplay: Whether this is a sub-display (affects formatting)
371
371
  title: Title for the table display
372
-
372
+
373
373
  Example:
374
374
  display_dict = {
375
375
  'AccountId': {'DisplayOrder': 1, 'Heading': 'Account'},
@@ -379,59 +379,60 @@ def display_results_rich(
379
379
  display_results_rich(instance_data, display_dict, title="EC2 Instances")
380
380
  """
381
381
  from datetime import datetime
382
-
382
+
383
383
  if not results_list:
384
384
  print_info("ℹ️ No results to display")
385
385
  return
386
-
386
+
387
387
  # Sort display fields by DisplayOrder
388
- sorted_fields = sorted(fdisplay_dict.items(), key=lambda x: x[1].get('DisplayOrder', 999))
389
-
388
+ sorted_fields = sorted(fdisplay_dict.items(), key=lambda x: x[1].get("DisplayOrder", 999))
389
+
390
390
  # Create Rich table
391
391
  table = create_table(
392
392
  title=f"📊 {title}",
393
- caption=f"Found {len(results_list)} results • {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
393
+ caption=f"Found {len(results_list)} results • {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
394
394
  )
395
-
395
+
396
396
  # Add columns based on display dictionary
397
397
  for field_name, field_config in sorted_fields:
398
- heading = field_config.get('Heading', field_name)
398
+ heading = field_config.get("Heading", field_name)
399
399
  table.add_column(heading, style="cyan" if "Id" in field_name else "white", no_wrap=True)
400
-
400
+
401
401
  # Add rows
402
402
  for result in results_list[:100]: # Limit to first 100 for performance
403
403
  row_data = []
404
-
404
+
405
405
  for field_name, field_config in sorted_fields:
406
406
  # Apply condition filter if specified
407
- condition = field_config.get('Condition', [])
407
+ condition = field_config.get("Condition", [])
408
408
  if condition:
409
409
  value = result.get(field_name, defaultAction)
410
410
  if value not in condition:
411
411
  continue
412
-
412
+
413
413
  # Get field value with default fallback
414
414
  value = result.get(field_name, defaultAction)
415
415
  if value is None:
416
416
  value = "N/A"
417
-
417
+
418
418
  # Format value as string
419
419
  row_data.append(str(value)[:50]) # Truncate long values
420
-
420
+
421
421
  table.add_row(*row_data)
422
-
422
+
423
423
  # Display the table
424
424
  console.print(table)
425
-
425
+
426
426
  # Show truncation notice if needed
427
427
  if len(results_list) > 100:
428
428
  console.print(f"[dim]Showing first 100 results. Total found: {len(results_list)}[/dim]")
429
-
429
+
430
430
  # Save to file if requested
431
431
  if file_to_save:
432
432
  try:
433
433
  import json
434
- with open(file_to_save, 'w') as f:
434
+
435
+ with open(file_to_save, "w") as f:
435
436
  json.dump(results_list, f, indent=2, default=str)
436
437
  print_success(f"💾 Results saved to: {file_to_save}")
437
438
  except Exception as e:
@@ -441,7 +442,7 @@ def display_results_rich(
441
442
  def display_progress_rich(current: int, total: int, description: str = "Processing") -> None:
442
443
  """
443
444
  Rich CLI replacement for legacy progress indicators.
444
-
445
+
445
446
  Args:
446
447
  current: Current progress count
447
448
  total: Total items to process
@@ -454,21 +455,21 @@ def display_progress_rich(current: int, total: int, description: str = "Processi
454
455
  def print_colorized_rich(text: str, color: str = "white") -> None:
455
456
  """
456
457
  Rich CLI replacement for colorama print statements.
457
-
458
+
458
459
  Args:
459
460
  text: Text to print
460
461
  color: Color name (red, green, yellow, cyan, blue, white)
461
462
  """
462
463
  color_map = {
463
464
  "red": "red",
464
- "green": "green",
465
+ "green": "green",
465
466
  "yellow": "yellow",
466
467
  "cyan": "cyan",
467
468
  "blue": "blue",
468
469
  "white": "white",
469
- "magenta": "magenta"
470
+ "magenta": "magenta",
470
471
  }
471
-
472
+
472
473
  rich_color = color_map.get(color.lower(), "white")
473
474
  console.print(f"[{rich_color}]{text}[/{rich_color}]")
474
475
 
@@ -476,13 +477,13 @@ def print_colorized_rich(text: str, color: str = "white") -> None:
476
477
  # Export public functions
477
478
  __all__ = [
478
479
  "display_inventory_header",
479
- "create_inventory_progress",
480
+ "create_inventory_progress",
480
481
  "display_ec2_inventory_results",
481
482
  "display_generic_inventory_results",
482
483
  "display_inventory_error",
483
484
  "display_multi_resource_summary",
484
485
  "display_account_tree",
485
486
  "display_results_rich",
486
- "display_progress_rich",
487
+ "display_progress_rich",
487
488
  "print_colorized_rich",
488
489
  ]
@@ -7,14 +7,12 @@ import Inventory_Modules
7
7
  from account_class import aws_acct_access
8
8
  from ArgumentsClass import CommonArguments
9
9
  from botocore.exceptions import ClientError
10
- from colorama import Fore, init
11
- from rich.console import Console
10
+ from runbooks.common.rich_utils import console
12
11
  from rich.panel import Panel
13
12
 
14
- # Initialize Rich console
15
- console = Console()
13
+ # Initialize Rich console with test mode support
14
+ from runbooks.common.rich_utils import console
16
15
 
17
- init()
18
16
  __version__ = "2023.05.04"
19
17
 
20
18
  parser = CommonArguments()