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
@@ -12,10 +12,9 @@ import Inventory_Modules
12
12
  from account_class import aws_acct_access
13
13
  from ArgumentsClass import CommonArguments
14
14
  from botocore.exceptions import ClientError
15
- from colorama import Fore, init
16
- from prettytable import PrettyTable
15
+ from runbooks.common.rich_utils import console, print_success, print_error, print_info
16
+ from runbooks.common.rich_utils import console, create_table
17
17
 
18
- init()
19
18
  __version__ = "2024.05.18"
20
19
 
21
20
  script_path, script_name = os.path.split(sys.argv[0])
@@ -86,32 +85,32 @@ def intersection(lst1, lst2):
86
85
  def explain_script():
87
86
  print("This script does the following... ")
88
87
  print(
89
- f"{Fore.BLUE} 0.{Fore.RESET} Checks to ensure you have the necessary cross-account role access to the child account."
88
+ f"[blue] 0. Checks to ensure you have the necessary cross-account role access to the child account."
90
89
  )
91
- print(f"{Fore.BLUE} 1.{Fore.RESET} This check previously checked for default VPCs, but has since been removed.")
92
- print(f"{Fore.BLUE} 2.{Fore.RESET} Checks the child account in each of the regions")
93
- print(f" to see if there's already a {Fore.RED}Config Recorder and Delivery Channel {Fore.RESET}enabled...")
90
+ print(f"[blue] 1. This check previously checked for default VPCs, but has since been removed.")
91
+ print(f"[blue] 2. Checks the child account in each of the regions")
92
+ print(f" to see if there's already a [red]Config Recorder and Delivery Channel enabled...")
94
93
  print(
95
- f"{Fore.BLUE} 3.{Fore.RESET} Checks that there isn't a duplicate {Fore.RED}CloudTrail{Fore.RESET} trail in the account."
94
+ f"[blue] 3. Checks that there isn't a duplicate [red]CloudTrail trail in the account."
96
95
  )
97
96
  print(
98
- f"{Fore.BLUE} 4.{Fore.RESET} This check previously checked for the presence of GuardDuty within this account, but has since been removed."
97
+ f"[blue] 4. This check previously checked for the presence of GuardDuty within this account, but has since been removed."
99
98
  )
100
99
  print(
101
- f"{Fore.BLUE} 5.{Fore.RESET} This child account {Fore.RED}must exist{Fore.RESET} within the Parent Organization."
100
+ f"[blue] 5. This child account [red]must exist within the Parent Organization."
102
101
  )
103
102
  print(" If it doesn't - then you must move it into this Org - this script can't do that for you.")
104
103
  print(
105
- f"{Fore.BLUE} 6.{Fore.RESET} The target account {Fore.RED}can't exist{Fore.RESET} within an already managed OU."
104
+ f"[blue] 6. The target account [red]can't exist within an already managed OU."
106
105
  )
107
106
  print(" If it does - then you're already managing this account with Control Tower and just don't know it.")
108
- print(f"{Fore.BLUE} 7.{Fore.RESET} Looking for {Fore.RED}SNS Topics{Fore.RESET} with duplicate names.")
107
+ print(f"[blue] 7. Looking for [red]SNS Topics with duplicate names.")
109
108
  print(" If found, we can delete them, but you probably want to do that manually - to be sure.")
110
- print(f"{Fore.BLUE} 8.{Fore.RESET} Looking for {Fore.RED}Lambda Functions{Fore.RESET} with duplicate names.")
109
+ print(f"[blue] 8. Looking for [red]Lambda Functions with duplicate names.")
111
110
  print(" If found, we can delete them, but you probably want to do that manually - to be sure.")
112
- print(f"{Fore.BLUE} 9.{Fore.RESET} Looking for {Fore.RED}IAM Roles{Fore.RESET} with duplicate names.")
111
+ print(f"[blue] 9. Looking for [red]IAM Roles with duplicate names.")
113
112
  print(" If found, we can delete them, but you probably want to do that manually - to be sure.")
114
- print(f"{Fore.BLUE} 10.{Fore.RESET} Looking for duplicate {Fore.RED}CloudWatch Log Groups.{Fore.RESET}")
113
+ print(f"[blue] 10. Looking for duplicate [red]CloudWatch Log Groups.")
115
114
  print(" If found, we can delete them, but you probably want to do that manually - to be sure.")
116
115
  print()
