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
@@ -203,6 +203,17 @@ class InventoryFormatter(CloudFoundationsFormatter):
203
203
 
204
204
  def format_console_table(self) -> str:
205
205
  """Format inventory data for console display."""
206
+ import os
207
+ import sys
208
+ import re
209
+
210
+ # Test Mode Support: Disable Rich Console in test environments to prevent I/O conflicts
211
+ USE_RICH = os.getenv("RUNBOOKS_TEST_MODE") != "1"
212
+
213
+ if not USE_RICH:
214
+ # Test mode: Use simple text formatting to prevent Click CliRunner I/O conflicts
215
+ return self._format_simple_text_table()
216
+
206
217
  try:
207
218
  from rich.console import Console
208
219
  from rich.table import Table
@@ -58,7 +58,7 @@ from typing import Any, Dict, List, Optional
58
58
 
59
59
  import boto3
60
60
  from ArgumentsClass import CommonArguments
61
- from colorama import Fore, init
61
+ from runbooks.common.rich_utils import console
62
62
  from graphviz import Digraph
63
63
 
64
64
  # Optional imports for enhanced features
@@ -74,7 +74,6 @@ except ImportError:
74
74
 
75
75
  __version__ = "2025.04.09"
76
76
 
77
- init()
78
77
 
79
78
  # Visual styling constants
80
79
  account_fillcolor = "orange"
@@ -379,7 +378,7 @@ def generate_mermaid(org_structure: Any, filename: str) -> None:
379
378
  try:
380
379
  with open(filename, "w", encoding="utf-8") as f:
381
380
  f.write("\n".join(lines))
382
- print(f"Mermaid diagram successfully saved to '{Fore.RED}{filename}{Fore.RESET}'")
381
+ print(f"Mermaid diagram successfully saved to '[red]{filename}'")
383
382
  logging.info(f"Mermaid diagram successfully saved to {filename}")
384
383
  except Exception as e:
385
384
  logging.error(f"Failed to write Mermaid diagram to {filename}: {e}")
@@ -412,7 +411,7 @@ def generate_diagrams(org_structure: Any, filename: str) -> None:
412
411
  )
413
412
  except ImportError as imp_err:
414
413
  logging.error("Please install the 'diagrams' package to use generate_diagrams: pip install diagrams")
415
- print(f"{Fore.YELLOW}Warning: diagrams library not available. Install with: pip install diagrams{Fore.RESET}")
414
+ print(f"[yellow]Warning: diagrams library not available. Install with: pip install diagrams")
416
415
  return
417
416
 
418
417
  def build_diagram(node: Dict[str, Any]):
@@ -434,7 +433,7 @@ def generate_diagrams(org_structure: Any, filename: str) -> None:
434
433
  try:
435
434
  with Diagram("AWS Organization Diagram", filename=filename, show=False, direction="LR"):
436
435
  build_diagram(org_structure)
437
- print(f"Diagrams image successfully generated as '{Fore.RED}{filename}{Fore.RESET}'")
436
+ print(f"Diagrams image successfully generated as '[red]{filename}'")
438
437
  logging.info(f"Diagrams image successfully generated as {filename}")
439
438
  except Exception as e:
440
439
  logging.error(f"Failed to generate diagrams image: {e}")
@@ -636,7 +635,7 @@ def draw_org(froot: str, filename: str):
636
635
 
637
636
  # Save the diagram to a PNG file
638
637
  dot_unflat.render(filename, format="png", view=False)
639
- print(f"Diagram saved to '{Fore.RED}{filename}.png{Fore.RESET}'")
638
+ print(f"Diagram saved to '[red]{filename}.png'")
640
639
 
641
640
 
642
641
  #####################
@@ -708,7 +707,7 @@ if __name__ == "__main__":
708
707
  if anticipated_time > 30:
709
708
  print()
710
709
  print(
711
- f"{Fore.RED}Since this may take a while, you could re-run this script for only a specific OU by using the '--ou <OU ID>' parameter {Fore.RESET}"
710
+ f"[red]Since this may take a while, you could re-run this script for only a specific OU by using the '--ou <OU ID>' parameter "
712
711
  )
