runbooks 0.2.5__py3-none-any.whl → 0.6.1__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 (221) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env.template +2 -0
  3. jupyter-agent/.gitattributes +35 -0
  4. jupyter-agent/README.md +16 -0
  5. jupyter-agent/app.py +256 -0
  6. jupyter-agent/cloudops-agent.png +0 -0
  7. jupyter-agent/ds-system-prompt.txt +154 -0
  8. jupyter-agent/jupyter-agent.png +0 -0
  9. jupyter-agent/llama3_template.jinja +123 -0
  10. jupyter-agent/requirements.txt +9 -0
  11. jupyter-agent/utils.py +409 -0
  12. runbooks/__init__.py +71 -3
  13. runbooks/__main__.py +13 -0
  14. runbooks/aws/ec2_describe_instances.py +1 -1
  15. runbooks/aws/ec2_run_instances.py +8 -2
  16. runbooks/aws/ec2_start_stop_instances.py +17 -4
  17. runbooks/aws/ec2_unused_volumes.py +5 -1
  18. runbooks/aws/s3_create_bucket.py +4 -2
  19. runbooks/aws/s3_list_objects.py +6 -1
  20. runbooks/aws/tagging_lambda_handler.py +13 -2
  21. runbooks/aws/tags.json +12 -0
  22. runbooks/base.py +353 -0
  23. runbooks/cfat/README.md +49 -0
  24. runbooks/cfat/__init__.py +74 -0
  25. runbooks/cfat/app.ts +644 -0
  26. runbooks/cfat/assessment/__init__.py +40 -0
  27. runbooks/cfat/assessment/asana-import.csv +39 -0
  28. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  29. runbooks/cfat/assessment/cfat.txt +520 -0
  30. runbooks/cfat/assessment/collectors.py +200 -0
  31. runbooks/cfat/assessment/jira-import.csv +39 -0
  32. runbooks/cfat/assessment/runner.py +387 -0
  33. runbooks/cfat/assessment/validators.py +290 -0
  34. runbooks/cfat/cli.py +103 -0
  35. runbooks/cfat/docs/asana-import.csv +24 -0
  36. runbooks/cfat/docs/cfat-checks.csv +31 -0
  37. runbooks/cfat/docs/cfat.txt +335 -0
  38. runbooks/cfat/docs/checks-output.png +0 -0
  39. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  40. runbooks/cfat/docs/cloudshell-download.png +0 -0
  41. runbooks/cfat/docs/cloudshell-output.png +0 -0
  42. runbooks/cfat/docs/downloadfile.png +0 -0
  43. runbooks/cfat/docs/jira-import.csv +24 -0
  44. runbooks/cfat/docs/open-cloudshell.png +0 -0
  45. runbooks/cfat/docs/report-header.png +0 -0
  46. runbooks/cfat/models.py +1026 -0
  47. runbooks/cfat/package-lock.json +5116 -0
  48. runbooks/cfat/package.json +38 -0
  49. runbooks/cfat/report.py +496 -0
  50. runbooks/cfat/reporting/__init__.py +46 -0
  51. runbooks/cfat/reporting/exporters.py +337 -0
  52. runbooks/cfat/reporting/formatters.py +496 -0
  53. runbooks/cfat/reporting/templates.py +135 -0
  54. runbooks/cfat/run-assessment.sh +23 -0
  55. runbooks/cfat/runner.py +69 -0
  56. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  57. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  58. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  59. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  60. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  61. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  62. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  63. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  64. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  65. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  66. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  67. runbooks/cfat/src/actions/create-report.ts +616 -0
  68. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  69. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  70. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  71. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  72. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  73. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  74. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  75. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  76. runbooks/cfat/src/actions/get-regions.ts +22 -0
  77. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  78. runbooks/cfat/src/types/index.d.ts +147 -0
  79. runbooks/cfat/tests/__init__.py +141 -0
  80. runbooks/cfat/tests/test_cli.py +340 -0
  81. runbooks/cfat/tests/test_integration.py +290 -0
  82. runbooks/cfat/tests/test_models.py +505 -0
  83. runbooks/cfat/tests/test_reporting.py +354 -0
  84. runbooks/cfat/tsconfig.json +16 -0
  85. runbooks/cfat/webpack.config.cjs +27 -0
  86. runbooks/config.py +260 -0
  87. runbooks/finops/__init__.py +88 -0
  88. runbooks/finops/aws_client.py +245 -0
  89. runbooks/finops/cli.py +151 -0
  90. runbooks/finops/cost_processor.py +410 -0
  91. runbooks/finops/dashboard_runner.py +448 -0
  92. runbooks/finops/helpers.py +355 -0
  93. runbooks/finops/main.py +14 -0
  94. runbooks/finops/profile_processor.py +174 -0
  95. runbooks/finops/types.py +66 -0
  96. runbooks/finops/visualisations.py +80 -0
  97. runbooks/inventory/.gitignore +354 -0
  98. runbooks/inventory/ArgumentsClass.py +261 -0
  99. runbooks/inventory/Inventory_Modules.py +6130 -0
  100. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  101. runbooks/inventory/README.md +1320 -0
  102. runbooks/inventory/__init__.py +62 -0
  103. runbooks/inventory/account_class.py +532 -0
  104. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  105. runbooks/inventory/aws_decorators.py +201 -0
  106. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  107. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  108. runbooks/inventory/check_controltower_readiness.py +1107 -0
  109. runbooks/inventory/check_landingzone_readiness.py +711 -0
  110. runbooks/inventory/cloudtrail.md +727 -0
  111. runbooks/inventory/collectors/__init__.py +20 -0
  112. runbooks/inventory/collectors/aws_compute.py +518 -0
  113. runbooks/inventory/collectors/aws_networking.py +275 -0
  114. runbooks/inventory/collectors/base.py +222 -0
  115. runbooks/inventory/core/__init__.py +19 -0
  116. runbooks/inventory/core/collector.py +303 -0
  117. runbooks/inventory/core/formatter.py +296 -0
  118. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  119. runbooks/inventory/discovery.md +81 -0
  120. runbooks/inventory/draw_org_structure.py +748 -0
  121. runbooks/inventory/ec2_vpc_utils.py +341 -0
  122. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  123. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  124. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  125. runbooks/inventory/find_ec2_security_groups.py +669 -0
  126. runbooks/inventory/find_landingzone_versions.py +201 -0
  127. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  128. runbooks/inventory/inventory.sh +659 -0
  129. runbooks/inventory/list_cfn_stacks.py +558 -0
  130. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  131. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  132. runbooks/inventory/list_cfn_stacksets.py +453 -0
  133. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  134. runbooks/inventory/list_ds_directories.py +354 -0
  135. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  136. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  137. runbooks/inventory/list_ec2_instances.py +425 -0
  138. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  139. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  140. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  141. runbooks/inventory/list_guardduty_detectors.py +568 -0
  142. runbooks/inventory/list_iam_policies.py +404 -0
  143. runbooks/inventory/list_iam_roles.py +518 -0
  144. runbooks/inventory/list_iam_saml_providers.py +359 -0
  145. runbooks/inventory/list_lambda_functions.py +882 -0
  146. runbooks/inventory/list_org_accounts.py +446 -0
  147. runbooks/inventory/list_org_accounts_users.py +354 -0
  148. runbooks/inventory/list_rds_db_instances.py +406 -0
  149. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  150. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  151. runbooks/inventory/list_sns_topics.py +360 -0
  152. runbooks/inventory/list_ssm_parameters.py +402 -0
  153. runbooks/inventory/list_vpc_subnets.py +433 -0
  154. runbooks/inventory/list_vpcs.py +422 -0
  155. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  156. runbooks/inventory/models/__init__.py +24 -0
  157. runbooks/inventory/models/account.py +192 -0
  158. runbooks/inventory/models/inventory.py +309 -0
  159. runbooks/inventory/models/resource.py +247 -0
  160. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  161. runbooks/inventory/requirements.txt +12 -0
  162. runbooks/inventory/run_on_multi_accounts.py +211 -0
  163. runbooks/inventory/tests/common_test_data.py +3661 -0
  164. runbooks/inventory/tests/common_test_functions.py +204 -0
  165. runbooks/inventory/tests/script_test_data.py +0 -0
  166. runbooks/inventory/tests/setup.py +24 -0
  167. runbooks/inventory/tests/src.py +18 -0
  168. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  169. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  170. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  171. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  172. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  173. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  174. runbooks/inventory/update_aws_actions.py +173 -0
  175. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  176. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  177. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  178. runbooks/inventory/update_s3_public_access_block.py +539 -0
  179. runbooks/inventory/utils/__init__.py +23 -0
  180. runbooks/inventory/utils/aws_helpers.py +510 -0
  181. runbooks/inventory/utils/threading_utils.py +493 -0
  182. runbooks/inventory/utils/validation.py +682 -0
  183. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  184. runbooks/main.py +785 -0
  185. runbooks/organizations/__init__.py +12 -0
  186. runbooks/organizations/manager.py +374 -0
  187. runbooks/security_baseline/README.md +324 -0
  188. runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
  189. runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
  190. runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
  191. runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
  192. runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
  193. runbooks/security_baseline/checklist/root_access_key.py +6 -1
  194. runbooks/security_baseline/config-origin.json +1 -1
  195. runbooks/security_baseline/config.json +1 -1
  196. runbooks/security_baseline/permission.json +1 -1
  197. runbooks/security_baseline/report_generator.py +10 -2
  198. runbooks/security_baseline/report_template_en.html +7 -7
  199. runbooks/security_baseline/report_template_jp.html +7 -7
  200. runbooks/security_baseline/report_template_kr.html +12 -12
  201. runbooks/security_baseline/report_template_vn.html +7 -7
  202. runbooks/security_baseline/requirements.txt +7 -0
  203. runbooks/security_baseline/run_script.py +8 -2
  204. runbooks/security_baseline/security_baseline_tester.py +10 -2
  205. runbooks/security_baseline/utils/common.py +5 -1
  206. runbooks/utils/__init__.py +204 -0
  207. runbooks-0.6.1.dist-info/METADATA +373 -0
  208. runbooks-0.6.1.dist-info/RECORD +237 -0
  209. {runbooks-0.2.5.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
  210. runbooks-0.6.1.dist-info/entry_points.txt +7 -0
  211. runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
  212. runbooks-0.6.1.dist-info/top_level.txt +3 -0
  213. runbooks/python101/calculator.py +0 -34
  214. runbooks/python101/config.py +0 -1
  215. runbooks/python101/exceptions.py +0 -16
  216. runbooks/python101/file_manager.py +0 -218
  217. runbooks/python101/toolkit.py +0 -153
  218. runbooks-0.2.5.dist-info/METADATA +0 -439
  219. runbooks-0.2.5.dist-info/RECORD +0 -61
  220. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  221. runbooks-0.2.5.dist-info/top_level.txt +0 -1
@@ -0,0 +1,433 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ AWS VPC Subnet Inventory and IP Utilization Analysis Script
5
+
6
+ This script provides comprehensive discovery, analysis, and reporting capabilities for
7
+ AWS VPC subnets across multiple accounts and regions. It's designed for enterprise
8
+ networking teams who need visibility into subnet utilization, IP address allocation,
9
+ and network architecture across large-scale AWS deployments.
10
+
11
+ Key Features:
12
+ - Multi-account VPC subnet discovery using assume role capabilities
13
+ - Multi-region scanning with configurable region targeting
14
+ - IP address allocation tracking and utilization analysis
15
+ - Subnet exhaustion detection with configurable thresholds
16
+ - Parallel processing with optimized threading for large environments
17
+ - IP address search capabilities for network troubleshooting
18
+ - Comprehensive subnet metadata extraction and reporting
19
+ - Enterprise reporting with CSV export and structured output
20
+ - Profile-based authentication with support for federated access
21
+
22
+ Enterprise Use Cases:
23
+ - Network capacity planning and IP address management (IPAM)
24
+ - Subnet utilization monitoring and exhaustion prevention
25
+ - Multi-account network architecture documentation
26
+ - IP address conflict detection and resolution
27
+ - Network security auditing and compliance reporting
28
+ - Cost optimization through subnet consolidation analysis
29
+ - Disaster recovery planning for network infrastructure
30
+
31
+ Network Analysis Features:
32
+ - IP utilization percentage calculation with exhaustion alerts
33
+ - Available IP address counting across subnet ranges
34
+ - VPC-level aggregation for architectural analysis
35
+ - CIDR block overlap detection and validation
36
+ - Network segmentation compliance verification
37
+ - Subnet naming convention analysis and standardization
38
+
39
+ Security Considerations:
40
+ - Uses IAM assume role capabilities for cross-account access
41
+ - Implements proper error handling for authorization failures
42
+ - Supports read-only operations with no network modification capabilities
43
+ - Respects VPC and subnet access permissions
44
+ - Provides comprehensive audit trail through detailed logging
45
+
46
+ Performance Optimizations:
47
+ - Multi-threaded processing with configurable worker pools
48
+ - Queue-based work distribution for load balancing
49
+ - Progress tracking with real-time feedback
50
+ - API rate limiting respect to prevent throttling
51
+ - Efficient memory usage for large subnet inventories
52
+
53
+ Future Enhancements:
54
+ - Elastic IP address integration for complete IP visibility
55
+ - Network ACL and security group association analysis
56
+ - Route table mapping and traffic flow analysis
57
+ - ENI (Elastic Network Interface) inventory integration
58
+
59
+ Dependencies:
60
+ - boto3/botocore for AWS VPC API interactions
61
+ - Threading support for concurrent processing
62
+ - Inventory_Modules for common utility functions
63
+ - ArgumentsClass for standardized CLI argument parsing
64
+
65
+ Author: AWS CloudOps Team
66
+ Version: 2024.10.24
67
+ """
68
+
69
+ import logging
70
+ import os
71
+ import sys
72
+ from queue import Queue
73
+ from threading import Thread
74
+ from time import time
75
+
76
+ import Inventory_Modules
77
+ from ArgumentsClass import CommonArguments
78
+ from botocore.exceptions import ClientError
79
+ from colorama import Fore, init
80
+ from Inventory_Modules import display_results, get_all_credentials
81
+
82
+ init()
83
+ __version__ = "2024.10.24"
84
+
85
+ # TODO: Add Elastic IPs to this script as well.
86
+
87
+
88
+ ##################
89
+ # Functions
90
+ ##################
91
+
92
+
93
+ def parse_args(f_args):
94
+ """
95
+ Parse command line arguments for VPC subnet inventory and IP analysis operations.
96
+
97
+ Configures comprehensive argument parsing for multi-account, multi-region VPC subnet
98
+ discovery with specialized support for IP address searching and network analysis.
99
+ Supports enterprise networking patterns with profile management, region targeting,
100
+ and operational customization for large-scale network environments.
101
+
102
+ Args:
103
+ f_args (list): Command line arguments from sys.argv[1:]
104
+
105
+ Returns:
106
+ argparse.Namespace: Parsed arguments containing:
107
+ - Profiles: List of AWS profiles to process
108
+ - Regions: Target regions for subnet discovery
109
+ - SkipProfiles/SkipAccounts: Exclusion filters
110
+ - RootOnly: Limit to organization root accounts
111
+ - AccessRoles: IAM roles for cross-account access
112
+ - pipaddresses: Specific IP addresses to search for
113
+ - Filename: Output file for CSV export
114
+ - Time: Enable performance timing metrics
115
+ - loglevel: Logging verbosity configuration
116
+
117
+ Configuration Options:
118
+ - Multi-region scanning with region filters
119
+ - Multi-profile support for federated access
120
+ - Extended arguments for advanced filtering
121
+ - Root-only mode for organization-level inventory
122
+ - Role-based access for cross-account operations
123
+ - IP address search for network troubleshooting
124
+ - File output for integration and reporting
125
+ - Timing metrics for performance optimization
126
+ - Verbose logging for debugging and audit
127
+
128
+ Network-Specific Arguments:
129
+ --ipaddress/--ip: Enables targeted IP address search across all discovered
130
+ subnets for network troubleshooting and IP conflict detection.
131
+ Supports multiple IP addresses for batch operations.
132
+
133
+ Enterprise Features:
134
+ - Cross-account role assumption for organizational visibility
135
+ - Configurable region targeting for regulatory compliance
136
+ - Profile-based access controls for security boundaries
137
+ - Comprehensive logging for network audit requirements
138
+ """
139
+ script_path, script_name = os.path.split(sys.argv[0])
140
+ parser = CommonArguments()
141
+ parser.multiprofile()
142
+ parser.multiregion()
143
+ parser.extendedargs()
144
+ parser.rootOnly()
145
+ parser.rolestouse()
146
+ parser.save_to_file()
147
+ parser.timing()
148
+ parser.verbosity()
149
+ parser.version(__version__)
150
+ local = parser.my_parser.add_argument_group(script_name, "Parameters specific to this script")
151
+ local.add_argument(
152
+ "--ipaddress",
153
+ "--ip",
154
+ dest="pipaddresses",
155
+ nargs="*",
156
+ metavar="IP address",
157
+ default=None,
158
+ help="IP address(es) you're looking for within your VPCs",
159
+ )
160
+ return parser.my_parser.parse_args(f_args)
161
+
162
+
163
+ def check_accounts_for_subnets(CredentialList, fip=None):
164
+ """
165
+ Discover and analyze VPC subnets across multiple AWS accounts and regions with parallel processing.
166
+
167
+ Implements high-performance multi-threaded processing to efficiently scan large-scale AWS
168
+ environments for VPC subnets with optional IP address filtering. Uses optimized worker
169
+ pools to maximize throughput while respecting AWS API limits and providing real-time
170
+ progress feedback for operational visibility.
171
+
172
+ Args:
173
+ CredentialList (list): List of credential dictionaries containing:
174
+ - AccountId: AWS account identifier
175
+ - MgmtAccount: Organization management account
176
+ - Region: Target AWS region for scanning
177
+ - Credentials: Temporary AWS credentials for API access
178
+ fip (list, optional): List of IP addresses to search for within subnets
179
+
180
+ Returns:
181
+ list: Aggregated collection of subnet records with structure:
182
+ - MgmtAccount: Organization management account
183
+ - AccountId: Account containing the subnet
184
+ - Region: AWS region of the subnet
185
+ - VpcId: VPC identifier containing the subnet
186
+ - SubnetName: Subnet name from Name tag (default: "None")
187
+ - CidrBlock: CIDR block assigned to subnet
188
+ - AvailableIpAddressCount: Available IP addresses in subnet
189
+ - SubnetId: Unique subnet identifier
190
+ - Tags: Resource tags for metadata and organization
191
+
192
+ Threading Architecture:
193
+ - Worker pool limited to min(credentials, 50) threads for optimal performance
194
+ - Queue-based work distribution for balanced load across workers
195
+ - Daemon threads for clean shutdown handling
196
+ - Real-time progress indicators for operational feedback
197
+
198
+ Network Processing:
199
+ - Subnet metadata enrichment with organizational context
200
+ - Tag-based name resolution with fallback handling
201
+ - VPC association mapping for network architecture visibility
202
+ - IP filtering capabilities for targeted subnet discovery
203
+
204
+ Error Handling:
205
+ - KeyError: Account access credential issues
206
+ - AttributeError: Profile configuration problems
207
+ - ClientError: AWS API authorization and regional failures
208
+ - Graceful handling with continued processing on individual failures
209
+
210
+ Performance Optimizations:
211
+ - Concurrent processing across accounts and regions
212
+ - Worker thread pool tuning for API efficiency
213
+ - Progress tracking for operational visibility
214
+ - Memory-efficient result aggregation
215
+ """
216
+
217
+ class FindSubnets(Thread):
218
+ """
219
+ Worker thread class for concurrent VPC subnet discovery and analysis.
220
+
221
+ Processes work items from shared queue to discover subnets in individual
222
+ AWS accounts and regions. Enriches subnet data with organizational metadata
223
+ and handles tag-based name resolution for enterprise visibility.
224
+ """
225
+
226
+ def __init__(self, queue):
227
+ """Initialize worker thread with shared work queue."""
228
+ Thread.__init__(self)
229
+ self.queue = queue
230
+
231
+ def run(self):
232
+ """
233
+ Main worker thread execution loop for subnet discovery.
234
+
235
+ Continuously processes credential sets from the work queue, discovers
236
+ VPC subnets, enriches metadata, and aggregates results. Implements
237
+ comprehensive error handling for various AWS API error conditions.
238
+ """
239
+ while True:
240
+ # Get the work from the queue and expand the tuple
241
+ c_account_credentials, c_fip, c_PlacesToLook, c_PlaceCount = self.queue.get()
242
+ logging.info(f"De-queued info for account {c_account_credentials['AccountId']}")
243
+ try:
244
+ logging.info(f"Attempting to connect to {c_account_credentials['AccountId']}")
245
+ # Call VPC API to discover subnets in this account/region with optional IP filtering
246
+ account_subnets = Inventory_Modules.find_account_subnets2(c_account_credentials, c_fip)
247
+ logging.info(f"Successfully connected to account {c_account_credentials['AccountId']}")
248
+
249
+ # Enrich each discovered subnet with organizational metadata
250
+ for y in range(len(account_subnets["Subnets"])):
251
+ account_subnets["Subnets"][y]["MgmtAccount"] = c_account_credentials["MgmtAccount"]
252
+ account_subnets["Subnets"][y]["AccountId"] = c_account_credentials["AccountId"]
253
+ account_subnets["Subnets"][y]["Region"] = c_account_credentials["Region"]
254
+ account_subnets["Subnets"][y]["SubnetName"] = "None"
255
+
256
+ # Extract subnet name from tags for human-readable identification
257
+ if "Tags" in account_subnets["Subnets"][y].keys():
258
+ for tag in account_subnets["Subnets"][y]["Tags"]:
259
+ if tag["Key"] == "Name":
260
+ account_subnets["Subnets"][y]["SubnetName"] = tag["Value"]
261
+
262
+ # Normalize VPC ID field for consistent reporting
263
+ account_subnets["Subnets"][y]["VPCId"] = (
264
+ account_subnets["Subnets"][y]["VpcId"]
265
+ if "VpcId" in account_subnets["Subnets"][y].keys()
266
+ else None
267
+ )
268
+
269
+ # Aggregate discovered subnets to shared result collection
270
+ if len(account_subnets["Subnets"]) > 0:
271
+ AllSubnets.extend(account_subnets["Subnets"])
272
+
273
+ except KeyError as my_Error:
274
+ # Handle credential or account access failures
275
+ logging.error(f"Account Access failed - trying to access {c_account_credentials['AccountId']}")
276
+ logging.info(f"Actual Error: {my_Error}")
277
+ pass
278
+ except AttributeError as my_Error:
279
+ # Handle profile configuration issues
280
+ logging.error(f"Error: Likely that one of the supplied profiles {pProfiles} was wrong")
281
+ logging.warning(my_Error)
282
+ continue
283
+ finally:
284
+ # Provide real-time progress feedback and mark work item complete
285
+ print(
286
+ f"{ERASE_LINE}Finished finding subnets in account {c_account_credentials['AccountId']} in region {c_account_credentials['Region']} - {c_PlaceCount} / {c_PlacesToLook}",
287
+ end="\r",
288
+ )
289
+ self.queue.task_done()
290
+
291
+ # Initialize threading infrastructure for parallel processing
292
+ checkqueue = Queue()
293
+ AllSubnets = []
294
+ PlaceCount = 0
295
+ PlacesToLook = len(CredentialList)
296
+ WorkerThreads = min(len(CredentialList), 50) # Limit worker threads for API efficiency
297
+
298
+ # Start worker thread pool for concurrent subnet discovery
299
+ for x in range(WorkerThreads):
300
+ worker = FindSubnets(checkqueue)
301
+ # Setting daemon to True will let the main thread exit even though the workers are blocking
302
+ worker.daemon = True
303
+ worker.start()
304
+
305
+ # Queue all credential sets for processing by worker threads
306
+ for credential in CredentialList:
307
+ logging.info(f"Connecting to account {credential['AccountId']}")
308
+ try:
309
+ checkqueue.put((credential, fip, PlacesToLook, PlaceCount))
310
+ PlaceCount += 1
311
+ except ClientError as my_Error:
312
+ # Handle authorization errors during queue operations
313
+ if "AuthFailure" in str(my_Error):
314
+ logging.error(
315
+ f"Authorization Failure accessing account {credential['AccountId']} in {credential['Region']} region"
316
+ )
317
+ logging.warning(f"It's possible that the region {credential['Region']} hasn't been opted-into")
318
+ pass
319
+
320
+ # Wait for all work items to be processed
321
+ checkqueue.join()
322
+ return AllSubnets
323
+
324
+
325
+ def present_results(fSubnetsFound: list):
326
+ """
327
+ Description: Shows off results at the end
328
+ @param fSubnetsFound: List of subnets found and their attributes.
329
+ """
330
+ display_dict = {
331
+ "MgmtAccount": {"DisplayOrder": 1, "Heading": "Mgmt Acct"},
332
+ "AccountId": {"DisplayOrder": 2, "Heading": "Acct Number"},
333
+ "Region": {"DisplayOrder": 3, "Heading": "Region"},
334
+ "VpcId": {"DisplayOrder": 4, "Heading": "VPC ID"},
335
+ "SubnetName": {"DisplayOrder": 5, "Heading": "Subnet Name"},
336
+ "CidrBlock": {"DisplayOrder": 6, "Heading": "CIDR Block"},
337
+ "AvailableIpAddressCount": {"DisplayOrder": 7, "Heading": "Available IPs"},
338
+ # 'IPUtilization' : {'DisplayOrder': 8, 'Heading': 'IP Utilization'},
339
+ # 'NearExhaustion' : {'DisplayOrder': 8, 'Heading': 'Near Exhaustion', 'Condition': [True]},
340
+ }
341
+ AccountNum = len(set([acct["AccountId"] for acct in AllCredentials]))
342
+ RegionNum = len(set([acct["Region"] for acct in AllCredentials]))
343
+ sorted_Subnets_Found = sorted(
344
+ fSubnetsFound, key=lambda x: (x["MgmtAccount"], x["AccountId"], x["Region"], x["SubnetName"])
345
+ )
346
+ display_results(sorted_Subnets_Found, display_dict, "None", pFilename)
347
+ print()
348
+ print(f"These accounts were skipped - as requested: {pSkipAccounts}") if pSkipAccounts is not None else ""
349
+ print(f"These profiles were skipped - as requested: {pSkipProfiles}") if pSkipProfiles is not None else ""
350
+ print(
351
+ f"The output has also been written to a file beginning with '{pFilename}' + the date and time"
352
+ ) if pFilename is not None else ""
353
+ print()
354
+ print(f"Found {len(SubnetsFound)} subnets across {AccountNum} accounts across {RegionNum} regions")
355
+
356
+
357
+ def analyze_results(fSubnetsFound: list):
358
+ # :fSubnetsFound: a list of the subnets found and their attributes
359
+ account_summary = []
360
+ VPC_summary = []
361
+ subnets_near_exhaustion = []
362
+ for record in fSubnetsFound:
363
+ AvailableIps = record["AvailableIpAddressCount"]
364
+ account_number = record["AccountId"]
365
+ vpc_id = record["VpcId"]
366
+ mask = int(str(record["CidrBlock"]).split("/")[1])
367
+ TotalIPs = 2 ** (32 - mask) - 5
368
+ IPUtilization = 100 - (round(AvailableIps / TotalIPs, 2) * 100)
369
+ subnets_near_exhaustion.append(record["SubnetId"]) if IPUtilization > 74 else ""
370
+ if account_number not in account_summary:
371
+ account_summary.append(account_number)
372
+ if vpc_id not in VPC_summary:
373
+ VPC_summary.append(vpc_id)
374
+ print()
375
+ print(f"Number of accounts found with subnets: {len(account_summary)}")
376
+ print(f"Number of unique VPCs found: {len(VPC_summary)}")
377
+ print(f"Number of subnets in danger of IP Exhaustion (80%+ IPs utilized): {len(subnets_near_exhaustion)}")
378
+ # print(f"Number of subnets using unroutable space (100.64.*.*): ")
379
+ print()
380
+
381
+
382
+ ##################
383
+ # Main
384
+ ##################
385
+
386
+
387
+ ERASE_LINE = "\x1b[2K"
388
+ begin_time = time()
389
+
390
+ if __name__ == "__main__":
391
+ args = parse_args(sys.argv[1:])
392
+ pProfiles = args.Profiles
393
+ pRegionList = args.Regions
394
+ pAccounts = args.Accounts
395
+ pRoleList = args.AccessRoles
396
+ pSkipAccounts = args.SkipAccounts
397
+ pSkipProfiles = args.SkipProfiles
398
+ pRootOnly = args.RootOnly
399
+ pIPaddressList = args.pipaddresses
400
+ pFilename = args.Filename
401
+ pTiming = args.Time
402
+ verbose = args.loglevel
403
+ # Setup logging levels
404
+ logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
405
+ logging.getLogger("boto3").setLevel(logging.CRITICAL)
406
+ logging.getLogger("botocore").setLevel(logging.CRITICAL)
407
+ logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
408
+ logging.getLogger("urllib3").setLevel(logging.CRITICAL)
409
+
410
+ logging.info(f"Profiles: {pProfiles}")
411
+
412
+ print()
413
+ print(f"Checking accounts for Subnets... ")
414
+ print()
415
+
416
+ # Get credentials from all relevant Children accounts
417
+ AllCredentials = get_all_credentials(
418
+ pProfiles, pTiming, pSkipProfiles, pSkipAccounts, pRootOnly, pAccounts, pRegionList, pRoleList
419
+ )
420
+ # Get relevant subnets
421
+ SubnetsFound = check_accounts_for_subnets(AllCredentials, fip=pIPaddressList)
422
+ # display_results(SubnetsFound, display_dict)
423
+ present_results(SubnetsFound)
424
+ # Print out an analysis of what was found at the end
425
+ if verbose < 50:
426
+ analyze_results(SubnetsFound)
427
+ if pTiming:
428
+ print(ERASE_LINE)
429
+ print(f"{Fore.GREEN}This script completed in {time() - begin_time:.2f} seconds{Fore.RESET}")
430
+
431
+ print()
432
+ print("Thank you for using this script")
433
+ print()