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.
Files changed (249) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env +2 -0
  3. jupyter-agent/.env.template +2 -0
  4. jupyter-agent/.gitattributes +35 -0
  5. jupyter-agent/.gradio/certificate.pem +31 -0
  6. jupyter-agent/README.md +16 -0
  7. jupyter-agent/__main__.log +8 -0
  8. jupyter-agent/app.py +256 -0
  9. jupyter-agent/cloudops-agent.png +0 -0
  10. jupyter-agent/ds-system-prompt.txt +154 -0
  11. jupyter-agent/jupyter-agent.png +0 -0
  12. jupyter-agent/llama3_template.jinja +123 -0
  13. jupyter-agent/requirements.txt +9 -0
  14. jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
  15. jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
  16. jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
  17. jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
  18. jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
  19. jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
  20. jupyter-agent/utils.py +409 -0
  21. runbooks/__init__.py +71 -3
  22. runbooks/__main__.py +13 -0
  23. runbooks/aws/ec2_describe_instances.py +1 -1
  24. runbooks/aws/ec2_run_instances.py +8 -2
  25. runbooks/aws/ec2_start_stop_instances.py +17 -4
  26. runbooks/aws/ec2_unused_volumes.py +5 -1
  27. runbooks/aws/s3_create_bucket.py +4 -2
  28. runbooks/aws/s3_list_objects.py +6 -1
  29. runbooks/aws/tagging_lambda_handler.py +13 -2
  30. runbooks/aws/tags.json +12 -0
  31. runbooks/base.py +353 -0
  32. runbooks/cfat/README.md +49 -0
  33. runbooks/cfat/__init__.py +74 -0
  34. runbooks/cfat/app.ts +644 -0
  35. runbooks/cfat/assessment/__init__.py +40 -0
  36. runbooks/cfat/assessment/asana-import.csv +39 -0
  37. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  38. runbooks/cfat/assessment/cfat.txt +520 -0
  39. runbooks/cfat/assessment/collectors.py +200 -0
  40. runbooks/cfat/assessment/jira-import.csv +39 -0
  41. runbooks/cfat/assessment/runner.py +387 -0
  42. runbooks/cfat/assessment/validators.py +290 -0
  43. runbooks/cfat/cli.py +103 -0
  44. runbooks/cfat/docs/asana-import.csv +24 -0
  45. runbooks/cfat/docs/cfat-checks.csv +31 -0
  46. runbooks/cfat/docs/cfat.txt +335 -0
  47. runbooks/cfat/docs/checks-output.png +0 -0
  48. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  49. runbooks/cfat/docs/cloudshell-download.png +0 -0
  50. runbooks/cfat/docs/cloudshell-output.png +0 -0
  51. runbooks/cfat/docs/downloadfile.png +0 -0
  52. runbooks/cfat/docs/jira-import.csv +24 -0
  53. runbooks/cfat/docs/open-cloudshell.png +0 -0
  54. runbooks/cfat/docs/report-header.png +0 -0
  55. runbooks/cfat/models.py +1026 -0
  56. runbooks/cfat/package-lock.json +5116 -0
  57. runbooks/cfat/package.json +38 -0
  58. runbooks/cfat/report.py +496 -0
  59. runbooks/cfat/reporting/__init__.py +46 -0
  60. runbooks/cfat/reporting/exporters.py +337 -0
  61. runbooks/cfat/reporting/formatters.py +496 -0
  62. runbooks/cfat/reporting/templates.py +135 -0
  63. runbooks/cfat/run-assessment.sh +23 -0
  64. runbooks/cfat/runner.py +69 -0
  65. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  66. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  67. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  68. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  69. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  70. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  71. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  72. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  73. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  74. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  75. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  76. runbooks/cfat/src/actions/create-report.ts +616 -0
  77. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  78. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  79. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  80. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  81. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  82. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  83. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  84. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  85. runbooks/cfat/src/actions/get-regions.ts +22 -0
  86. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  87. runbooks/cfat/src/types/index.d.ts +147 -0
  88. runbooks/cfat/tests/__init__.py +141 -0
  89. runbooks/cfat/tests/test_cli.py +340 -0
  90. runbooks/cfat/tests/test_integration.py +290 -0
  91. runbooks/cfat/tests/test_models.py +505 -0
  92. runbooks/cfat/tests/test_reporting.py +354 -0
  93. runbooks/cfat/tsconfig.json +16 -0
  94. runbooks/cfat/webpack.config.cjs +27 -0
  95. runbooks/config.py +260 -0
  96. runbooks/finops/README.md +337 -0
  97. runbooks/finops/__init__.py +86 -0
  98. runbooks/finops/aws_client.py +245 -0
  99. runbooks/finops/cli.py +151 -0
  100. runbooks/finops/cost_processor.py +410 -0
  101. runbooks/finops/dashboard_runner.py +448 -0
  102. runbooks/finops/helpers.py +355 -0
  103. runbooks/finops/main.py +14 -0
  104. runbooks/finops/profile_processor.py +174 -0
  105. runbooks/finops/types.py +66 -0
  106. runbooks/finops/visualisations.py +80 -0
  107. runbooks/inventory/.gitignore +354 -0
  108. runbooks/inventory/ArgumentsClass.py +261 -0
  109. runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
  110. runbooks/inventory/Inventory_Modules.py +6130 -0
  111. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  112. runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
  113. runbooks/inventory/README.md +1320 -0
  114. runbooks/inventory/__init__.py +62 -0
  115. runbooks/inventory/account_class.py +532 -0
  116. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  117. runbooks/inventory/aws_decorators.py +201 -0
  118. runbooks/inventory/aws_organization.png +0 -0
  119. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  120. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  121. runbooks/inventory/check_controltower_readiness.py +1107 -0
  122. runbooks/inventory/check_landingzone_readiness.py +711 -0
  123. runbooks/inventory/cloudtrail.md +727 -0
  124. runbooks/inventory/collectors/__init__.py +20 -0
  125. runbooks/inventory/collectors/aws_compute.py +518 -0
  126. runbooks/inventory/collectors/aws_networking.py +275 -0
  127. runbooks/inventory/collectors/base.py +222 -0
  128. runbooks/inventory/core/__init__.py +19 -0
  129. runbooks/inventory/core/collector.py +303 -0
  130. runbooks/inventory/core/formatter.py +296 -0
  131. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  132. runbooks/inventory/discovery.md +81 -0
  133. runbooks/inventory/draw_org_structure.py +748 -0
  134. runbooks/inventory/ec2_vpc_utils.py +341 -0
  135. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  136. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  137. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  138. runbooks/inventory/find_ec2_security_groups.py +669 -0
  139. runbooks/inventory/find_landingzone_versions.py +201 -0
  140. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  141. runbooks/inventory/inventory.sh +659 -0
  142. runbooks/inventory/list_cfn_stacks.py +558 -0
  143. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  144. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  145. runbooks/inventory/list_cfn_stacksets.py +453 -0
  146. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  147. runbooks/inventory/list_ds_directories.py +354 -0
  148. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  149. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  150. runbooks/inventory/list_ec2_instances.py +425 -0
  151. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  152. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  153. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  154. runbooks/inventory/list_guardduty_detectors.py +568 -0
  155. runbooks/inventory/list_iam_policies.py +404 -0
  156. runbooks/inventory/list_iam_roles.py +518 -0
  157. runbooks/inventory/list_iam_saml_providers.py +359 -0
  158. runbooks/inventory/list_lambda_functions.py +882 -0
  159. runbooks/inventory/list_org_accounts.py +446 -0
  160. runbooks/inventory/list_org_accounts_users.py +354 -0
  161. runbooks/inventory/list_rds_db_instances.py +406 -0
  162. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  163. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  164. runbooks/inventory/list_sns_topics.py +360 -0
  165. runbooks/inventory/list_ssm_parameters.py +402 -0
  166. runbooks/inventory/list_vpc_subnets.py +433 -0
  167. runbooks/inventory/list_vpcs.py +422 -0
  168. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  169. runbooks/inventory/models/__init__.py +24 -0
  170. runbooks/inventory/models/account.py +192 -0
  171. runbooks/inventory/models/inventory.py +309 -0
  172. runbooks/inventory/models/resource.py +247 -0
  173. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  174. runbooks/inventory/requirements.txt +12 -0
  175. runbooks/inventory/run_on_multi_accounts.py +211 -0
  176. runbooks/inventory/tests/common_test_data.py +3661 -0
  177. runbooks/inventory/tests/common_test_functions.py +204 -0
  178. runbooks/inventory/tests/setup.py +24 -0
  179. runbooks/inventory/tests/src.py +18 -0
  180. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  181. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  182. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  183. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  184. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  185. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  186. runbooks/inventory/update_aws_actions.py +173 -0
  187. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  188. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  189. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  190. runbooks/inventory/update_s3_public_access_block.py +539 -0
  191. runbooks/inventory/utils/__init__.py +23 -0
  192. runbooks/inventory/utils/aws_helpers.py +510 -0
  193. runbooks/inventory/utils/threading_utils.py +493 -0
  194. runbooks/inventory/utils/validation.py +682 -0
  195. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  196. runbooks/main.py +1004 -0
  197. runbooks/organizations/__init__.py +12 -0
  198. runbooks/organizations/manager.py +374 -0
  199. runbooks/security/README.md +447 -0
  200. runbooks/security/__init__.py +71 -0
  201. runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
  202. runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
  203. runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
  204. runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
  205. runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
  206. runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
  207. runbooks/{security_baseline → security}/config-origin.json +1 -1
  208. runbooks/{security_baseline → security}/config.json +1 -1
  209. runbooks/{security_baseline → security}/permission.json +1 -1
  210. runbooks/{security_baseline → security}/report_generator.py +10 -2
  211. runbooks/{security_baseline → security}/report_template_en.html +7 -7
  212. runbooks/{security_baseline → security}/report_template_jp.html +7 -7
  213. runbooks/{security_baseline → security}/report_template_kr.html +12 -12
  214. runbooks/{security_baseline → security}/report_template_vn.html +7 -7
  215. runbooks/{security_baseline → security}/run_script.py +8 -2
  216. runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
  217. runbooks/{security_baseline → security}/utils/common.py +5 -1
  218. runbooks/utils/__init__.py +204 -0
  219. runbooks-0.7.0.dist-info/METADATA +375 -0
  220. runbooks-0.7.0.dist-info/RECORD +249 -0
  221. {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
  222. runbooks-0.7.0.dist-info/entry_points.txt +7 -0
  223. runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
  224. runbooks-0.7.0.dist-info/top_level.txt +3 -0
  225. runbooks/python101/calculator.py +0 -34
  226. runbooks/python101/config.py +0 -1
  227. runbooks/python101/exceptions.py +0 -16
  228. runbooks/python101/file_manager.py +0 -218
  229. runbooks/python101/toolkit.py +0 -153
  230. runbooks-0.2.5.dist-info/METADATA +0 -439
  231. runbooks-0.2.5.dist-info/RECORD +0 -61
  232. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  233. runbooks-0.2.5.dist-info/top_level.txt +0 -1
  234. /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
  235. /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
  236. /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
  237. /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
  238. /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
  239. /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
  240. /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
  241. /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
  242. /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
  243. /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
  244. /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
  245. /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
  246. /runbooks/{security_baseline → security}/utils/enums.py +0 -0
  247. /runbooks/{security_baseline → security}/utils/language.py +0 -0
  248. /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
  249. /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
+ ]