713
712
  print()
714
713
  else:
@@ -737,11 +736,11 @@ if __name__ == "__main__":
737
736
  # Display timing information
738
737
  if pTiming and pPolicy:
739
738
  print(
740
- f"{Fore.GREEN}Drawing the Org structure when policies are included took {time() - begin_time:.2f} seconds{Fore.RESET}"
739
+ f"[green]Drawing the Org structure when policies are included took {time() - begin_time:.2f} seconds"
741
740
  )
742
741
  elif pTiming:
743
742
  print(
744
- f"{Fore.GREEN}Drawing the Org structure without policies took {time() - begin_time:.2f} seconds{Fore.RESET}"
743
+ f"[green]Drawing the Org structure without policies took {time() - begin_time:.2f} seconds"
745
744
  )
746
745
 
747
746
  print("Thank you for using this script")
@@ -11,7 +11,7 @@ Strategic Alignment:
11
11
 
12
12
  Features:
13
13
  - Real-time AWS API cross-validation
14
- - Terraform state drift detection
14
+ - Terraform state drift detection
15
15
  - Rich CLI enterprise UX with visual indicators
16
16
  - Evidence-based drift reporting
17
17
  - Profile override priority system
@@ -34,7 +34,7 @@ from ..common.rich_utils import console, print_error, print_info, print_success
34
34
  from .mcp_inventory_validator import (
35
35
  create_inventory_mcp_validator,
36
36
  generate_drift_report,
37
- validate_inventory_results_with_mcp
37
+ validate_inventory_results_with_mcp,
38
38
  )
39
39
 
40
40
 
@@ -45,46 +45,32 @@ def drift():
45
45
 
46
46
 
47
47
  @drift.command("detect")
48
- @click.option(
49
- "--profile",
50
- help="AWS profile to use (overrides environment variables)"
51
- )
52
- @click.option(
53
- "--profiles",
54
- help="Comma-separated list of AWS profiles for multi-account analysis"
55
- )
48
+ @click.option("--profile", help="AWS profile to use (overrides environment variables)")
49
+ @click.option("--profiles", help="Comma-separated list of AWS profiles for multi-account analysis")
56
50
  @click.option(
57
51
  "--terraform-dir",
58
52
  default="/Volumes/Working/1xOps/CloudOps-Runbooks/terraform-aws",
59
- help="Path to terraform configuration directory"
53
+ help="Path to terraform configuration directory",
60
54
  )
61
55
  @click.option(
62
56
  "--report-format",
63
57
  type=click.Choice(["console", "json", "csv"]),
64
58
  default="console",
65
- help="Output format for drift report"
66
- )
67
- @click.option(
68
- "--output-file",
69
- help="File path to save drift report (optional)"
70
- )
71
- @click.option(
72
- "--threshold",
73
- type=float,
74
- default=99.5,
75
- help="Accuracy threshold for drift detection (default: 99.5%)"
59
+ help="Output format for drift report",
76
60
  )
61
+ @click.option("--output-file", help="File path to save drift report (optional)")
62
+ @click.option("--threshold", type=float, default=99.5, help="Accuracy threshold for drift detection (default: 99.5%)")
77
63
  def detect_drift(
78
64
  profile: Optional[str],
79
- profiles: Optional[str],
65
+ profiles: Optional[str],
80
66
  terraform_dir: str,
81
67
  report_format: str,
82
68
  output_file: Optional[str],
83
- threshold: float
69
+ threshold: float,
84
70
  ):
85
71
  """
86
72
  Detect infrastructure drift using 3-way validation.
87
-
73
+
88
74
  Compares inventory collection results against AWS API and terraform state
89
75
  to identify discrepancies and provide actionable recommendations.
90
76
  """