117
116
  print("Since this script is fairly new - All comments or suggestions are enthusiastically encouraged")
@@ -166,7 +165,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
166
165
  ]
167
166
  # TODO: I don't use this next variable, but eventually I intend to supply the JSON code needed to update a role with.
168
167
  json_formatted_str_TP = ""
169
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
168
+ print(f"[blue]{Step}:") if verbose < 50 else None
170
169
  print(
171
170
  f"Confirming we have the necessary cross-account access to account {fChildAccountId} in region {fRegion}"
172
171
  ) if verbose < 50 else None
@@ -206,7 +205,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
206
205
  finally:
207
206
  if account_credentials["AccessError"]:
208
207
  print(
209
- f"{Fore.RED}We weren't able to connect to the Child Account {fChildAccountId} from this Management Account {aws_account.acct_number}. Please check the role Trust Policy and re-run this script.{Fore.RESET}"
208
+ f"[red]We weren't able to connect to the Child Account {fChildAccountId} from this Management Account {aws_account.acct_number}. Please check the role Trust Policy and re-run this script."
210
209
  )
211
210
  print(
212
211
  f"The following list of roles were tried, but none were allowed access to account {fChildAccountId} using the {aws_account.acct_number} account in region {fRegion}"
@@ -219,10 +218,10 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
219
218
  logging.info("Was able to successfully connect using the credentials... ")
220
219
  print() if verbose < 50 else None
221
220
  print(
222
- f"Confirmed the role {Fore.GREEN}{account_credentials['Role']}{Fore.RESET}"
223
- f" exists in account {Fore.GREEN}{fChildAccountId}{Fore.RESET} and trusts the Management Account"
221
+ f"Confirmed the role [green]{account_credentials['Role']}"
222
+ f" exists in account [green]{fChildAccountId} and trusts the Management Account"
224
223
  ) if verbose < 50 else None
225
- print(f"{Fore.GREEN}** Step 0 completed without issues{Fore.RESET}") if verbose < 50 else None
224
+ print(f"[green]** Step 0 completed without issues") if verbose < 50 else None
226
225
  print() if verbose < 50 else None
227
226
 
228
227
  """
@@ -231,7 +230,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
231
230
  """
232
231
  Step = "Step1"
233
232
  serviceName = "config.amazonaws.com"
234
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
233
+ print(f"[blue]{Step}:") if verbose < 50 else None
235
234
  print(
236
235
  f"Checking Org Account {fChildAccountId} to see if Config is enabled at the Org Level\n"
237
236
  f"Which means we only run this step if this is the Root Account\n"
@@ -256,7 +255,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
256
255
 
257
256
  if fFixRun:
258
257
  logging.warning(
259
- f"{Fore.RED}Found {serviceName} is enabled as an Organizational Service and you've requested that we remedy that for you... {Fore.RESET}"
258
+ f"[red]Found {serviceName} is enabled as an Organizational Service and you've requested that we remedy that for you... "
260
259
  )
261
260
  ProcessStatus[Step]["Success"] = False
262
261
  ProcessStatus[Step]["IssuesFound"] += 1
@@ -278,25 +277,25 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
278
277
  logging.error(DelConfigService["ErrorMessage"])
279
278
 
280
279
  if ProcessStatus[Step]["Success"]:
281
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
280
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
282
281
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
283
282
  print(
284
- f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issue, but we were able to fix it{Fore.RESET}"
283
+ f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issue, but we were able to fix it"
285
284
  ) if verbose < 50 else None
286
285
  ProcessStatus[Step]["Success"] = True
287
286
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
288
287
  print(
289
- f"{ERASE_LINE + Fore.RED}** {Step} completed, but there was {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blocker found that wasn't fixed{Fore.RESET}"
288
+ f"{ERASE_LINE + Fore.RED}** {Step} completed, but there was {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blocker found that wasn't fixed"
290
289
  ) if verbose < 50 else None
291
290
  else:
292
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
291
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
293
292
  print() if verbose < 50 else None
294
293
 
295
294
  # Step 2
296
295
  # This part will check the Config Recorder and Delivery Channel. If they have one, we need to delete it, so we can create another. We'll ask whether this is ok before we delete.
297
296
  Step = "Step2"
298
297
  try:
299
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
298
+ print(f"[blue]{Step}:") if verbose < 50 else None
300
299
  print(
301
300
  f" Checking account {fChildAccountId} for a Config Recorders and Delivery Channels in any region"
302
301
  ) if verbose < 50 else None
@@ -340,7 +339,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
340
339
 
341
340
  for _ in range(len(ConfigList)):
342
341
  logging.warning(
343
- f"{Fore.RED}Found a config recorder for account %s in region %s",
342
+ f"[red]Found a config recorder for account %s in region %s",
344
343
  ConfigList[_]["AccountID"],
345
344
  ConfigList[_]["Region"] + Fore.RESET,
346
345
  )
@@ -361,7 +360,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
361
360
  ProcessStatus[Step]["IssuesFixed"] += 1
362
361
  for _ in range(len(DeliveryChanList)):
363
362
  logging.warning(
364
- f"{Fore.RED}Found a delivery channel for account {DeliveryChanList[_]['AccountID']} in region {DeliveryChanList[_]['Region']}{Fore.RESET}"
363
+ f"[red]Found a delivery channel for account {DeliveryChanList[_]['AccountID']} in region {DeliveryChanList[_]['Region']}"
365
364
  )
366
365
  ProcessStatus[Step]["Success"] = False
367
366
  ProcessStatus[Step]["IssuesFound"] += 1
@@ -380,18 +379,18 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
380
379
  ProcessStatus[Step]["IssuesFixed"] += 1
381
380
 
382
381
  if ProcessStatus[Step]["Success"]:
383
- print(f"{ERASE_LINE + Fore.GREEN}** Step 2 completed with no issues{Fore.RESET}") if verbose < 50 else None
382
+ print(f"{ERASE_LINE + Fore.GREEN}** Step 2 completed with no issues") if verbose < 50 else None
384
383
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
385
384
  print(
386
- f"{ERASE_LINE + Fore.GREEN}** Step 2 found {ProcessStatus[Step]['IssuesFound']} issues, but they were fixed by deleting the existing Config Recorders and Delivery Channels{Fore.RESET}"
385
+ f"{ERASE_LINE + Fore.GREEN}** Step 2 found {ProcessStatus[Step]['IssuesFound']} issues, but they were fixed by deleting the existing Config Recorders and Delivery Channels"
387
386
  ) if verbose < 50 else None
388
387
  ProcessStatus[Step]["Success"] = True
389
388
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
390
389
  print(
391
- f"{ERASE_LINE + Fore.RED}** Step 2 completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} items found that weren't deleted{Fore.RESET}"
390
+ f"{ERASE_LINE + Fore.RED}** Step 2 completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} items found that weren't deleted"
392
391
  ) if verbose < 50 else None
393
392
  else:
394
- print(f"{ERASE_LINE + Fore.RED}** Step 2 completed with blockers found{Fore.RESET}") if verbose < 50 else None
393
+ print(f"{ERASE_LINE + Fore.RED}** Step 2 completed with blockers found") if verbose < 50 else None
395
394
  print() if verbose < 50 else None
396
395
 
397
396
  # Step 3
@@ -399,7 +398,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
399
398
  Step = "Step3"
400
399
  CTtrails2 = []
401
400
  try:
402
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
401
+ print(f"[blue]{Step}:") if verbose < 50 else None
403
402
  print(
404
403
  f" Checking account {fChildAccountId} for a specially named CloudTrail in all regions"
405
404
  ) if verbose < 50 else None
@@ -422,7 +421,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
422
421
 
423
422
  for _ in range(len(CTtrails2)):
424
423
  logging.warning(
425
- f"{Fore.RED}Found a CloudTrail trail for account {fChildAccountId} in region {CTtrails2[_]['HomeRegion']} named {CTtrails2[_]['Name']}{Fore.RESET}"
424
+ f"[red]Found a CloudTrail trail for account {fChildAccountId} in region {CTtrails2[_]['HomeRegion']} named {CTtrails2[_]['Name']}"
426
425
  )
427
426
  ProcessStatus[Step]["IssuesFound"] += 1
428
427
  ProcessStatus[Step]["ProblemsFound"].extend(CTtrails2)
@@ -435,18 +434,18 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
435
434
  print(my_Error)
436
435
 
437
436
  if ProcessStatus[Step]["Success"]:
438
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
437
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
439
438
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
440
439
  print(
441
- f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but they were fixed by deleting the existing CloudTrail trail names{Fore.RESET}"
440
+ f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but they were fixed by deleting the existing CloudTrail trail names"
442
441
  ) if verbose < 50 else None
443
442
  ProcessStatus[Step]["Success"] = True
444
443
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
445
444
  print(
446
- f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} trail names found that wasn't deleted{Fore.RESET}"
445
+ f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} trail names found that wasn't deleted"
447
446
  ) if verbose < 50 else None
