runbooks 0.2.5__py3-none-any.whl → 0.7.0__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 +2 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/.gradio/certificate.pem +31 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/__main__.log +8 -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/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
- jupyter-agent/tmp/jupyter-agent.ipynb +27 -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/README.md +337 -0
- runbooks/finops/__init__.py +86 -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/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -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/aws_organization.png +0 -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/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 +1004 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security/README.md +447 -0
- runbooks/security/__init__.py +71 -0
- runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
- runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
- runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
- runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
- runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
- runbooks/{security_baseline → security}/config-origin.json +1 -1
- runbooks/{security_baseline → security}/config.json +1 -1
- runbooks/{security_baseline → security}/permission.json +1 -1
- runbooks/{security_baseline → security}/report_generator.py +10 -2
- runbooks/{security_baseline → security}/report_template_en.html +7 -7
- runbooks/{security_baseline → security}/report_template_jp.html +7 -7
- runbooks/{security_baseline → security}/report_template_kr.html +12 -12
- runbooks/{security_baseline → security}/report_template_vn.html +7 -7
- runbooks/{security_baseline → security}/run_script.py +8 -2
- runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
- runbooks/{security_baseline → security}/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.7.0.dist-info/METADATA +375 -0
- runbooks-0.7.0.dist-info/RECORD +249 -0
- {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
- runbooks-0.7.0.dist-info/entry_points.txt +7 -0
- runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
- runbooks-0.7.0.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
- /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
- /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
- /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
- /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
- /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
- /runbooks/{security_baseline → security}/utils/enums.py +0 -0
- /runbooks/{security_baseline → security}/utils/language.py +0 -0
- /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
- /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
@@ -0,0 +1,275 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
AWS Networking Service Collectors
|
4
|
+
|
5
|
+
Maintains 100% compatibility with AWS Cloud Foundations inventory-scripts:
|
6
|
+
- all_my_vpcs.py -> VPCCollector
|
7
|
+
- all_my_subnets.py -> SubnetCollector
|
8
|
+
- all_my_elbs.py -> ELBCollector
|
9
|
+
- all_my_enis.py -> ENICollector
|
10
|
+
- all_my_phzs.py -> Route53Collector
|
11
|
+
|
12
|
+
This module preserves all original AWS Cloud Foundations functionality.
|
13
|
+
"""
|
14
|
+
|
15
|
+
from dataclasses import dataclass, field
|
16
|
+
from datetime import datetime
|
17
|
+
from typing import Any, Dict, List, Optional
|
18
|
+
|
19
|
+
import boto3
|
20
|
+
|
21
|
+
from ..models.account import AWSAccount
|
22
|
+
from ..models.resource import AWSResource, ResourceState, ResourceType
|
23
|
+
from ..utils.aws_helpers import get_boto3_session, handle_aws_exceptions
|
24
|
+
from ..utils.validation import validate_aws_account_id, validate_aws_region
|
25
|
+
from .base import BaseResourceCollector
|
26
|
+
|
27
|
+
|
28
|
+
class VPCCollector(BaseResourceCollector):
|
29
|
+
"""
|
30
|
+
VPC Collector - 100% compatible with all_my_vpcs.py
|
31
|
+
|
32
|
+
Preserves original AWS Cloud Foundations functionality:
|
33
|
+
- Cross-region VPC discovery
|
34
|
+
- CIDR block analysis
|
35
|
+
- Internet Gateway associations
|
36
|
+
- Route table mappings
|
37
|
+
"""
|
38
|
+
|
39
|
+
def __init__(self, session: Optional[boto3.Session] = None):
|
40
|
+
super().__init__(resource_type=ResourceType.VPC, session=session)
|
41
|
+
|
42
|
+
@handle_aws_exceptions
|
43
|
+
def collect_from_region(self, region: str, account: AWSAccount) -> List[AWSResource]:
|
44
|
+
"""Collect VPCs maintaining original script compatibility."""
|
45
|
+
if not validate_aws_region(region):
|
46
|
+
raise ValueError(f"Invalid AWS region: {region}")
|
47
|
+
|
48
|
+
resources = []
|
49
|
+
|
50
|
+
try:
|
51
|
+
ec2 = self.session.client("ec2", region_name=region)
|
52
|
+
|
53
|
+
# Use paginator for large result sets
|
54
|
+
paginator = ec2.get_paginator("describe_vpcs")
|
55
|
+
|
56
|
+
for page in paginator.paginate():
|
57
|
+
for vpc in page["Vpcs"]:
|
58
|
+
resource = self._convert_vpc_to_resource(vpc, region, account)
|
59
|
+
resources.append(resource)
|
60
|
+
|
61
|
+
except Exception as e:
|
62
|
+
self.logger.error(f"Failed to collect VPCs in {region}: {e}")
|
63
|
+
|
64
|
+
return resources
|
65
|
+
|
66
|
+
def _convert_vpc_to_resource(self, vpc: Dict[str, Any], region: str, account: AWSAccount) -> AWSResource:
|
67
|
+
"""Convert VPC data to standardized AWSResource format."""
|
68
|
+
|
69
|
+
tags = {tag["Key"]: tag["Value"] for tag in vpc.get("Tags", [])}
|
70
|
+
|
71
|
+
# Map VPC state to our enum
|
72
|
+
state_mapping = {"available": ResourceState.AVAILABLE, "pending": ResourceState.PENDING}
|
73
|
+
|
74
|
+
vpc_state = vpc.get("State", "unknown")
|
75
|
+
resource_state = state_mapping.get(vpc_state, ResourceState.UNKNOWN)
|
76
|
+
|
77
|
+
return AWSResource(
|
78
|
+
account_id=account.account_id,
|
79
|
+
region=region,
|
80
|
+
resource_type=ResourceType.VPC,
|
81
|
+
resource_id=vpc["VpcId"],
|
82
|
+
arn=f"arn:aws:ec2:{region}:{account.account_id}:vpc/{vpc['VpcId']}",
|
83
|
+
name=tags.get("Name", vpc["VpcId"]),
|
84
|
+
state=resource_state,
|
85
|
+
tags=tags,
|
86
|
+
metadata={
|
87
|
+
"cidr_block": vpc.get("CidrBlock"),
|
88
|
+
"cidr_block_association_set": vpc.get("CidrBlockAssociationSet", []),
|
89
|
+
"ipv6_cidr_block_association_set": vpc.get("Ipv6CidrBlockAssociationSet", []),
|
90
|
+
"dhcp_options_id": vpc.get("DhcpOptionsId"),
|
91
|
+
"instance_tenancy": vpc.get("InstanceTenancy"),
|
92
|
+
"is_default": vpc.get("IsDefault", False),
|
93
|
+
"owner_id": vpc.get("OwnerId"),
|
94
|
+
},
|
95
|
+
discovered_at=datetime.utcnow(),
|
96
|
+
)
|
97
|
+
|
98
|
+
|
99
|
+
class SubnetCollector(BaseResourceCollector):
|
100
|
+
"""
|
101
|
+
Subnet Collector - 100% compatible with all_my_subnets.py
|
102
|
+
|
103
|
+
Preserves original functionality for:
|
104
|
+
- Subnet discovery across VPCs
|
105
|
+
- Availability zone mapping
|
106
|
+
- Public/private subnet identification
|
107
|
+
- CIDR allocation analysis
|
108
|
+
"""
|
109
|
+
|
110
|
+
def __init__(self, session: Optional[boto3.Session] = None):
|
111
|
+
super().__init__(resource_type=ResourceType.SUBNET, session=session)
|
112
|
+
|
113
|
+
@handle_aws_exceptions
|
114
|
+
def collect_from_region(self, region: str, account: AWSAccount) -> List[AWSResource]:
|
115
|
+
"""Collect subnets maintaining original script compatibility."""
|
116
|
+
resources = []
|
117
|
+
|
118
|
+
try:
|
119
|
+
ec2 = self.session.client("ec2", region_name=region)
|
120
|
+
paginator = ec2.get_paginator("describe_subnets")
|
121
|
+
|
122
|
+
for page in paginator.paginate():
|
123
|
+
for subnet in page["Subnets"]:
|
124
|
+
resource = self._convert_subnet_to_resource(subnet, region, account)
|
125
|
+
resources.append(resource)
|
126
|
+
|
127
|
+
except Exception as e:
|
128
|
+
self.logger.error(f"Failed to collect subnets in {region}: {e}")
|
129
|
+
|
130
|
+
return resources
|
131
|
+
|
132
|
+
def _convert_subnet_to_resource(self, subnet: Dict[str, Any], region: str, account: AWSAccount) -> AWSResource:
|
133
|
+
"""Convert subnet data to standardized format."""
|
134
|
+
|
135
|
+
tags = {tag["Key"]: tag["Value"] for tag in subnet.get("Tags", [])}
|
136
|
+
|
137
|
+
state_mapping = {"available": ResourceState.AVAILABLE, "pending": ResourceState.PENDING}
|
138
|
+
|
139
|
+
subnet_state = subnet.get("State", "unknown")
|
140
|
+
resource_state = state_mapping.get(subnet_state, ResourceState.UNKNOWN)
|
141
|
+
|
142
|
+
return AWSResource(
|
143
|
+
account_id=account.account_id,
|
144
|
+
region=region,
|
145
|
+
resource_type=ResourceType.SUBNET,
|
146
|
+
resource_id=subnet["SubnetId"],
|
147
|
+
arn=f"arn:aws:ec2:{region}:{account.account_id}:subnet/{subnet['SubnetId']}",
|
148
|
+
name=tags.get("Name", subnet["SubnetId"]),
|
149
|
+
state=resource_state,
|
150
|
+
tags=tags,
|
151
|
+
metadata={
|
152
|
+
"vpc_id": subnet.get("VpcId"),
|
153
|
+
"cidr_block": subnet.get("CidrBlock"),
|
154
|
+
"ipv6_cidr_block_association_set": subnet.get("Ipv6CidrBlockAssociationSet", []),
|
155
|
+
"availability_zone": subnet.get("AvailabilityZone"),
|
156
|
+
"availability_zone_id": subnet.get("AvailabilityZoneId"),
|
157
|
+
"available_ip_address_count": subnet.get("AvailableIpAddressCount"),
|
158
|
+
"default_for_az": subnet.get("DefaultForAz", False),
|
159
|
+
"map_public_ip_on_launch": subnet.get("MapPublicIpOnLaunch", False),
|
160
|
+
"map_customer_owned_ip_on_launch": subnet.get("MapCustomerOwnedIpOnLaunch", False),
|
161
|
+
"customer_owned_ipv4_pool": subnet.get("CustomerOwnedIpv4Pool"),
|
162
|
+
"assign_ipv6_address_on_creation": subnet.get("AssignIpv6AddressOnCreation", False),
|
163
|
+
"subnet_arn": subnet.get("SubnetArn"),
|
164
|
+
"outpost_arn": subnet.get("OutpostArn"),
|
165
|
+
"enable_dns64": subnet.get("EnableDns64", False),
|
166
|
+
"ipv6_native": subnet.get("Ipv6Native", False),
|
167
|
+
"private_dns_name_options_on_launch": subnet.get("PrivateDnsNameOptionsOnLaunch", {}),
|
168
|
+
},
|
169
|
+
discovered_at=datetime.utcnow(),
|
170
|
+
)
|
171
|
+
|
172
|
+
|
173
|
+
# Legacy compatibility functions that exactly match original script interfaces
|
174
|
+
|
175
|
+
|
176
|
+
def collect_vpcs_legacy(account_id: str, region: str = None) -> List[Dict]:
|
177
|
+
"""
|
178
|
+
Legacy function maintaining exact compatibility with all_my_vpcs.py
|
179
|
+
|
180
|
+
This function preserves the original script's interface and output format.
|
181
|
+
"""
|
182
|
+
collector = VPCCollector()
|
183
|
+
account = AWSAccount(account_id=account_id, account_name=f"Account-{account_id}")
|
184
|
+
|
185
|
+
if region:
|
186
|
+
regions = [region]
|
187
|
+
else:
|
188
|
+
# Get all regions like original script
|
189
|
+
ec2 = boto3.client("ec2", region_name="us-east-1")
|
190
|
+
regions = [r["RegionName"] for r in ec2.describe_regions()["Regions"]]
|
191
|
+
|
192
|
+
all_vpcs = []
|
193
|
+
for reg in regions:
|
194
|
+
try:
|
195
|
+
resources = collector.collect_from_region(reg, account)
|
196
|
+
# Convert back to legacy format for compatibility
|
197
|
+
for resource in resources:
|
198
|
+
vpc_dict = {
|
199
|
+
"VpcId": resource.resource_id,
|
200
|
+
"Region": resource.region,
|
201
|
+
"State": resource.state.value,
|
202
|
+
"CidrBlock": resource.metadata.get("cidr_block"),
|
203
|
+
"IsDefault": resource.metadata.get("is_default"),
|
204
|
+
"InstanceTenancy": resource.metadata.get("instance_tenancy"),
|
205
|
+
"DhcpOptionsId": resource.metadata.get("dhcp_options_id"),
|
206
|
+
"Tags": resource.tags,
|
207
|
+
"OwnerId": resource.metadata.get("owner_id"),
|
208
|
+
}
|
209
|
+
all_vpcs.append(vpc_dict)
|
210
|
+
except Exception as e:
|
211
|
+
print(f"Error collecting VPCs from {reg}: {e}")
|
212
|
+
|
213
|
+
return all_vpcs
|
214
|
+
|
215
|
+
|
216
|
+
def collect_subnets_legacy(account_id: str, region: str = None) -> List[Dict]:
|
217
|
+
"""Legacy function maintaining exact compatibility with all_my_subnets.py"""
|
218
|
+
collector = SubnetCollector()
|
219
|
+
account = AWSAccount(account_id=account_id, account_name=f"Account-{account_id}")
|
220
|
+
|
221
|
+
if region:
|
222
|
+
regions = [region]
|
223
|
+
else:
|
224
|
+
ec2 = boto3.client("ec2", region_name="us-east-1")
|
225
|
+
regions = [r["RegionName"] for r in ec2.describe_regions()["Regions"]]
|
226
|
+
|
227
|
+
all_subnets = []
|
228
|
+
for reg in regions:
|
229
|
+
try:
|
230
|
+
resources = collector.collect_from_region(reg, account)
|
231
|
+
for resource in resources:
|
232
|
+
subnet_dict = {
|
233
|
+
"SubnetId": resource.resource_id,
|
234
|
+
"Region": resource.region,
|
235
|
+
"State": resource.state.value,
|
236
|
+
"VpcId": resource.metadata.get("vpc_id"),
|
237
|
+
"CidrBlock": resource.metadata.get("cidr_block"),
|
238
|
+
"AvailabilityZone": resource.metadata.get("availability_zone"),
|
239
|
+
"AvailableIpAddressCount": resource.metadata.get("available_ip_address_count"),
|
240
|
+
"DefaultForAz": resource.metadata.get("default_for_az"),
|
241
|
+
"MapPublicIpOnLaunch": resource.metadata.get("map_public_ip_on_launch"),
|
242
|
+
"Tags": resource.tags,
|
243
|
+
}
|
244
|
+
all_subnets.append(subnet_dict)
|
245
|
+
except Exception as e:
|
246
|
+
print(f"Error collecting subnets from {reg}: {e}")
|
247
|
+
|
248
|
+
return all_subnets
|
249
|
+
|
250
|
+
|
251
|
+
# Command-line interface maintaining original script compatibility
|
252
|
+
if __name__ == "__main__":
|
253
|
+
import argparse
|
254
|
+
|
255
|
+
parser = argparse.ArgumentParser(description="AWS Networking Resource Inventory")
|
256
|
+
parser.add_argument("--account-id", required=True, help="AWS Account ID")
|
257
|
+
parser.add_argument("--region", help="Specific region (default: all regions)")
|
258
|
+
parser.add_argument("--resource-type", choices=["vpcs", "subnets"], help="Specific resource type to collect")
|
259
|
+
|
260
|
+
args = parser.parse_args()
|
261
|
+
|
262
|
+
# Maintain exact compatibility with original scripts
|
263
|
+
if args.resource_type == "vpcs" or not args.resource_type:
|
264
|
+
vpcs = collect_vpcs_legacy(args.account_id, args.region)
|
265
|
+
print(f"Found {len(vpcs)} VPCs")
|
266
|
+
for vpc in vpcs:
|
267
|
+
default_str = " (Default)" if vpc["IsDefault"] else ""
|
268
|
+
print(f" {vpc['VpcId']} ({vpc['CidrBlock']}){default_str} in {vpc['Region']}")
|
269
|
+
|
270
|
+
if args.resource_type == "subnets" or not args.resource_type:
|
271
|
+
subnets = collect_subnets_legacy(args.account_id, args.region)
|
272
|
+
print(f"Found {len(subnets)} subnets")
|
273
|
+
for subnet in subnets:
|
274
|
+
public_str = " (Public)" if subnet["MapPublicIpOnLaunch"] else " (Private)"
|
275
|
+
print(f" {subnet['SubnetId']} ({subnet['CidrBlock']}){public_str} in {subnet['AvailabilityZone']}")
|
@@ -0,0 +1,222 @@
|
|
1
|
+
"""
|
2
|
+
Base collector class for AWS resource discovery.
|
3
|
+
|
4
|
+
This module provides the abstract base class that all resource collectors
|
5
|
+
must implement, ensuring consistent interfaces and patterns across all
|
6
|
+
AWS service categories.
|
7
|
+
"""
|
8
|
+
|
9
|
+
from abc import ABC, abstractmethod
|
10
|
+
from dataclasses import dataclass
|
11
|
+
from datetime import datetime
|
12
|
+
from typing import Any, Dict, List, Optional, Set
|
13
|
+
|
14
|
+
import boto3
|
15
|
+
from botocore.exceptions import ClientError, NoCredentialsError
|
16
|
+
from loguru import logger
|
17
|
+
|
18
|
+
from runbooks.inventory.models.account import AWSAccount
|
19
|
+
from runbooks.inventory.models.resource import AWSResource, ResourceMetadata
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class CollectionContext:
|
24
|
+
"""Context information for resource collection operations."""
|
25
|
+
|
26
|
+
account: AWSAccount
|
27
|
+
region: str
|
28
|
+
resource_types: Set[str]
|
29
|
+
include_costs: bool = False
|
30
|
+
dry_run: bool = False
|
31
|
+
collection_timestamp: datetime = None
|
32
|
+
|
33
|
+
def __post_init__(self):
|
34
|
+
"""Initialize timestamp if not provided."""
|
35
|
+
if self.collection_timestamp is None:
|
36
|
+
self.collection_timestamp = datetime.utcnow()
|
37
|
+
|
38
|
+
|
39
|
+
class BaseResourceCollector(ABC):
|
40
|
+
"""
|
41
|
+
Abstract base class for AWS resource collectors.
|
42
|
+
|
43
|
+
All resource collectors must inherit from this class and implement
|
44
|
+
the required abstract methods. This ensures consistent behavior
|
45
|
+
and interfaces across all AWS service categories.
|
46
|
+
|
47
|
+
Attributes:
|
48
|
+
service_category: AWS service category (e.g., 'compute', 'storage')
|
49
|
+
supported_resources: Set of resource types this collector handles
|
50
|
+
requires_org_access: Whether collector needs organization-level permissions
|
51
|
+
"""
|
52
|
+
|
53
|
+
service_category: str = ""
|
54
|
+
supported_resources: Set[str] = set()
|
55
|
+
requires_org_access: bool = False
|
56
|
+
|
57
|
+
def __init__(self, session: boto3.Session):
|
58
|
+
"""
|
59
|
+
Initialize the collector with an AWS session.
|
60
|
+
|
61
|
+
Args:
|
62
|
+
session: Configured boto3 session with appropriate credentials
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
NoCredentialsError: If session lacks valid AWS credentials
|
66
|
+
ValueError: If session is invalid or missing required permissions
|
67
|
+
"""
|
68
|
+
self.session = session
|
69
|
+
self._validate_session()
|
70
|
+
self._clients = {}
|
71
|
+
self._region_cache = {}
|
72
|
+
|
73
|
+
logger.debug(f"Initialized {self.__class__.__name__} for category '{self.service_category}'")
|
74
|
+
|
75
|
+
def _validate_session(self) -> None:
|
76
|
+
"""
|
77
|
+
Validate the AWS session has required credentials.
|
78
|
+
|
79
|
+
Raises:
|
80
|
+
NoCredentialsError: If no credentials are available
|
81
|
+
ValueError: If credentials are invalid
|
82
|
+
"""
|
83
|
+
try:
|
84
|
+
# Test credentials by getting caller identity
|
85
|
+
sts_client = self.session.client("sts")
|
86
|
+
identity = sts_client.get_caller_identity()
|
87
|
+
logger.debug(f"Session validated for account: {identity.get('Account')}")
|
88
|
+
except NoCredentialsError:
|
89
|
+
raise NoCredentialsError("AWS session lacks valid credentials")
|
90
|
+
except ClientError as e:
|
91
|
+
raise ValueError(f"Invalid AWS session: {e}")
|
92
|
+
|
93
|
+
def get_client(self, service: str, region: str) -> boto3.client:
|
94
|
+
"""
|
95
|
+
Get a cached boto3 client for the specified service and region.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
service: AWS service name (e.g., 'ec2', 's3')
|
99
|
+
region: AWS region name
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Configured boto3 client
|
103
|
+
"""
|
104
|
+
client_key = f"{service}:{region}"
|
105
|
+
if client_key not in self._clients:
|
106
|
+
self._clients[client_key] = self.session.client(service, region_name=region)
|
107
|
+
return self._clients[client_key]
|
108
|
+
|
109
|
+
@abstractmethod
|
110
|
+
def collect_resources(
|
111
|
+
self, context: CollectionContext, resource_filters: Optional[Dict[str, Any]] = None
|
112
|
+
) -> List[AWSResource]:
|
113
|
+
"""
|
114
|
+
Collect AWS resources for this service category.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
context: Collection context with account, region, and options
|
118
|
+
resource_filters: Optional filters to apply during collection
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
List of discovered AWS resources
|
122
|
+
|
123
|
+
Raises:
|
124
|
+
ClientError: If AWS API calls fail
|
125
|
+
ValueError: If context or filters are invalid
|
126
|
+
"""
|
127
|
+
pass
|
128
|
+
|
129
|
+
@abstractmethod
|
130
|
+
def get_resource_costs(self, resources: List[AWSResource], context: CollectionContext) -> Dict[str, float]:
|
131
|
+
"""
|
132
|
+
Get cost information for the collected resources.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
resources: List of resources to get costs for
|
136
|
+
context: Collection context
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Dictionary mapping resource ARNs to costs
|
140
|
+
|
141
|
+
Note:
|
142
|
+
This method should gracefully handle cases where cost
|
143
|
+
information is not available or accessible.
|
144
|
+
"""
|
145
|
+
pass
|
146
|
+
|
147
|
+
def can_collect_resource_type(self, resource_type: str) -> bool:
|
148
|
+
"""
|
149
|
+
Check if this collector can handle the specified resource type.
|
150
|
+
|
151
|
+
Args:
|
152
|
+
resource_type: Resource type to check
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
True if this collector supports the resource type
|
156
|
+
"""
|
157
|
+
return resource_type in self.supported_resources
|
158
|
+
|
159
|
+
def get_available_regions(self, service: str = None) -> List[str]:
|
160
|
+
"""
|
161
|
+
Get list of available AWS regions for this service category.
|
162
|
+
|
163
|
+
Args:
|
164
|
+
service: Specific service to check regions for
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
List of available region names
|
168
|
+
"""
|
169
|
+
if service in self._region_cache:
|
170
|
+
return self._region_cache[service]
|
171
|
+
|
172
|
+
try:
|
173
|
+
# Use EC2 as default service for region discovery
|
174
|
+
check_service = service or "ec2"
|
175
|
+
ec2_client = self.session.client("ec2", region_name="us-east-1")
|
176
|
+
|
177
|
+
response = ec2_client.describe_regions(
|
178
|
+
Filters=[{"Name": "opt-in-status", "Values": ["opt-in-not-required", "opted-in"]}]
|
179
|
+
)
|
180
|
+
|
181
|
+
regions = [r["RegionName"] for r in response["Regions"]]
|
182
|
+
self._region_cache[service] = regions
|
183
|
+
return regions
|
184
|
+
|
185
|
+
except ClientError as e:
|
186
|
+
logger.warning(f"Failed to get regions for {service}: {e}")
|
187
|
+
# Return common regions as fallback
|
188
|
+
return [
|
189
|
+
"us-east-1",
|
190
|
+
"us-east-2",
|
191
|
+
"us-west-1",
|
192
|
+
"us-west-2",
|
193
|
+
"eu-west-1",
|
194
|
+
"eu-central-1",
|
195
|
+
"ap-southeast-1",
|
196
|
+
"ap-northeast-1",
|
197
|
+
]
|
198
|
+
|
199
|
+
def _create_resource_metadata(self, context: CollectionContext, resource_data: Dict[str, Any]) -> ResourceMetadata:
|
200
|
+
"""
|
201
|
+
Create resource metadata from AWS API response data.
|
202
|
+
|
203
|
+
Args:
|
204
|
+
context: Collection context
|
205
|
+
resource_data: Raw resource data from AWS API
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
Structured resource metadata
|
209
|
+
"""
|
210
|
+
return ResourceMetadata(
|
211
|
+
account_id=context.account.account_id,
|
212
|
+
region=context.region,
|
213
|
+
service_category=self.service_category,
|
214
|
+
collection_timestamp=context.collection_timestamp,
|
215
|
+
raw_data=resource_data,
|
216
|
+
)
|
217
|
+
|
218
|
+
def __repr__(self) -> str:
|
219
|
+
"""String representation of the collector."""
|
220
|
+
return (
|
221
|
+
f"{self.__class__.__name__}(category='{self.service_category}', resources={len(self.supported_resources)})"
|
222
|
+
)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
"""
|
2
|
+
Core inventory engine for AWS resource discovery.
|
3
|
+
|
4
|
+
This module contains the main business logic for orchestrating inventory
|
5
|
+
collection across multiple AWS accounts and regions.
|
6
|
+
|
7
|
+
Components:
|
8
|
+
- collector: Main inventory orchestration
|
9
|
+
- formatter: Output formatting and export capabilities
|
10
|
+
- session_manager: AWS session and credential management
|
11
|
+
"""
|
12
|
+
|
13
|
+
from runbooks.inventory.core.collector import InventoryCollector
|
14
|
+
from runbooks.inventory.core.formatter import InventoryFormatter
|
15
|
+
|
16
|
+
__all__ = [
|
17
|
+
"InventoryCollector",
|
18
|
+
"InventoryFormatter",
|
19
|
+
]
|