@@ -98,9 +84,9 @@ def detect_drift(
98
84
  # Use operational profile as default
99
85
  default_profile = get_profile_for_operation("operational", None)
100
86
  profile_list = [default_profile]
101
-
87
+
102
88
  print_info(f"Starting drift detection for {len(profile_list)} profile(s)")
103
-
89
+
104
90
  # Validate all profiles
105
91
  valid_profiles = []
106
92
  for prof in profile_list:
@@ -108,99 +94,83 @@ def detect_drift(
108
94
  valid_profiles.append(prof)
109
95
  else:
110
96
  print_error(f"Profile '{prof}' validation failed - skipping")
111
-
97
+
112
98
  if not valid_profiles:
113
99
  print_error("No valid profiles available for drift detection")
114
100
  return
115
-
101
+
116
102
  # Create validator with terraform integration
117
- validator = create_inventory_mcp_validator(
118
- profiles=valid_profiles,
119
- terraform_directory=terraform_dir
120
- )
121
-
103
+ validator = create_inventory_mcp_validator(profiles=valid_profiles, terraform_directory=terraform_dir)
104
+
122
105
  # Set custom threshold if provided
123
106
  validator.validation_threshold = threshold
124
-
107
+
125
108
  console.print(f"[blue]🔍 Initializing drift detection...[/]")
126
109
  console.print(f"[dim]Terraform directory: {terraform_dir}[/]")
127
110
  console.print(f"[dim]Accuracy threshold: {threshold}%[/]")
128
-
111
+
129
112
  # For demonstration, create mock inventory data
130
113
  # In practice, this would come from actual inventory collection
131
114
  mock_inventory = _create_mock_inventory_data(valid_profiles)
132
-
115
+
133
116
  # Perform enhanced validation with drift detection
134
117
  validation_results = validator.validate_inventory_data(mock_inventory)
135
-
118
+
136
119
  # Generate summary results
137
120
  overall_accuracy = validation_results.get("total_accuracy", 0)
138
121
  terraform_integration = validation_results.get("terraform_integration", {})
139
-
122
+
140
123
  if validation_results.get("passed_validation", False):
141
124
  print_success(f"✅ Drift detection completed: {overall_accuracy:.1f}% accuracy")
142
125
  else:
143
126
  console.print(f"[yellow]🔄 Infrastructure drift detected: {overall_accuracy:.1f}% accuracy[/]")
144
-
127
+
145
128
  # Display terraform integration status
146
129
  if terraform_integration.get("enabled", False):
147
130
  tf_files = terraform_integration.get("state_files_discovered", 0)
148
131
  print_info(f"Terraform integration: {tf_files} configuration files analyzed")
149
-
132
+
150
133
  drift_analysis = terraform_integration.get("drift_analysis", {})
151
134
  if drift_analysis:
152
135
  drift_pct = drift_analysis.get("drift_percentage", 0)
153
136
  tf_coverage = drift_analysis.get("terraform_coverage_percentage", 0)
154
137
  console.print(f"[dim]📊 {drift_pct:.1f}% of accounts have drift detected[/]")
155
138
  console.print(f"[dim]🎯 {tf_coverage:.1f}% of accounts have terraform coverage[/]")
156
-
139
+
157
140
  # Generate and export report if requested
158
141
  if output_file or report_format != "console":
159
142
  drift_report = generate_drift_report(valid_profiles, mock_inventory, terraform_dir)
160
-
143
+
161
144
  if output_file:
162
145
  _export_drift_report(drift_report, output_file, report_format)
163
146
  print_success(f"Drift report exported to: {output_file}")
164
-
147
+
165
148
  print_info("Drift detection analysis complete")
166
-
149
+
167
150
  except Exception as e:
168
151
  print_error(f"Drift detection failed: {str(e)}")
169
152
  raise click.Abort()
170
153
 
171
154
 
172
155
  @drift.command("report")
173
- @click.option(
174
- "--profile",
175
- help="AWS profile to use for single-account analysis"
176
- )
156
+ @click.option("--profile", help="AWS profile to use for single-account analysis")
177
157
  @click.option(
178
158
  "--terraform-dir",
179
159
  default="/Volumes/Working/1xOps/CloudOps-Runbooks/terraform-aws",
180
- help="Path to terraform configuration directory"
160
+ help="Path to terraform configuration directory",
181
161
  )
182
162
  @click.option(
183
163
  "--format",
184
164
  "report_format",
185
165
  type=click.Choice(["json", "csv", "markdown"]),
186
166
  default="json",
187
- help="Report output format"
167
+ help="Report output format",
188
168
  )
189
- @click.option(
190
- "--output",
191
- "output_file",
192
- required=True,
193
- help="Output file path for drift report"
194
- )
195
- def generate_report(
196
- profile: Optional[str],
197
- terraform_dir: str,
198
- report_format: str,
199
- output_file: str
200
- ):
169
+ @click.option("--output", "output_file", required=True, help="Output file path for drift report")
170
+ def generate_report(profile: Optional[str], terraform_dir: str, report_format: str, output_file: str):
201
171
  """
202
172
  Generate comprehensive infrastructure drift report.
203
-
173
+
204
174
  Creates detailed drift analysis report with actionable recommendations
205
175
  for infrastructure as code management and compliance.
206
176
  """
@@ -208,35 +178,35 @@ def generate_report(
208
178
  # Use default profile if none specified
209
179
  if not profile:
210
180
  profile = get_profile_for_operation("operational", None)
211
-
181
+
212
182
  print_info(f"Generating drift report for profile: {profile}")
213
-
183
+
214
184
  # Validate profile
215
185
  if not validate_profile_access(profile, "drift-reporting"):
216
186
  print_error(f"Profile '{profile}' validation failed")
217
187
  return
218
-
188
+
219
189
  # Create mock inventory for demonstration
220
190
  mock_inventory = _create_mock_inventory_data([profile])
221
-
191
+
222
192
  # Generate comprehensive drift report
223
193
  drift_report = generate_drift_report([profile], mock_inventory, terraform_dir)
224
-
194
+
225
195
  # Export report
226
196
  _export_drift_report(drift_report, output_file, report_format)
227
-
197
+
228
198
  print_success(f"Drift report generated: {output_file}")
229
-
199
+
230
200
  # Display summary
231
201
  accounts_analyzed = drift_report.get("accounts_analyzed", 0)
232
202
  overall_accuracy = drift_report.get("overall_accuracy", 0)
233
203
  drift_detected = drift_report.get("drift_detected", False)
234
-
204
+
235
205
  console.print(f"[dim]📊 Analysis Summary:[/]")
236
206
  console.print(f"[dim] Accounts analyzed: {accounts_analyzed}[/]")
237
207
  console.print(f"[dim] Overall accuracy: {overall_accuracy:.1f}%[/]")
238
208
  console.print(f"[dim] Drift detected: {'Yes' if drift_detected else 'No'}[/]")
239
-
209
+
240
210
  except Exception as e:
241
211
  print_error(f"Report generation failed: {str(e)}")
242
212
  raise click.Abort()
@@ -245,7 +215,7 @@ def generate_report(
245
215
  def _create_mock_inventory_data(profiles: List[str]) -> dict:
246
216
  """Create mock inventory data for demonstration purposes."""
247
217
  mock_data = {}
248
-
218
+
249
219
  for profile in profiles:
250
220
  mock_data[profile] = {
251
221
  "resource_counts": {
@@ -258,12 +228,12 @@ def _create_mock_inventory_data(profiles: List[str]) -> dict:
258
228
  "cloudformation": 4,
259
229
  "elbv2": 1,
260
230
  "route53": 2,
261
- "sns": 3
231
+ "sns": 3,
262
232
  },
263
233
  "regions": ["us-east-1", "us-west-2", "ap-southeast-2"],
264
- "collection_timestamp": "2024-09-10T12:00:00Z"
234
+ "collection_timestamp": "2024-09-10T12:00:00Z",
265
235
  }
266
-
236
+
267
237
  return mock_data
268
238
 
269
239
 
@@ -271,50 +241,53 @@ def _export_drift_report(report_data: dict, output_file: str, format_type: str)
271
241
  """Export drift report to specified format."""
272
242
  import json
273
243
  from pathlib import Path
274
-
244
+
275
245
  output_path = Path(output_file)
276
246
  output_path.parent.mkdir(parents=True, exist_ok=True)
277
-
247
+
278
248
  if format_type == "json":
279
- with open(output_path, 'w') as f:
249
+ with open(output_path, "w") as f:
280
250
  json.dump(report_data, f, indent=2, default=str)
281
251
  elif format_type == "csv":
282
252
  # Create CSV summary
283
253
  import csv
284
- with open(output_path, 'w', newline='') as f:
254
+
255
+ with open(output_path, "w", newline="") as f:
285
256
  writer = csv.writer(f)
286
- writer.writerow(['Account ID', 'Profile', 'Accuracy %', 'Drift Detected', 'Terraform Coverage'])
287
-
257
+ writer.writerow(["Account ID", "Profile", "Accuracy %", "Drift Detected", "Terraform Coverage"])
258
+
288
259
  for account in report_data.get("detailed_analysis", []):
289
- writer.writerow([
290
- account.get("account_id", "Unknown"),
291
- account.get("profile", "Unknown"),
292
- f"{account.get('accuracy_percent', 0):.1f}",
293
- "Yes" if account.get("drift_summary", {}).get("drift_detected", 0) > 0 else "No",
294
- "Yes" if account.get("terraform_coverage", False) else "No"
295
- ])
260
+ writer.writerow(
261
+ [
262
+ account.get("account_id", "Unknown"),
263
+ account.get("profile", "Unknown"),
264
+ f"{account.get('accuracy_percent', 0):.1f}",
265
+ "Yes" if account.get("drift_summary", {}).get("drift_detected", 0) > 0 else "No",
266
+ "Yes" if account.get("terraform_coverage", False) else "No",
267
+ ]
268
+ )
296
269
  elif format_type == "markdown":
297
270
  # Create markdown report
298
- with open(output_path, 'w') as f:
271
+ with open(output_path, "w") as f:
299
272
  f.write("# Infrastructure Drift Analysis Report\n\n")
300
273
  f.write(f"**Generated:** {report_data.get('generated_timestamp', 'Unknown')}\n\n")
301
-
274
+
302
275
  terraform_info = report_data.get("terraform_integration", {})
303
276
  f.write(f"**Terraform Integration:** {terraform_info.get('enabled', False)}\n")
304
277
  f.write(f"**State Files Discovered:** {terraform_info.get('state_files_discovered', 0)}\n\n")
305
-
278
+
306
279
  f.write("## Summary\n\n")
307
280
  f.write(f"- **Accounts Analyzed:** {report_data.get('accounts_analyzed', 0)}\n")
308
281
  f.write(f"- **Overall Accuracy:** {report_data.get('overall_accuracy', 0):.1f}%\n")
309
282
  f.write(f"- **Drift Detected:** {'Yes' if report_data.get('drift_detected', False) else 'No'}\n\n")
310
-
283
+
311
284
  f.write("## Detailed Analysis\n\n")
312
285
  for account in report_data.get("detailed_analysis", []):
313
286
  f.write(f"### Account: {account.get('account_id', 'Unknown')}\n\n")
314
287
  f.write(f"- **Profile:** {account.get('profile', 'Unknown')}\n")
315
288
  f.write(f"- **Accuracy:** {account.get('accuracy_percent', 0):.1f}%\n")
316
289
  f.write(f"- **Terraform Coverage:** {'Yes' if account.get('terraform_coverage', False) else 'No'}\n\n")
317
-
290
+
318
291
  recommendations = account.get("recommendations", [])
319
292
  if recommendations:
320
293
  f.write("**Recommendations:**\n")
@@ -324,4 +297,4 @@ def _export_drift_report(report_data: dict, output_file: str, format_type: str)
324
297
 
325
298
 
326
299
  if __name__ == "__main__":
327
- drift()
300
+ drift()
@@ -14,7 +14,7 @@ def del_vpc(ocredentials, fVPCId, fRegion):
14
14
 
15
15
  import boto3
16
16
  from botocore.exceptions import ClientError
17
- from colorama import Fore, init
17
+ from runbooks.common.rich_utils import console
18
18
 
19
19
  # ERASE_LINE = '\x1b[2K'
20
20
 
@@ -335,7 +335,7 @@ def del_vpc(ocredentials, fVPCId, fRegion):
335
335
  return 1 # out of the try
336
336
  except ClientError as my_Error:
337
337
  print(my_Error)
338
- print(f"{Fore.RED}What to do now?{Fore.RESET}")
338
+ print(f"[red]What to do now?")
339
339
  return 1
340
340
 
341
341
  return 0
@@ -70,9 +70,8 @@ import Inventory_Modules
70
70
  from account_class import aws_acct_access
71
71
  from ArgumentsClass import CommonArguments
72
72
  from botocore.exceptions import ClientError
73
- from colorama import Fore, init
73
+ from runbooks.common.rich_utils import console
74
74
 
75
- init()
76
75
  __version__ = "2023.05.04"
77
76
 
78
77
  # Configure comprehensive argument parsing for CloudFormation drift detection operations
@@ -142,7 +141,6 @@ This would enable:
142
141
  """
143
142
 
144
143
  ##########################
145
- ERASE_LINE = "\x1b[2K" # Terminal control for dynamic output updates
146
144
 
147
145
  # Initialize AWS account access and organizational context for drift detection
148
146
  aws_acct = aws_acct_access(pProfile)
@@ -235,7 +233,7 @@ for account in ChildAccounts:
235
233
  # Log regional stack discovery progress for operational visibility
236
234
  logging.warning(f"Account: {account['AccountId']} | Region: {region} | Found {StackNum} Stacks")
237
235
  logging.info(
238
- f"{ERASE_LINE}{Fore.RED}Account: {account['AccountId']} Region: {region} Found {StackNum} Stacks{Fore.RESET}"
236
+ f"[red]Account: {account['AccountId']} Region: {region} Found {StackNum} Stacks"
239
237
  )
240
238
 
241
239
  except ClientError as my_Error:
@@ -260,10 +258,10 @@ for account in ChildAccounts:
260
258
  NumStacksFound += 1 # Increment total drift detection counter
261
259
 
262
260
  # Provide comprehensive operational summary with drift detection metrics
263
- print(ERASE_LINE)
261
+ console.print()
264
262
  print(
265
- f"{Fore.RED}Looked through {NumStacksFound} Stacks across {len(ChildAccounts)} accounts across "
266
- f"{len(RegionList)} regions{Fore.RESET}"
263
+ f"[red]Looked through {NumStacksFound} Stacks across {len(ChildAccounts)} accounts across "
264
+ f"{len(RegionList)} regions"
267
265
  )
268
266
  print()
269
267
 
@@ -76,7 +76,7 @@ from ArgumentsClass import CommonArguments
76
76
 
77
77
  # import simplejson as json
78
78
  from botocore.exceptions import ClientError
79
- from colorama import Fore, init
79
+ from runbooks.common.rich_utils import console
80
80
  from Inventory_Modules import (
81
81
  display_results,
82
82
  find_stack_instances3,
@@ -87,9 +87,7 @@ from Inventory_Modules import (
87
87
  print_timings,
88
88
  )
89
89
 
90
- init()
91
90
  __version__ = "2024.05.18"
92
- ERASE_LINE = "\x1b[2K"
93
91
  begin_time = time()
94
92
 
95
93
 
@@ -250,7 +248,7 @@ def setup_auth_and_regions(fProfile: str, f_AccountList: list, f_Region: str, f_
250
248
  pass # Valid profile specification (string or None for default)
251
249
  else: # Invalid profile type (list, integer, etc.) - should be caught at argparse level
252
250
  print(
253
- f"{Fore.RED}You specified an invalid profile name. This script only allows for one profile at a time. Please try again.{Fore.RESET}"
251
+ f"[red]You specified an invalid profile name. This script only allows for one profile at a time. Please try again."
254
252
  )
255
253
  sys.exit(7)
256
254
 
@@ -268,8 +266,8 @@ def setup_auth_and_regions(fProfile: str, f_AccountList: list, f_Region: str, f_
268
266
  # Validate home region specification ensuring single region for StackSet control plane
269
267
  if f_Region.lower() not in AllRegions:
270
268
  print(
271
- f"{Fore.RED}You specified '{f_Region}' as the region, but this script only works with a single region.\n"
272
- f"Please run the command again and specify only a single, valid region{Fore.RESET}"
269
+ f"[red]You specified '{f_Region}' as the region, but this script only works with a single region.\n"
270
+ f"Please run the command again and specify only a single, valid region"
273
271
  )
274
272
  sys.exit(9)
275
273
 
@@ -309,12 +307,12 @@ def setup_auth_and_regions(fProfile: str, f_AccountList: list, f_Region: str, f_
309
307
 
310
308
  # Display account exclusion list if specified
311
309
  print(
312
- f"While skipping these accounts:\n{Fore.RED}{f_args.SkipAccounts}{Fore.RESET}"
310
+ f"While skipping these accounts:\n[red]{f_args.SkipAccounts}"
313
311
  ) if f_args.SkipAccounts is not None else ""
314
312
 
315
313
  # Display fragment filtering configuration with exact vs substring matching
316
314
  if f_args.Exact:
317
- print(f"\t\tFor stacksets that {Fore.RED}exactly match{Fore.RESET}: {f_args.Fragments}")
315
+ print(f"\t\tFor stacksets that [red]exactly match: {f_args.Fragments}")
318
316
  else:
319
317
  print(
320
318
  f"\t\tFor stacksets that contain th{'is fragment' if len(f_args.Fragments) == 1 else 'ese fragments'}: {f_args.Fragments}"
@@ -712,7 +710,7 @@ if __name__ == "__main__":
712
710
 
713
711
  if pTiming:
714
712
  print(ERASE_LINE)
715
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
713
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
716
714
 
717
715
  print()
718
716
  print("Thanks for using this script...")
@@ -70,11 +70,10 @@ import Inventory_Modules
70
70
  from account_class import aws_acct_access
71
71
  from ArgumentsClass import CommonArguments
72
72
  from botocore.exceptions import ClientError
73
- from colorama import Fore, init
73
+ from runbooks.common.rich_utils import console
74
74
  from Inventory_Modules import display_results
75
75
 
76
76
  # Initialize colorama for cross-platform colored terminal output
77
- init()
78
77
  __version__ = "2024.03.06"
79
78
  begin_time = time() # Script execution timing for performance monitoring
80
79
  sleep_interval = 5 # Default wait interval for drift detection operations
@@ -248,7 +247,7 @@ def setup_auth(fProfile: str) -> aws_acct_access:
248
247
 
249
248
  # Validate successful authentication and account access
250
249
  if not aws_acct.Success:
251
- print(f"{Fore.RED}Profile {pProfile} failed to access an account. Check credentials and try again{Fore.RESET}")
250
+ print(f"[red]Profile {pProfile} failed to access an account. Check credentials and try again")
252
251
  sys.exit(99)
253
252
 
254
253
  # Display comprehensive operational context for user confirmation and audit logging
@@ -259,7 +258,7 @@ def setup_auth(fProfile: str) -> aws_acct_access:
259
258
 
260
259
  # Display fragment filtering criteria with exact vs substring matching context
261
260
  if pExact:
262
- print(f"\t\tFor stacksets that {Fore.RED}exactly match{Fore.RESET}: {pFragments}")
261
+ print(f"\t\tFor stacksets that [red]exactly match: {pFragments}")
263
262
  else:
264
263
  print(
265
264
  f"\t\tFor stacksets that contain th{'is fragment' if len(pFragments) == 1 else 'ese fragments'}: {pFragments}"
@@ -723,11 +722,11 @@ if __name__ == "__main__":
723
722
  display_results(sorted_all_stacksets, display_dict_stacksets, None, pSaveFilename)
724
723
 
725
724
  print(ERASE_LINE)
726
- print(f"{Fore.RED}Looked through {len(sorted_all_stacksets)} StackSets across the {pRegion} region{Fore.RESET}")
725
+ print(f"[red]Looked through {len(sorted_all_stacksets)} StackSets across the {pRegion} region")
727
726
  print()
728
727
 
729
728
  if pTiming:
730
729
  print(ERASE_LINE)
731
- print(f"{Fore.GREEN}This script took {time() - begin_time:.3f} seconds{Fore.RESET}")
730
+ print(f"[green]This script took {time() - begin_time:.3f} seconds")
732
731
  print("Thanks for using this script...")
733
732
  print()