448
447
  else:
449
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
448
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
450
449
  print() if verbose < 50 else None
451
450
 
452
451
  """ Step 4
@@ -515,7 +514,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
515
514
  """
516
515
  # try:
517
516
  Step = "Step5"
518
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
517
+ print(f"[blue]{Step}:") if verbose < 50 else None
519
518
  print(" Checking that the account is part of the AWS Organization.") if verbose < 50 else None
520
519
  if not (fChildAccountId in OrgAccountList):
521
520
  print() if verbose < 50 else None
@@ -529,18 +528,18 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
529
528
  print("Which it is - so ...") if verbose < 50 else None
530
529
 
531
530
  if ProcessStatus[Step]["Success"]:
532
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
531
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
533
532
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
534
533
  print(
535
- f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issue, but we were able to fix it{Fore.RESET}"
534
+ f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issue, but we were able to fix it"
536
535
  ) if verbose < 50 else None
537
536
  ProcessStatus[Step]["Success"] = True
538
537
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
539
538
  print(
540
- f"{ERASE_LINE + Fore.RED}** {Step} completed, but there was {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blocker found that wasn't fixed{Fore.RESET}"
539
+ f"{ERASE_LINE + Fore.RED}** {Step} completed, but there was {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blocker found that wasn't fixed"
541
540
  ) if verbose < 50 else None
