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.
- conftest.py +26 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/__init__.py +88 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/script_test_data.py +0 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +785 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security_baseline/README.md +324 -0
- runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
- runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
- runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
- runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
- runbooks/security_baseline/checklist/root_access_key.py +6 -1
- runbooks/security_baseline/config-origin.json +1 -1
- runbooks/security_baseline/config.json +1 -1
- runbooks/security_baseline/permission.json +1 -1
- runbooks/security_baseline/report_generator.py +10 -2
- runbooks/security_baseline/report_template_en.html +7 -7
- runbooks/security_baseline/report_template_jp.html +7 -7
- runbooks/security_baseline/report_template_kr.html +12 -12
- runbooks/security_baseline/report_template_vn.html +7 -7
- runbooks/security_baseline/requirements.txt +7 -0
- runbooks/security_baseline/run_script.py +8 -2
- runbooks/security_baseline/security_baseline_tester.py +10 -2
- runbooks/security_baseline/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.6.1.dist-info/METADATA +373 -0
- runbooks-0.6.1.dist-info/RECORD +237 -0
- {runbooks-0.2.5.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
- runbooks-0.6.1.dist-info/entry_points.txt +7 -0
- runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
- runbooks-0.6.1.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.5.dist-info/METADATA +0 -439
- runbooks-0.2.5.dist-info/RECORD +0 -61
- runbooks-0.2.5.dist-info/entry_points.txt +0 -3
- 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()
|