runbooks 1.1.5__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 (62) hide show
  1. runbooks/cli/commands/inventory.py +21 -80
  2. runbooks/common/accuracy_validator.py +6 -12
  3. runbooks/common/mcp_integration.py +38 -7
  4. runbooks/common/rich_utils.py +116 -2
  5. runbooks/inventory/CLAUDE.md +1 -1
  6. runbooks/inventory/aws_decorators.py +2 -3
  7. runbooks/inventory/check_cloudtrail_compliance.py +2 -4
  8. runbooks/inventory/check_controltower_readiness.py +152 -151
  9. runbooks/inventory/check_landingzone_readiness.py +85 -84
  10. runbooks/inventory/core/formatter.py +11 -0
  11. runbooks/inventory/draw_org_structure.py +8 -9
  12. runbooks/inventory/ec2_vpc_utils.py +2 -2
  13. runbooks/inventory/find_cfn_drift_detection.py +5 -7
  14. runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
  15. runbooks/inventory/find_cfn_stackset_drift.py +5 -6
  16. runbooks/inventory/find_ec2_security_groups.py +48 -42
  17. runbooks/inventory/find_landingzone_versions.py +4 -6
  18. runbooks/inventory/find_vpc_flow_logs.py +7 -9
  19. runbooks/inventory/inventory_modules.py +103 -91
  20. runbooks/inventory/list_cfn_stacks.py +9 -10
  21. runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
  22. runbooks/inventory/list_cfn_stackset_operations.py +79 -57
  23. runbooks/inventory/list_cfn_stacksets.py +8 -10
  24. runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
  25. runbooks/inventory/list_ds_directories.py +65 -53
  26. runbooks/inventory/list_ec2_availability_zones.py +2 -4
  27. runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
  28. runbooks/inventory/list_ec2_instances.py +23 -28
  29. runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
  30. runbooks/inventory/list_elbs_load_balancers.py +22 -20
  31. runbooks/inventory/list_enis_network_interfaces.py +26 -33
  32. runbooks/inventory/list_guardduty_detectors.py +2 -4
  33. runbooks/inventory/list_iam_policies.py +2 -4
  34. runbooks/inventory/list_iam_roles.py +5 -7
  35. runbooks/inventory/list_iam_saml_providers.py +4 -6
  36. runbooks/inventory/list_lambda_functions.py +38 -38
  37. runbooks/inventory/list_org_accounts.py +6 -8
  38. runbooks/inventory/list_org_accounts_users.py +55 -44
  39. runbooks/inventory/list_rds_db_instances.py +31 -33
  40. runbooks/inventory/list_route53_hosted_zones.py +3 -5
  41. runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
  42. runbooks/inventory/list_sns_topics.py +2 -4
  43. runbooks/inventory/list_ssm_parameters.py +4 -7
  44. runbooks/inventory/list_vpc_subnets.py +2 -4
  45. runbooks/inventory/list_vpcs.py +7 -10
  46. runbooks/inventory/mcp_inventory_validator.py +5 -3
  47. runbooks/inventory/organizations_discovery.py +8 -4
  48. runbooks/inventory/recover_cfn_stack_ids.py +7 -8
  49. runbooks/inventory/requirements.txt +0 -1
  50. runbooks/inventory/rich_inventory_display.py +2 -2
  51. runbooks/inventory/run_on_multi_accounts.py +3 -5
  52. runbooks/inventory/unified_validation_engine.py +3 -2
  53. runbooks/inventory/verify_ec2_security_groups.py +1 -1
  54. runbooks/inventory/vpc_analyzer.py +3 -2
  55. runbooks/inventory/vpc_dependency_analyzer.py +2 -2
  56. runbooks/validation/terraform_drift_detector.py +16 -5
  57. {runbooks-1.1.5.dist-info → runbooks-1.1.6.dist-info}/METADATA +3 -4
  58. {runbooks-1.1.5.dist-info → runbooks-1.1.6.dist-info}/RECORD +62 -62
  59. {runbooks-1.1.5.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
  60. {runbooks-1.1.5.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
  61. {runbooks-1.1.5.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
  62. {runbooks-1.1.5.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -7,15 +7,13 @@ from time import time
7
7
 
8
8
  from ArgumentsClass import CommonArguments
9
9
  from botocore.exceptions import ClientError
10
- from colorama import Fore, init
10
+ from runbooks.common.rich_utils import console
11
11
  from Inventory_Modules import display_results, find_account_volumes2, get_all_credentials
12
- from tqdm.auto import tqdm
12
+ from runbooks.common.rich_utils import create_progress_bar
13
13
 
14
- init()
15
14
  __version__ = "2024.05.31"
16
15
 
17
16
  # ANSI escape code for clearing current line (progress bar cleanup)
18
- ERASE_LINE = "\x1b[2K"
19
17
 
20
18
 
21
19
  ##################
@@ -92,7 +90,7 @@ def present_results(fVolumesFound: list):
92
90
  # Calculate and display orphaned volumes
93
91
  orphaned_volumes = [vol for vol in de_dupe_VolumesFound if vol.get("State") == "available"]
94
92
  if orphaned_volumes:
95
- print(f"{Fore.YELLOW}Warning: {len(orphaned_volumes)} orphaned (unattached) volumes found{Fore.RESET}")
93
+ print(f"[yellow]Warning: {len(orphaned_volumes)} orphaned (unattached) volumes found")
96
94
  total_orphaned_size = sum(vol.get("Size", 0) for vol in orphaned_volumes)
97
95
  print(f"Total orphaned storage: {total_orphaned_size} GB")
98
96
 
@@ -136,9 +134,9 @@ def check_accounts_for_ebs_volumes(f_CredentialList, f_fragment_list=None):
136
134
  continue
137
135
  finally:
138
136
  logging.info(
139
- f"{ERASE_LINE}Finished finding EBS volumes in account {c_account_credentials['AccountId']} in region {c_account_credentials['Region']}"
137
+ f"Finished finding EBS volumes in account {c_account_credentials['AccountId']} in region {c_account_credentials['Region']}"
140
138
  )
141
- pbar.update()
139
+ progress.update(task, advance=1)
142
140
  self.queue.task_done()
143
141
 
144
142
  if f_fragment_list is None:
@@ -148,32 +146,31 @@ def check_accounts_for_ebs_volumes(f_CredentialList, f_fragment_list=None):
148
146
 
149
147
  checkqueue = Queue()
150
148
 
151
- pbar = tqdm(
152
- desc=f"Finding ebs volumes from {len(f_CredentialList)} accounts and regions",
153
- total=len(f_CredentialList),
154
- unit=" accounts & regions",
155
- )
156
-
157
- for x in range(WorkerThreads):
158
- worker = FindVolumes(checkqueue)
159
- # Setting daemon to True will let the main thread exit even though the workers are blocking
160
- worker.daemon = True
161
- worker.start()
162
-
163
- for credential in f_CredentialList:
164
- logging.info(f"Connecting to account {credential['AccountId']}")
165
- try:
166
- # print(f"{ERASE_LINE}Queuing account {credential['AccountId']} in region {region}", end='\r')
167
- checkqueue.put((credential, credential["Region"], f_fragment_list))
168
- except ClientError as my_Error:
169
- if "AuthFailure" in str(my_Error):
170
- logging.error(
171
- f"Authorization Failure accessing account {credential['AccountId']} in '{credential['Region']}' region"
172
- )
173
- logging.warning(f"It's possible that the region '{credential['Region']}' hasn't been opted-into")
174
- pass
175
- checkqueue.join()
176
- pbar.close()
149
+ with create_progress_bar() as progress:
150
+ task = progress.add_task(
151
+ f"Finding ebs volumes from {len(f_CredentialList)} accounts and regions",
152
+ total=len(f_CredentialList)
153
+ )
154
+
155
+ for x in range(WorkerThreads):
156
+ worker = FindVolumes(checkqueue)
157
+ # Setting daemon to True will let the main thread exit even though the workers are blocking
158
+ worker.daemon = True
159
+ worker.start()
160
+
161
+ for credential in f_CredentialList:
162
+ logging.info(f"Connecting to account {credential['AccountId']}")
163
+ try:
164
+ # print(f"Queuing account {credential['AccountId']} in region {region}", end='\r')
165
+ checkqueue.put((credential, credential["Region"], f_fragment_list))
166
+ except ClientError as my_Error:
167
+ if "AuthFailure" in str(my_Error):
168
+ logging.error(
169
+ f"Authorization Failure accessing account {credential['AccountId']} in '{credential['Region']}' region"
170
+ )
171
+ logging.warning(f"It's possible that the region '{credential['Region']}' hasn't been opted-into")
172
+ pass
173
+ checkqueue.join()
177
174
  return AllVolumes
178
175
 
179
176
 
@@ -231,8 +228,8 @@ def main():
231
228
 
232
229
  # Display execution timing if requested
233
230
  if pTiming:
234
- print(ERASE_LINE)
235
- print(f"{Fore.GREEN}This script completed in {time() - begin_time:.2f} seconds{Fore.RESET}")
231
+ console.print()
232
+ print(f"[green]This script completed in {time() - begin_time:.2f} seconds")
236
233
 
237
234
 
238
235
  if __name__ == "__main__":
@@ -62,13 +62,10 @@ from time import time
62
62
  from . import inventory_modules as Inventory_Modules
63
63
  from .ArgumentsClass import CommonArguments
64
64
  from botocore.exceptions import ClientError
65
- from colorama import Fore, init
66
65
  from .inventory_modules import display_results, get_all_credentials
67
- from tqdm.auto import tqdm
66
+ from runbooks.common.rich_utils import create_progress_bar
68
67
 
69
- init()
70
68
  __version__ = "2025.04.09"
71
- ERASE_LINE = "\x1b[2K"
72
69
  begin_time = time()
73
70
 
74
71
 
@@ -322,30 +319,28 @@ def find_all_instances(fAllCredentials: list, fStatus: str = None) -> list:
322
319
  AllInstances = []
323
320
  WorkerThreads = min(len(fAllCredentials), 25)
324
321
 
325
- pbar = tqdm(
326
- desc=f"Finding instances from {len(fAllCredentials)} locations", total=len(fAllCredentials), unit=" locations"
327
- )
328
-
329
- for x in range(WorkerThreads):
330
- worker = FindInstances(checkqueue)
331
- # Setting daemon to True will let the main thread exit even though the workers are blocking
332
- worker.daemon = True
333
- worker.start()
334
-
335
- for credential in fAllCredentials:
336
- logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
337
- try:
338
- # I don't know why - but double parens are necessary below. If you remove them, only the first parameter is queued.
339
- checkqueue.put((credential))
340
- except ClientError as my_Error:
341
- if "AuthFailure" in str(my_Error):
342
- logging.error(
343
- f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
344
- )
345
- logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
346
- pass
347
- checkqueue.join()
348
- pbar.close()
322
+ with create_progress_bar() as progress:
323
+ task = progress.add_task(f"Finding instances from {len(fAllCredentials)} locations", total=len(fAllCredentials))
324
+
325
+ for x in range(WorkerThreads):
326
+ worker = FindInstances(checkqueue)
327
+ # Setting daemon to True will let the main thread exit even though the workers are blocking
328
+ worker.daemon = True
329
+ worker.start()
330
+
331
+ for credential in fAllCredentials:
332
+ logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
333
+ try:
334
+ # I don't know why - but double parens are necessary below. If you remove them, only the first parameter is queued.
335
+ checkqueue.put((credential))
336
+ except ClientError as my_Error:
337
+ if "AuthFailure" in str(my_Error):
338
+ logging.error(
339
+ f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
340
+ )
341
+ logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
342
+ pass
343
+ checkqueue.join()
349
344
  return AllInstances
350
345
 
351
346
 
@@ -90,13 +90,11 @@ from time import time
90
90
  import Inventory_Modules
91
91
  from ArgumentsClass import CommonArguments
92
92
  from botocore.exceptions import ClientError
93
- from colorama import Fore, init
93
+ from runbooks.common.rich_utils import console
94
94
  from Inventory_Modules import display_results, find_account_ecs_clusters_services_and_tasks2, get_all_credentials
95
- from tqdm.auto import tqdm
95
+ from runbooks.common.rich_utils import create_progress_bar
96
96
 
97
- init()
98
97
  __version__ = "2024.09.06"
99
- ERASE_LINE = "\x1b[2K"
100
98
  begin_time = time()
101
99
 
102
100
  # TODO: Need a table at the bottom that summarizes the results, by instance-type, by running/ stopped, maybe by account and region
@@ -246,7 +244,7 @@ def find_all_clusters_and_tasks(fAllCredentials: list, fStatus: str = None) -> l
246
244
  """
247
245
  while True:
248
246
  # Retrieve ECS discovery work item from thread-safe queue
249
- c_account_credentials = self.queue.get()
247
+ c_account_credentials, c_progress, c_task = self.queue.get()
250
248
  logging.info(f"De-queued info for account number {c_account_credentials['AccountId']}")
251
249
 
252
250
  try:
@@ -406,7 +404,7 @@ def find_all_clusters_and_tasks(fAllCredentials: list, fStatus: str = None) -> l
406
404
  continue
407
405
  finally:
408
406
  # Ensure progress tracking and queue management regardless of success/failure
409
- pbar.update()
407
+ c_progress.update(c_task, advance=1)
410
408
  self.queue.task_done()
411
409
 
412
410
  ###########
@@ -423,13 +421,6 @@ def find_all_clusters_and_tasks(fAllCredentials: list, fStatus: str = None) -> l
423
421
  # Maximum 25 threads to prevent overwhelming AWS APIs while maintaining efficiency
424
422
  WorkerThreads = min(len(fAllCredentials), 25)
425
423
 
426
- # Initialize progress tracking for operational visibility during large-scale operations
427
- pbar = tqdm(
428
- desc=f"Finding ECS clusters, services and tasks from {len(fAllCredentials)} accounts / regions",
429
- total=len(fAllCredentials),
430
- unit=" locations",
431
- )
432
-
433
424
  # Start worker threads for concurrent ECS resource discovery
434
425
  for x in range(WorkerThreads):
435
426
  worker = FindInstances(checkqueue)
@@ -437,25 +428,26 @@ def find_all_clusters_and_tasks(fAllCredentials: list, fStatus: str = None) -> l
437
428
  worker.daemon = True
438
429
  worker.start()
439
430
 
440
- # Queue credential sets for processing by worker threads
441
- for credential in fAllCredentials:
442
- logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
443
- try:
444
- # Queue individual credential set for ECS resource discovery
445
- # Note: Single parameter queuing - credential dictionary contains all needed info
446
- checkqueue.put(credential)
447
- except ClientError as my_Error:
448
- # Handle authorization failures during credential queuing
449
- if "AuthFailure" in str(my_Error):
450
- logging.error(
451
- f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
452
- )
453
- logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
454
- pass
455
-
456
- # Wait for all queued work to complete before proceeding
457
- checkqueue.join()
458
- pbar.close()
431
+ # Queue credential sets for processing by worker threads with progress tracking
432
+ with create_progress_bar() as progress:
433
+ task = progress.add_task("Discovering ECS clusters, services and tasks", total=len(fAllCredentials))
434
+ for credential in fAllCredentials:
435
+ logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
436
+ try:
437
+ # Queue individual credential set for ECS resource discovery with progress tracking
438
+ # Tuple format: (credential, progress, task)
439
+ checkqueue.put((credential, progress, task))
440
+ except ClientError as my_Error:
441
+ # Handle authorization failures during credential queuing
442
+ if "AuthFailure" in str(my_Error):
443
+ logging.error(
444
+ f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
445
+ )
446
+ logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
447
+ pass
448
+
449
+ # Wait for all queued work to complete before proceeding
450
+ checkqueue.join()
459
451
  return AllInstances
460
452
 
461
453
 
@@ -514,7 +506,7 @@ if __name__ == "__main__":
514
506
  print()
515
507
  milestone_time1 = time()
516
508
  print(
517
- f"{Fore.GREEN}\t\tCredential discovery and region enumeration took: {(milestone_time1 - begin_time):.3f} seconds{Fore.RESET}"
509
+ f"[green]\t\tCredential discovery and region enumeration took: {(milestone_time1 - begin_time):.3f} seconds"
518
510
  )
519
511
  print()
520
512
 
@@ -549,7 +541,7 @@ if __name__ == "__main__":
549
541
  # Display performance timing metrics for operational optimization and SLA compliance
550
542
  if pTiming:
551
543
  print(ERASE_LINE)
552
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
544
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
553
545
 
554
546
  print(ERASE_LINE)
555
547
 
@@ -88,13 +88,11 @@ from time import time
88
88
 
89
89
  from ArgumentsClass import CommonArguments
90
90
  from botocore.exceptions import ClientError
91
- from colorama import Fore, init
91
+ from runbooks.common.rich_utils import console
92
92
  from Inventory_Modules import display_results, find_load_balancers2, get_all_credentials
93
- from tqdm.auto import tqdm
93
+ from runbooks.common.rich_utils import create_progress_bar
94
94
 
95
- init()
96
95
  __version__ = "2024.05.06"
97
- ERASE_LINE = "\x1b[2K"
98
96
  begin_time = time()
99
97
 
100
98
 
@@ -306,20 +304,24 @@ def find_all_elbs(fAllCredentials: list, ffragment: list, fstatus: str):
306
304
  worker.start()
307
305
 
308
306
  # Queue credential sets with progress tracking for operational visibility
309
- for credential in tqdm(fAllCredentials):
310
- logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
311
- try:
312
- # Queue credential set with fragment and status filters for targeted discovery
313
- # Tuple format: (credentials, fragment_filter, status_filter)
314
- checkqueue.put((credential, ffragment, fstatus))
315
- except ClientError as my_Error:
316
- # Handle authorization failures during credential queuing
317
- if "AuthFailure" in str(my_Error):
318
- logging.error(
319
- f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
320
- )
321
- logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
322
- pass
307
+ with create_progress_bar() as progress:
308
+ task = progress.add_task("Queueing ELB discovery tasks", total=len(fAllCredentials))
309
+ for credential in fAllCredentials:
310
+ logging.info(f"Beginning to queue data - starting with {credential['AccountId']}")
311
+ try:
312
+ # Queue credential set with fragment and status filters for targeted discovery
313
+ # Tuple format: (credentials, fragment_filter, status_filter)
314
+ checkqueue.put((credential, ffragment, fstatus))
315
+ except ClientError as my_Error:
316
+ # Handle authorization failures during credential queuing
317
+ if "AuthFailure" in str(my_Error):
318
+ logging.error(
319
+ f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
320
+ )
321
+ logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
322
+ pass
323
+ finally:
324
+ progress.update(task, advance=1)
323
325
 
324
326
  # Wait for all queued work to complete before proceeding
325
327
  checkqueue.join()
@@ -396,13 +398,13 @@ if __name__ == "__main__":
396
398
  # Display performance timing metrics for operational optimization and SLA compliance
397
399
  if pTiming:
398
400
  print(ERASE_LINE)
399
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
401
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
400
402
 
401
403
  print(ERASE_LINE)
402
404
 
403
405
  # Display comprehensive operational summary for executive traffic management reporting
404
406
  print(
405
- f"{Fore.RED}Found {len(All_Load_Balancers)} Load Balancers across {AccountNum} profiles across {RegionNum} regions{Fore.RESET}"
407
+ f"[red]Found {len(All_Load_Balancers)} Load Balancers across {AccountNum} profiles across {RegionNum} regions"
406
408
  )
407
409
  print()
408
410
 
@@ -96,11 +96,10 @@ from ArgumentsClass import CommonArguments
96
96
  from botocore.exceptions import ClientError
97
97
 
98
98
  # from datetime import datetime
99
- from colorama import Fore, init
99
+ from runbooks.common.rich_utils import console
100
100
  from Inventory_Modules import display_results, find_account_enis2, get_all_credentials
101
- from tqdm.auto import tqdm
101
+ from runbooks.common.rich_utils import create_progress_bar
102
102
 
103
- init()
104
103
 
105
104
  __version__ = "2024.10.24"
106
105
 
@@ -269,8 +268,8 @@ def check_accounts_for_enis(fCredentialList, fip=None, fPublicOnly: bool = False
269
268
  """
270
269
  while True:
271
270
  # Retrieve ENI discovery work item from thread-safe queue
272
- c_account_credentials, c_region, c_fip, c_PlacesToLook, c_PlaceCount = self.queue.get()
273
- pbar.update() # Update progress tracking for operational visibility
271
+ c_account_credentials, c_region, c_fip, c_PlacesToLook, c_PlaceCount, c_progress, c_task = self.queue.get()
272
+ c_progress.update(c_task, advance=1) # Update progress tracking for operational visibility
274
273
  logging.info(f"De-queued info for account {c_account_credentials['AccountId']}")
275
274
 
276
275
  try:
@@ -323,13 +322,6 @@ def check_accounts_for_enis(fCredentialList, fip=None, fPublicOnly: bool = False
323
322
  # Initialize queue-based threading architecture for scalable ENI discovery
324
323
  checkqueue = Queue()
325
324
 
326
- # Initialize progress tracking for operational visibility during large-scale operations
327
- pbar = tqdm(
328
- desc=f"Finding enis from {len(CredentialList)} accounts / regions",
329
- total=len(fCredentialList),
330
- unit=" locations",
331
- )
332
-
333
325
  # Initialize results list for aggregating discovered ENIs
334
326
  Results = []
335
327
  PlaceCount = 0
@@ -347,24 +339,26 @@ def check_accounts_for_enis(fCredentialList, fip=None, fPublicOnly: bool = False
347
339
  worker.start()
348
340
 
349
341
  # Queue credential sets for processing by worker threads
350
- for credential in fCredentialList:
351
- logging.info(f"Connecting to account {credential['AccountId']} in region {credential['Region']}")
352
- try:
353
- # Queue credential set with IP filter and progress tracking parameters
354
- # Tuple format: (credentials, region, ip_filter, total_places, current_count)
355
- checkqueue.put((credential, credential["Region"], fip, PlacesToLook, PlaceCount))
356
- PlaceCount += 1
357
- except ClientError as my_Error:
358
- # Handle authorization failures during credential queuing
359
- if "AuthFailure" in str(my_Error):
360
- logging.error(
361
- f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
362
- )
363
- logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
364
- pass
365
-
366
- # Wait for all queued work to complete before proceeding
367
- checkqueue.join()
342
+ with create_progress_bar() as progress:
343
+ task = progress.add_task("Discovering ENIs", total=len(fCredentialList))
344
+ for credential in fCredentialList:
345
+ logging.info(f"Connecting to account {credential['AccountId']} in region {credential['Region']}")
346
+ try:
347
+ # Queue credential set with IP filter and progress tracking parameters
348
+ # Tuple format: (credentials, region, ip_filter, total_places, current_count, progress, task)
349
+ checkqueue.put((credential, credential["Region"], fip, PlacesToLook, PlaceCount, progress, task))
350
+ PlaceCount += 1
351
+ except ClientError as my_Error:
352
+ # Handle authorization failures during credential queuing
353
+ if "AuthFailure" in str(my_Error):
354
+ logging.error(
355
+ f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
356
+ )
357
+ logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
358
+ pass
359
+
360
+ # Wait for all queued work to complete before proceeding
361
+ checkqueue.join()
368
362
  return Results
369
363
 
370
364
 
@@ -450,7 +444,7 @@ def present_results(f_ENIsFound: list):
450
444
 
451
445
  # Highlight cost optimization opportunities with unused ENI identification
452
446
  print(
453
- f"{Fore.RED}Found {len(DetachedENIs)} ENIs that are not listed as 'in-use' and may therefore be costing you additional money while they're unused.{Fore.RESET}"
447
+ f"[red]Found {len(DetachedENIs)} ENIs that are not listed as 'in-use' and may therefore be costing you additional money while they're unused."
454
448
  ) if DetachedENIs else ""
455
449
  print()
456
450
 
@@ -464,7 +458,6 @@ def present_results(f_ENIsFound: list):
464
458
  # Main execution entry point for enterprise ENI discovery and network security analysis
465
459
  ##################
466
460
 
467
- ERASE_LINE = "\x1b[2K"
468
461
 
469
462
  if __name__ == "__main__":
470
463
  """
@@ -518,7 +511,7 @@ if __name__ == "__main__":
518
511
 
519
512
  # Display performance timing metrics for operational optimization and SLA compliance
520
513
  if pTiming:
521
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
514
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
522
515
 
523
516
  # Display completion message for user confirmation and operational closure
524
517
  print()
@@ -95,10 +95,9 @@ import Inventory_Modules
95
95
  from account_class import aws_acct_access
96
96
  from ArgumentsClass import CommonArguments
97
97
  from botocore.exceptions import ClientError
98
- from colorama import Fore, init
98
+ from runbooks.common.rich_utils import console
99
99
  from Inventory_Modules import get_all_credentials
100
100
 
101
- init()
102
101
  __version__ = "2023.07.18"
103
102
 
104
103
  # Parse enterprise command-line arguments with GuardDuty-specific security management options
@@ -150,7 +149,6 @@ logging.getLogger("urllib3").setLevel(logging.CRITICAL)
150
149
  # Initialize enterprise GuardDuty discovery and security management operations
151
150
  ##########################
152
151
 
153
- ERASE_LINE = "\x1b[2K"
154
152
 
155
153
  # Initialize AWS account access for GuardDuty administrative operations
156
154
  aws_acct = aws_acct_access(pProfile)
@@ -337,7 +335,7 @@ for credential in AllCredentials:
337
335
  # Display progress for accounts without GuardDuty detectors for operational visibility
338
336
  print(
339
337
  ERASE_LINE,
340
- f"{Fore.RED}No luck in account: {credential['AccountId']} in region {credential['Region']}{Fore.RESET} -- {places_to_try} of {len(AllCredentials)}",
338
+ f"[red]No luck in account: {credential['AccountId']} in region {credential['Region']} -- {places_to_try} of {len(AllCredentials)}",
341
339
  end="\r",
342
340
  )
343
341
  except ClientError as my_Error:
@@ -94,12 +94,10 @@ from time import time
94
94
 
95
95
  from ArgumentsClass import CommonArguments
96
96
  from botocore.exceptions import ClientError
97
- from colorama import Fore, init
97
+ from runbooks.common.rich_utils import console
98
98
  from Inventory_Modules import display_results, find_account_policies2, find_policy_action2, get_all_credentials
99
99
 
100
- init()
101
100
  __version__ = "2023.12.12"
102
- ERASE_LINE = "\x1b[2K"
103
101
  begin_time = time()
104
102
 
105
103
 
@@ -392,7 +390,7 @@ if __name__ == "__main__":
392
390
 
393
391
  if pTiming:
394
392
  print(ERASE_LINE)
395
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
393
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
396
394
  print(f"These accounts were skipped - as requested: {pSkipAccounts}") if pSkipAccounts is not None else print()
397
395
  print()
398
396
  print(
@@ -43,10 +43,9 @@ from time import sleep, time
43
43
  import boto3
44
44
  from ArgumentsClass import CommonArguments
45
45
  from botocore.exceptions import ClientError
46
- from colorama import Fore, init
47
46
  from Inventory_Modules import display_results, find_in, get_all_credentials
47
+ from runbooks.common.rich_utils import console
48
48
 
49
- init()
50
49
  __version__ = "2023.11.06"
51
50
 
52
51
 
@@ -249,8 +248,8 @@ def find_and_collect_roles_across_accounts(fAllCredentials: list, frole_fragment
249
248
  print(f"Listing out all roles across {len(fAllCredentials)} accounts")
250
249
  print()
251
250
  elif pExact:
252
- print(
253
- f"Looking for a role {Fore.RED}exactly{Fore.RESET} named one of these strings {frole_fragments} across {len(fAllCredentials)} accounts"
251
+ console.print(
252
+ f"Looking for a role [red]exactly[/red] named one of these strings {frole_fragments} across {len(fAllCredentials)} accounts"
254
253
  )
255
254
  print()
256
255
  else:
@@ -475,7 +474,6 @@ if __name__ == "__main__":
475
474
  logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
476
475
  logging.getLogger("urllib3").setLevel(logging.CRITICAL)
477
476
 
478
- ERASE_LINE = "\x1b[2K"
479
477
  time_to_sleep = 5
480
478
  begin_time = time()
481
479
 
@@ -511,8 +509,8 @@ if __name__ == "__main__":
511
509
  )
512
510
 
513
511
  if pTiming:
514
- print(ERASE_LINE)
515
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
512
+ console.print()
513
+ console.print(f"[green]This script took {time() - begin_time:.2f} seconds[/green]")
516
514
  print()
517
515
  print("Thanks for using this script...")
518
516
  print()
@@ -101,14 +101,12 @@ import boto3
101
101
  from account_class import aws_acct_access
102
102
  from ArgumentsClass import CommonArguments
103
103
  from botocore.exceptions import ClientError
104
- from colorama import Fore, init
104
+ from runbooks.common.rich_utils import console
105
105
  from Inventory_Modules import display_results, find_saml_components_in_acct2, get_child_access3
106
106
 
107
- init()
108
107
  __version__ = "2024.03.27"
109
108
 
110
109
  begin_time = time()
111
- ERASE_LINE = "\x1b[2K"
112
110
 
113
111
 
114
112
  ##################
@@ -254,7 +252,7 @@ def all_my_saml_providers(faws_acct: aws_acct_access, fChildAccounts: list, f_ac
254
252
  idpNum = len(Idps)
255
253
  logging.info(f"Account: {account['AccountId']} | Region: {pRegion} | Found {idpNum} Idps")
256
254
  logging.info(
257
- f"{ERASE_LINE}{Fore.RED}Account: {account['AccountId']} pRegion: {pRegion} Found {idpNum} Idps.{Fore.RESET}"
255
+ f"{ERASE_LINE}[red]Account: {account['AccountId']} pRegion: {pRegion} Found {idpNum} Idps."
258
256
  )
259
257
 
260
258
  # Process discovered identity providers and extract metadata
@@ -342,7 +340,7 @@ if __name__ == "__main__":
342
340
  RegionsFound = list(set([x["Region"] for x in IdpsFound]))
343
341
  print()
344
342
  print(
345
- f"{Fore.RED}Found {len(IdpsFound)} Idps across {len(AccountsFound)} accounts in {len(RegionsFound)} regions{Fore.RESET}"
343
+ f"[red]Found {len(IdpsFound)} Idps across {len(AccountsFound)} accounts in {len(RegionsFound)} regions"
346
344
  )
347
345
  print()
348
346
 
@@ -353,7 +351,7 @@ if __name__ == "__main__":
353
351
 
354
352
  print()
355
353
  if pTiming:
356
- print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
354
+ print(f"[green]This script took {time() - begin_time:.2f} seconds")
357
355
  print()
358
356
  print("Thanks for using this script...")
359
357
  print()