542
541
  else:
543
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
542
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
544
543
  print() if verbose < 50 else None
545
544
 
546
545
  # Step 6
@@ -550,7 +549,7 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
550
549
 
551
550
  Step = "Step6"
552
551
  try:
553
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
552
+ print(f"[blue]{Step}:") if verbose < 50 else None
554
553
  print(
555
554
  f" Checking account {fChildAccountId} to make sure it's not already in a Control-Tower managed OU"
556
555
  ) if verbose < 50 else None
@@ -567,13 +566,13 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
567
566
  # Checking for SNS Topics
568
567
  Step = "Step7"
569
568
  try:
570
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
569
+ print(f"[blue]{Step}:") if verbose < 50 else None
571
570
  print(
572
571
  f" Checking account {fChildAccountId} for any SNS topics containing the 'controltower' string"
573
572
  ) if verbose < 50 else None
574
573
  SNSTopics2 = []
575
574
  logging.warning(
576
- "Checking account %s in region %s for", fChildAccountId, f"{fRegion + Fore.RED} SNS Topics{Fore.RESET}"
575
+ "Checking account %s in region %s for", fChildAccountId, f"{fRegion + Fore.RED} SNS Topics"
577
576
  )
578
577
  print(
579
578
  ERASE_LINE, f"Checking account {fChildAccountId} in region {fRegion} for SNS Topics", end="\r"
@@ -596,31 +595,31 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
596
595
  ProcessStatus[Step]["Success"] = False
597
596
 
598
597
  if ProcessStatus[Step]["Success"]:
599
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
598
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
600
599
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
601
600
  print(
602
- f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending SNS Topics{Fore.RESET}"
601
+ f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending SNS Topics"
603
602
  ) if verbose < 50 else None
604
603
  ProcessStatus[Step]["Success"] = True
605
604
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
606
605
  print(
607
- f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that wasn't fixed{Fore.RESET}"
606
+ f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that wasn't fixed"
608
607
  ) if verbose < 50 else None
609
608
  else:
610
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
609
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
611
610
  print() if verbose < 50 else None
612
611
 
613
612
  # Step 8
614
613
  # Checking for Lambda functions
615
614
  Step = "Step8"
616
615
  try:
617
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
616
+ print(f"[blue]{Step}:") if verbose < 50 else None
618
617
  print(
619
618
  f" Checking account {fChildAccountId} for any Lambda functions containing the 'controltower' string"
620
619
  ) if verbose < 50 else None
621
620
  LambdaFunctions2 = []
622
621
  logging.warning(
623
- f"Checking account %s in region %s for {Fore.RED}Lambda functions{Fore.RESET}", fChildAccountId, fRegion
622
+ f"Checking account %s in region %s for [red]Lambda functions", fChildAccountId, fRegion
624
623
  )
625
624
  print(
626
625
  ERASE_LINE, f"Checking account {fChildAccountId} in region {fRegion} for Lambda Functions", end="\r"
@@ -651,18 +650,18 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
651
650
  ProcessStatus[Step]["Success"] = False
652
651
 
653
652
  if ProcessStatus[Step]["Success"]:
654
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
653
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
655
654
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
656
655
  print(
657
- f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending Lambda Functions{Fore.RESET}"
656
+ f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending Lambda Functions"
658
657
  ) if verbose < 50 else None
659
658
  ProcessStatus[Step]["Success"] = True
660
659
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
661
660
  print(
662
- f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that wasn't fixed{Fore.RESET}"
661
+ f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that wasn't fixed"
663
662
  ) if verbose < 50 else None
664
663
  else:
665
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
664
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
666
665
  print() if verbose < 50 else None
667
666
 
668
667
  # Step 9
@@ -670,12 +669,12 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
670
669
  # TODO: Need to find a way to only run this once, instead of for every region.
671
670
  Step = "Step9"
672
671
  try:
673
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
672
+ print(f"[blue]{Step}:") if verbose < 50 else None
674
673
  print(
675
674
  f" Checking account {fChildAccountId} for any Role names containing the 'controltower' string"
676
675
  ) if verbose < 50 else None
677
676
  RoleNames2 = []
678
- logging.warning(f"Checking account {Fore.RED}{fChildAccountId}{Fore.RESET} for Role names")
677
+ logging.warning(f"Checking account [red]{fChildAccountId} for Role names")
679
678
  RoleNames = Inventory_Modules.find_role_names2(
680
679
  account_credentials, "us-east-1", ["controltower", "ControlTower"]
681
680
  )
@@ -695,18 +694,18 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
695
694
  ProcessStatus[Step]["Success"] = False
696
695
 
697
696
  if ProcessStatus[Step]["Success"]:
698
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
697
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
699
698
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
700
699
  print(
701
- f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending IAM roles{Fore.RESET}"
700
+ f"{ERASE_LINE + Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending IAM roles"
702
701
  ) if verbose < 50 else None
703
702
  ProcessStatus[Step]["Success"] = True
704
703
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
705
704
  print(
706
- f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that remain to be fixed{Fore.RESET}"
705
+ f"{ERASE_LINE + Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that remain to be fixed"
707
706
  ) if verbose < 50 else None
708
707
  else:
709
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
708
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
710
709
  print() if verbose < 50 else None
711
710
 
712
711
  # Step 10
@@ -715,13 +714,13 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
715
714
 
716
715
  Step = "Step10"
717
716
  try:
718
- print(f"{Fore.BLUE}{Step}:{Fore.RESET}") if verbose < 50 else None
717
+ print(f"[blue]{Step}:") if verbose < 50 else None
719
718
  print(
720
719
  f"Checking account {fChildAccountId} to make sure there are no duplicate CloudWatch Log Groups"
721
720
  ) if verbose < 50 else None
722
721
  LogGroupNames2 = []
723
722
  logging.warning(
724
- f"Checking account {fChildAccountId} for {Fore.RED}duplicate CloudWatch Log Group names{Fore.RESET}"
723
+ f"Checking account {fChildAccountId} for [red]duplicate CloudWatch Log Group names"
725
724
  )
726
725
  LogGroupNames = Inventory_Modules.find_cw_log_group_names2(
727
726
  account_credentials, fRegion, ["controltower", "ControlTower"]
@@ -742,18 +741,18 @@ def DoAccountSteps(fChildAccountId, aws_account, fFixRun, fRegion):
742
741
  ProcessStatus[Step]["Success"] = False
743
742
 
744
743
  if ProcessStatus[Step]["Success"]:
745
- print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues{Fore.RESET}") if verbose < 50 else None
744
+ print(f"{ERASE_LINE + Fore.GREEN}** {Step} completed with no issues") if verbose < 50 else None
746
745
  elif ProcessStatus[Step]["IssuesFound"] - ProcessStatus[Step]["IssuesFixed"] == 0:
747
746
  print(
748
- f"{ERASE_LINE}{Fore.GREEN}** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending CW Log groups{Fore.RESET}"
747
+ f"{ERASE_LINE}[green]** {Step} found {ProcessStatus[Step]['IssuesFound']} issues, but we were able to remove the offending CW Log groups"
749
748
  ) if verbose < 50 else None
750
749
  ProcessStatus[Step]["Success"] = True
751
750
  elif ProcessStatus[Step]["IssuesFound"] > ProcessStatus[Step]["IssuesFixed"]:
752
751
  print(
753
- f"{ERASE_LINE}{Fore.RED}** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that remain to be fixed {Fore.RESET}"
752
+ f"{ERASE_LINE}[red]** {Step} completed, but there were {ProcessStatus[Step]['IssuesFound'] - ProcessStatus[Step]['IssuesFixed']} blockers found that remain to be fixed "
754
753
  ) if verbose < 50 else None
755
754
  else:
756
- print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found{Fore.RESET}") if verbose < 50 else None
755
+ print(f"{ERASE_LINE + Fore.RED}** {Step} completed with blockers found") if verbose < 50 else None
757
756
  print() if verbose < 50 else None
758
757
 
759
758
  """ Function Summary """
@@ -852,36 +851,43 @@ def DoThreadedAccountSteps(fChildAccountList, aws_account, fFixRun, fRegionList=
852
851
 
853
852
 
854
853
  def display_results():
855
- print()
856
- x = PrettyTable()
857
- y = PrettyTable()
858
-
859
- x.field_names = ["Account", "# of Regions", "Issues Found", "Issues Fixed", "Ready?"]
860
- y.field_names = [
861
- "Account",
862
- "Region",
863
- "Account Access",
864
- "Org Config",
865
- "Config",
866
- "CloudTrail",
867
- "GuardDuty",
868
- "Org Member",
869
- "CT OU",
870
- "SNS Topics",
871
- "Lambda",
872
- "Roles",
873
- "CW Log Groups",
874
- "Ready?",
875
- ]
854
+ console.print()
855
+ x = create_table(
856
+ title="Account Readiness Summary",
857
+ columns=[
858
+ {"header": "Account", "justify": "left"},
859
+ {"header": "# of Regions", "justify": "center"},
860
+ {"header": "Issues Found", "justify": "center"},
861
+ {"header": "Issues Fixed", "justify": "center"},
862
+ {"header": "Ready?", "justify": "center"},
863
+ ]
864
+ )
865
+ y = create_table(
866
+ title="Account Issue Details by Region",
867
+ columns=[
868
+ {"header": "Account", "justify": "left"},
869
+ {"header": "Region", "justify": "left"},
870
+ {"header": "Account Access", "justify": "center"},
871
+ {"header": "Org Config", "justify": "center"},
872
+ {"header": "Config", "justify": "center"},
873
+ {"header": "CloudTrail", "justify": "center"},
874
+ {"header": "GuardDuty", "justify": "center"},
875
+ {"header": "Org Member", "justify": "center"},
876
+ {"header": "CT OU", "justify": "center"},
877
+ {"header": "SNS Topics", "justify": "center"},
878
+ {"header": "Lambda", "justify": "center"},
879
+ {"header": "Roles", "justify": "center"},
880
+ {"header": "CW Log Groups", "justify": "center"},
881
+ {"header": "Ready?", "justify": "center"},
882
+ ]
883
+ )
876
884
  for i in SummarizedOrgResults:
877
885
  x.add_row(
878
- [
879
- SummarizedOrgResults[i]["AccountId"],
880
- len(SummarizedOrgResults[i]["Regions"]),
881
- SummarizedOrgResults[i]["IssuesFound"],
882
- SummarizedOrgResults[i]["IssuesFixed"],
883
- SummarizedOrgResults[i]["Ready"],
884
- ]
886
+ SummarizedOrgResults[i]["AccountId"],
887
+ str(len(SummarizedOrgResults[i]["Regions"])),
888
+ str(SummarizedOrgResults[i]["IssuesFound"]),
889
+ str(SummarizedOrgResults[i]["IssuesFixed"]),
890
+ str(SummarizedOrgResults[i]["Ready"]),
885
891
  )
886
892
 
887
893
  sorted_OrgResults = sorted(OrgResults, key=lambda k: (k["AccountId"], k["Region"]))
@@ -889,40 +895,37 @@ def display_results():
889
895
  for i in sorted_OrgResults:
890
896
  if pSkipAccounts is not None and i["AccountId"] in pSkipAccounts:
891
897
  y.add_row(
892
- [
893
- i["AccountId"],
894
- i["Region"],
895
- "N/A",
896
- "N/A",
897
- "N/A",
898
- "N/A",
899
- "N/A",
900
- "N/A",
901
- "N/A",
902
- "N/A",
903
- "N/A",
904
- "N/A",
905
- "Skipped",
906
- ]
898
+ i["AccountId"],
899
+ i["Region"],
900
+ "N/A",
901
+ "N/A",
902
+ "N/A",
903
+ "N/A",
904
+ "N/A",
905
+ "N/A",
906
+ "N/A",
907
+ "N/A",
908
+ "N/A",
909
+ "N/A",
910
+ "N/A",
911
+ "Skipped",
907
912
  )
908
913
  else:
909
- # x.add_row([i['AccountId'], i['Region'], i['IssuesFound'], i['IssuesFixed'], i['Ready']])
910
914
  y.add_row(
911
- [
912
- i["AccountId"],
913
- i["Region"],
914
- i["Step0"]["IssuesFound"] - i["Step0"]["IssuesFixed"],
915
- i["Step1"]["IssuesFound"] - i["Step1"]["IssuesFixed"],
916
- i["Step2"]["IssuesFound"] - i["Step2"]["IssuesFixed"],
917
- i["Step3"]["IssuesFound"] - i["Step3"]["IssuesFixed"],
918
- i["Step4"]["IssuesFound"] - i["Step4"]["IssuesFixed"],
919
- i["Step5"]["IssuesFound"] - i["Step5"]["IssuesFixed"],
920
- i["Step6"]["IssuesFound"] - i["Step6"]["IssuesFixed"],
921
- i["Step7"]["IssuesFound"] - i["Step7"]["IssuesFixed"],
922
- i["Step8"]["IssuesFound"] - i["Step8"]["IssuesFixed"],
923
- i["Step9"]["IssuesFound"] - i["Step9"]["IssuesFixed"],
924
- i["Step10"]["IssuesFound"] - i["Step10"]["IssuesFixed"],
925
- i["Step0"]["Success"]
915
+ i["AccountId"],
916
+ i["Region"],
917
+ str(i["Step0"]["IssuesFound"] - i["Step0"]["IssuesFixed"]),
918
+ str(i["Step1"]["IssuesFound"] - i["Step1"]["IssuesFixed"]),
919
+ str(i["Step2"]["IssuesFound"] - i["Step2"]["IssuesFixed"]),
920
+ str(i["Step3"]["IssuesFound"] - i["Step3"]["IssuesFixed"]),
921
+ str(i["Step4"]["IssuesFound"] - i["Step4"]["IssuesFixed"]),
922
+ str(i["Step5"]["IssuesFound"] - i["Step5"]["IssuesFixed"]),
923
+ str(i["Step6"]["IssuesFound"] - i["Step6"]["IssuesFixed"]),
924
+ str(i["Step7"]["IssuesFound"] - i["Step7"]["IssuesFixed"]),
925
+ str(i["Step8"]["IssuesFound"] - i["Step8"]["IssuesFixed"]),
926
+ str(i["Step9"]["IssuesFound"] - i["Step9"]["IssuesFixed"]),
927
+ str(i["Step10"]["IssuesFound"] - i["Step10"]["IssuesFixed"]),
928
+ str(i["Step0"]["Success"]
926
929
  and i["Step1"]["Success"]
927
930
  and i["Step2"]["Success"]
928
931
  and i["Step3"]["Success"]
@@ -932,28 +935,27 @@ def display_results():
932
935
  and i["Step7"]["Success"]
933
936
  and i["Step8"]["Success"]
934
937
  and i["Step9"]["Success"]
935
- and i["Step10"]["Success"],
936
- ]
938
+ and i["Step10"]["Success"]),
937
939
  )
938
- print(
939
- "The following table represents the accounts looked at, and whether they are ready to be incorporated into a Control Tower environment."
940
+ console.print(
941
+ "\n[bold cyan]The following table represents the accounts looked at, and whether they are ready to be incorporated into a Control Tower environment.[/bold cyan]"
940
942
  )
941
- print()
943
+ console.print()
942
944
  if aws_acct.AccountType.lower() == "root" and (
943
945
  pChildAccountList is None or aws_acct.acct_number in pChildAccountList
944
946
  ):
945
- print(
946
- f"Please note that for the Org Root account {aws_acct.acct_number}, the number of issues found for 'Org Config' will mistakenly show as 1 per region, since these issues are checked on a per-region basis."
947
+ console.print(
948
+ f"[yellow]Please note that for the Org Root account {aws_acct.acct_number}, the number of issues found for 'Org Config' will mistakenly show as 1 per region, since these issues are checked on a per-region basis.[/yellow]"
947
949
  )
948
- print(
949
- f"Additionally, issues found with 'Roles', though global, will show as regional as well. This will be remedied in future versions of this script."
950
+ console.print(
951
+ f"[yellow]Additionally, issues found with 'Roles', though global, will show as regional as well. This will be remedied in future versions of this script.[/yellow]"
950
952
  )
951
- print(x)
952
- print()
953
- print(
954
- "The following table represents the accounts looked at, and gives details under each type of issue as to what might prevent a successful migration of this account into a Control Tower environment."
953
+ console.print(x)
954
+ console.print()
955
+ console.print(
956
+ "[bold cyan]The following table represents the accounts looked at, and gives details under each type of issue as to what might prevent a successful migration of this account into a Control Tower environment.[/bold cyan]"
955
957
  )
956
- print(y)
958
+ console.print(y)
957
959
 
958
960
  if verbose < 50:
959
961
  for account in OrgResults:
@@ -961,21 +963,21 @@ def display_results():
961
963
  FixesWorked = account["IssuesFound"] - account["IssuesFixed"] == 0
962
964
  if account["Ready"] and account["IssuesFound"] == 0:
963
965
  print(
964
- f"{Fore.GREEN}**** We've found NO issues that would hinder the adoption of account {account['AccountId']} ****{Fore.RESET}"
966
+ f"[green]**** We've found NO issues that would hinder the adoption of account {account['AccountId']} ****"
965
967
  )
966
968
  elif account["Ready"] and FixesWorked:
967
969
  print(
968
- f"{Fore.GREEN}We've found and fixed{Fore.RED}",
969
- f"{account['IssuesFixed']}{Fore.RESET}",
970
- f"{Fore.GREEN}issues that would have otherwise blocked the adoption of account {account['AccountId']}{Fore.RESET}",
970
+ f"[green]We've found and fixed[red]",
971
+ f"{account['IssuesFixed']}",
972
+ f"[green]issues that would have otherwise blocked the adoption of account {account['AccountId']}",
971
973
  )
972
974
  else:
973
975
  print(
974
- f"{Fore.RED}Account # {account['AccountId']} has {account['IssuesFound'] - account['IssuesFixed']} issues that would hinder the adoption of this account{Fore.RESET}"
976
+ f"[red]Account # {account['AccountId']} has {account['IssuesFound'] - account['IssuesFixed']} issues that would hinder the adoption of this account"
975
977
  )
976
978
  for step in account:
977
979
  if step[:4] == "Step" and len(account[step]["ProblemsFound"]) > 0:
978
- print(f"{Fore.LIGHTRED_EX}Issues Found for {step} in account {account['AccountId']}:{Fore.RESET}")
980
+ print(f"{Fore.LIGHTRED_EX}Issues Found for {step} in account {account['AccountId']}:")
979
981
  pprint(account[step]["ProblemsFound"])
980
982
 
981
983
 
@@ -983,7 +985,7 @@ def setup(fProfile, fRegions):
983
985
  f_aws_acct = aws_acct_access(fProfile)
984
986
  if not f_aws_acct.Success:
985
987
  logging.error(f"Error: {f_aws_acct.ErrorType}")
986
- print(f"{Fore.RED}\nThere was an error with profile {fProfile}. Unable to continue.\n{Fore.RESET}")
988
+ print(f"[red]\nThere was an error with profile {fProfile}. Unable to continue.\n")
987
989
  sys.exit(9)
988
990
 
989
991
  if Quick:
@@ -1092,7 +1094,6 @@ SNS topic created for AWS Config -- not yet implemented
1092
1094
 
1093
1095
  """
1094
1096
 
1095
- ERASE_LINE = "\x1b[2K"
1096
1097
  begin_time = time()
1097
1098
 
1098
1099
  if __name__ == "__main__":
@@ -1102,6 +1103,6 @@ if __name__ == "__main__":
1102
1103
 
1103
1104
  if pTiming:
1104
1105
  print(ERASE_LINE)
1105
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
1106
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
1106
1107
  print("Thanks for using this script...")
1107
1108
  print()