runbooks 0.2.5__py3-none-any.whl → 0.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env.template +2 -0
  3. jupyter-agent/.gitattributes +35 -0
  4. jupyter-agent/README.md +16 -0
  5. jupyter-agent/app.py +256 -0
  6. jupyter-agent/cloudops-agent.png +0 -0
  7. jupyter-agent/ds-system-prompt.txt +154 -0
  8. jupyter-agent/jupyter-agent.png +0 -0
  9. jupyter-agent/llama3_template.jinja +123 -0
  10. jupyter-agent/requirements.txt +9 -0
  11. jupyter-agent/utils.py +409 -0
  12. runbooks/__init__.py +71 -3
  13. runbooks/__main__.py +13 -0
  14. runbooks/aws/ec2_describe_instances.py +1 -1
  15. runbooks/aws/ec2_run_instances.py +8 -2
  16. runbooks/aws/ec2_start_stop_instances.py +17 -4
  17. runbooks/aws/ec2_unused_volumes.py +5 -1
  18. runbooks/aws/s3_create_bucket.py +4 -2
  19. runbooks/aws/s3_list_objects.py +6 -1
  20. runbooks/aws/tagging_lambda_handler.py +13 -2
  21. runbooks/aws/tags.json +12 -0
  22. runbooks/base.py +353 -0
  23. runbooks/cfat/README.md +49 -0
  24. runbooks/cfat/__init__.py +74 -0
  25. runbooks/cfat/app.ts +644 -0
  26. runbooks/cfat/assessment/__init__.py +40 -0
  27. runbooks/cfat/assessment/asana-import.csv +39 -0
  28. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  29. runbooks/cfat/assessment/cfat.txt +520 -0
  30. runbooks/cfat/assessment/collectors.py +200 -0
  31. runbooks/cfat/assessment/jira-import.csv +39 -0
  32. runbooks/cfat/assessment/runner.py +387 -0
  33. runbooks/cfat/assessment/validators.py +290 -0
  34. runbooks/cfat/cli.py +103 -0
  35. runbooks/cfat/docs/asana-import.csv +24 -0
  36. runbooks/cfat/docs/cfat-checks.csv +31 -0
  37. runbooks/cfat/docs/cfat.txt +335 -0
  38. runbooks/cfat/docs/checks-output.png +0 -0
  39. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  40. runbooks/cfat/docs/cloudshell-download.png +0 -0
  41. runbooks/cfat/docs/cloudshell-output.png +0 -0
  42. runbooks/cfat/docs/downloadfile.png +0 -0
  43. runbooks/cfat/docs/jira-import.csv +24 -0
  44. runbooks/cfat/docs/open-cloudshell.png +0 -0
  45. runbooks/cfat/docs/report-header.png +0 -0
  46. runbooks/cfat/models.py +1026 -0
  47. runbooks/cfat/package-lock.json +5116 -0
  48. runbooks/cfat/package.json +38 -0
  49. runbooks/cfat/report.py +496 -0
  50. runbooks/cfat/reporting/__init__.py +46 -0
  51. runbooks/cfat/reporting/exporters.py +337 -0
  52. runbooks/cfat/reporting/formatters.py +496 -0
  53. runbooks/cfat/reporting/templates.py +135 -0
  54. runbooks/cfat/run-assessment.sh +23 -0
  55. runbooks/cfat/runner.py +69 -0
  56. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  57. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  58. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  59. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  60. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  61. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  62. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  63. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  64. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  65. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  66. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  67. runbooks/cfat/src/actions/create-report.ts +616 -0
  68. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  69. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  70. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  71. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  72. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  73. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  74. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  75. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  76. runbooks/cfat/src/actions/get-regions.ts +22 -0
  77. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  78. runbooks/cfat/src/types/index.d.ts +147 -0
  79. runbooks/cfat/tests/__init__.py +141 -0
  80. runbooks/cfat/tests/test_cli.py +340 -0
  81. runbooks/cfat/tests/test_integration.py +290 -0
  82. runbooks/cfat/tests/test_models.py +505 -0
  83. runbooks/cfat/tests/test_reporting.py +354 -0
  84. runbooks/cfat/tsconfig.json +16 -0
  85. runbooks/cfat/webpack.config.cjs +27 -0
  86. runbooks/config.py +260 -0
  87. runbooks/finops/__init__.py +88 -0
  88. runbooks/finops/aws_client.py +245 -0
  89. runbooks/finops/cli.py +151 -0
  90. runbooks/finops/cost_processor.py +410 -0
  91. runbooks/finops/dashboard_runner.py +448 -0
  92. runbooks/finops/helpers.py +355 -0
  93. runbooks/finops/main.py +14 -0
  94. runbooks/finops/profile_processor.py +174 -0
  95. runbooks/finops/types.py +66 -0
  96. runbooks/finops/visualisations.py +80 -0
  97. runbooks/inventory/.gitignore +354 -0
  98. runbooks/inventory/ArgumentsClass.py +261 -0
  99. runbooks/inventory/Inventory_Modules.py +6130 -0
  100. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  101. runbooks/inventory/README.md +1320 -0
  102. runbooks/inventory/__init__.py +62 -0
  103. runbooks/inventory/account_class.py +532 -0
  104. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  105. runbooks/inventory/aws_decorators.py +201 -0
  106. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  107. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  108. runbooks/inventory/check_controltower_readiness.py +1107 -0
  109. runbooks/inventory/check_landingzone_readiness.py +711 -0
  110. runbooks/inventory/cloudtrail.md +727 -0
  111. runbooks/inventory/collectors/__init__.py +20 -0
  112. runbooks/inventory/collectors/aws_compute.py +518 -0
  113. runbooks/inventory/collectors/aws_networking.py +275 -0
  114. runbooks/inventory/collectors/base.py +222 -0
  115. runbooks/inventory/core/__init__.py +19 -0
  116. runbooks/inventory/core/collector.py +303 -0
  117. runbooks/inventory/core/formatter.py +296 -0
  118. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  119. runbooks/inventory/discovery.md +81 -0
  120. runbooks/inventory/draw_org_structure.py +748 -0
  121. runbooks/inventory/ec2_vpc_utils.py +341 -0
  122. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  123. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  124. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  125. runbooks/inventory/find_ec2_security_groups.py +669 -0
  126. runbooks/inventory/find_landingzone_versions.py +201 -0
  127. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  128. runbooks/inventory/inventory.sh +659 -0
  129. runbooks/inventory/list_cfn_stacks.py +558 -0
  130. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  131. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  132. runbooks/inventory/list_cfn_stacksets.py +453 -0
  133. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  134. runbooks/inventory/list_ds_directories.py +354 -0
  135. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  136. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  137. runbooks/inventory/list_ec2_instances.py +425 -0
  138. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  139. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  140. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  141. runbooks/inventory/list_guardduty_detectors.py +568 -0
  142. runbooks/inventory/list_iam_policies.py +404 -0
  143. runbooks/inventory/list_iam_roles.py +518 -0
  144. runbooks/inventory/list_iam_saml_providers.py +359 -0
  145. runbooks/inventory/list_lambda_functions.py +882 -0
  146. runbooks/inventory/list_org_accounts.py +446 -0
  147. runbooks/inventory/list_org_accounts_users.py +354 -0
  148. runbooks/inventory/list_rds_db_instances.py +406 -0
  149. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  150. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  151. runbooks/inventory/list_sns_topics.py +360 -0
  152. runbooks/inventory/list_ssm_parameters.py +402 -0
  153. runbooks/inventory/list_vpc_subnets.py +433 -0
  154. runbooks/inventory/list_vpcs.py +422 -0
  155. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  156. runbooks/inventory/models/__init__.py +24 -0
  157. runbooks/inventory/models/account.py +192 -0
  158. runbooks/inventory/models/inventory.py +309 -0
  159. runbooks/inventory/models/resource.py +247 -0
  160. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  161. runbooks/inventory/requirements.txt +12 -0
  162. runbooks/inventory/run_on_multi_accounts.py +211 -0
  163. runbooks/inventory/tests/common_test_data.py +3661 -0
  164. runbooks/inventory/tests/common_test_functions.py +204 -0
  165. runbooks/inventory/tests/script_test_data.py +0 -0
  166. runbooks/inventory/tests/setup.py +24 -0
  167. runbooks/inventory/tests/src.py +18 -0
  168. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  169. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  170. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  171. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  172. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  173. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  174. runbooks/inventory/update_aws_actions.py +173 -0
  175. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  176. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  177. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  178. runbooks/inventory/update_s3_public_access_block.py +539 -0
  179. runbooks/inventory/utils/__init__.py +23 -0
  180. runbooks/inventory/utils/aws_helpers.py +510 -0
  181. runbooks/inventory/utils/threading_utils.py +493 -0
  182. runbooks/inventory/utils/validation.py +682 -0
  183. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  184. runbooks/main.py +785 -0
  185. runbooks/organizations/__init__.py +12 -0
  186. runbooks/organizations/manager.py +374 -0
  187. runbooks/security_baseline/README.md +324 -0
  188. runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
  189. runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
  190. runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
  191. runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
  192. runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
  193. runbooks/security_baseline/checklist/root_access_key.py +6 -1
  194. runbooks/security_baseline/config-origin.json +1 -1
  195. runbooks/security_baseline/config.json +1 -1
  196. runbooks/security_baseline/permission.json +1 -1
  197. runbooks/security_baseline/report_generator.py +10 -2
  198. runbooks/security_baseline/report_template_en.html +7 -7
  199. runbooks/security_baseline/report_template_jp.html +7 -7
  200. runbooks/security_baseline/report_template_kr.html +12 -12
  201. runbooks/security_baseline/report_template_vn.html +7 -7
  202. runbooks/security_baseline/requirements.txt +7 -0
  203. runbooks/security_baseline/run_script.py +8 -2
  204. runbooks/security_baseline/security_baseline_tester.py +10 -2
  205. runbooks/security_baseline/utils/common.py +5 -1
  206. runbooks/utils/__init__.py +204 -0
  207. runbooks-0.6.1.dist-info/METADATA +373 -0
  208. runbooks-0.6.1.dist-info/RECORD +237 -0
  209. {runbooks-0.2.5.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
  210. runbooks-0.6.1.dist-info/entry_points.txt +7 -0
  211. runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
  212. runbooks-0.6.1.dist-info/top_level.txt +3 -0
  213. runbooks/python101/calculator.py +0 -34
  214. runbooks/python101/config.py +0 -1
  215. runbooks/python101/exceptions.py +0 -16
  216. runbooks/python101/file_manager.py +0 -218
  217. runbooks/python101/toolkit.py +0 -153
  218. runbooks-0.2.5.dist-info/METADATA +0 -439
  219. runbooks-0.2.5.dist-info/RECORD +0 -61
  220. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  221. runbooks-0.2.5.dist-info/top_level.txt +0 -1
@@ -0,0 +1,341 @@
1
+ def del_vpc(ocredentials, fVPCId, fRegion):
2
+ """
3
+ ocredentials looks like this:
4
+ session_vpc = boto3.Session(
5
+ aws_access_key_id = ocredentials['AccessKeyId'],
6
+ aws_secret_access_key = ocredentials['SecretAccessKey'],
7
+ aws_session_token = ocredentials['SessionToken'],
8
+ region_name=fRegion)
9
+ fVPCId is a string with the VPC ID in it
10
+ fRegion is the string that represents the AWS region name
11
+ """
12
+
13
+ import logging
14
+
15
+ import boto3
16
+ from botocore.exceptions import ClientError
17
+ from colorama import Fore, init
18
+
19
+ # ERASE_LINE = '\x1b[2K'
20
+
21
+ def find_and_delete_vpc_endpoints(fVPC_client, fVpcId, fRegion):
22
+ import logging
23
+
24
+ vpc_endpoints = fVPC_client.describe_vpc_endpoints(Filters=[{"Name": "vpc-id", "Values": [fVpcId]}])
25
+ vpc_endpoints_to_delete = []
26
+ logging.info("Found %s vpc endpoints", len(vpc_endpoints))
27
+ for x in range(len(vpc_endpoints["VpcEndpoints"])):
28
+ vpc_endpoints_to_delete.append(vpc_endpoints["VpcEndpoints"][x]["VpcEndpointId"])
29
+
30
+ logging.warning("Found %s endpoints in vpc", len(vpc_endpoints_to_delete))
31
+ if len(vpc_endpoints_to_delete) > 0:
32
+ try:
33
+ response = fVPC_client.delete_vpc_endpoints(VpcEndpointIds=vpc_endpoints_to_delete)
34
+ return 0
35
+ except ClientError as my_error:
36
+ print(my_error)
37
+ return 1
38
+ else:
39
+ logging.warning("No Endpoints found to delete")
40
+ return 0
41
+
42
+ def find_and_delete_vpc_security_groups(fVPC_client, fVpcId, fRegion):
43
+ from botocore.exceptions import ClientError
44
+
45
+ vpc_security_groups = fVPC_client.describe_security_groups(Filters=[{"Name": "vpc-id", "Values": [fVpcId]}])
46
+ for x in range(len(vpc_security_groups["SecurityGroups"])):
47
+ if vpc_security_groups["SecurityGroups"][x]["GroupName"] == "default":
48
+ logging.info("Only found default security groups. These will auto-delete")
49
+ continue
50
+ else:
51
+ try:
52
+ logging.info("Deleting security group %s", vpc_security_groups["SecurityGroups"][x]["GroupId"])
53
+ response = fVPC_client.delete_security_group(
54
+ GroupId=vpc_security_groups["SecurityGroups"][x]["GroupId"]
55
+ )
56
+ except ClientError as my_Error:
57
+ print(my_Error)
58
+ return 1
59
+ return 0
60
+
61
+ def find_and_delete_vpc_peering_connections(fVPC_client, fVpcId, fRegion):
62
+ from botocore.exceptions import ClientError
63
+
64
+ vpc_peering_connections = fVPC_client.describe_vpc_peering_connections(
65
+ Filters=[{"Name": "requester-vpc-info.vpc-id", "Values": [fVpcId]}]
66
+ )
67
+ for x in range(len(vpc_peering_connections["VpcPeeringConnections"])):
68
+ try:
69
+ response = fVPC_client.delete_vpc_peering_connection(
70
+ VpcPeeringConnectionId=vpc_peering_connections["VpcPeeringConnections"][x]["VpcPeeringConnectionId"]
71
+ )
72
+ except ClientError as my_Error:
73
+ print(my_Error)
74
+ return 1
75
+ return 0
76
+
77
+ def find_and_delete_vpc_route_tables(fVPC_client, fVpcId, fRegion):
78
+ from botocore.exceptions import ClientError
79
+
80
+ vpc_route_tables = fVPC_client.describe_route_tables(Filters=[{"Name": "vpc-id", "Values": [fVpcId]}])
81
+ vpc_route_tables_to_delete = list()
82
+ for x in range(len(vpc_route_tables["RouteTables"])):
83
+ rRouteTableId = vpc_route_tables["RouteTables"][x]["RouteTableId"]
84
+ # Add all route tables to the delete list...
85
+ vpc_route_tables_to_delete.append(rRouteTableId)
86
+ for y in range(len(vpc_route_tables["RouteTables"][x]["Associations"])):
87
+ rIsMain = vpc_route_tables["RouteTables"][x]["Associations"][y]["Main"]
88
+ # However, since we can't disassociate the "Main" Route Table, and we can't delete it, we remove it from our list.
89
+ if rIsMain:
90
+ vpc_route_tables_to_delete.remove(rRouteTableId)
91
+ continue
92
+ rRouteAssociation = vpc_route_tables["RouteTables"][x]["Associations"][y]["RouteTableAssociationId"]
93
+ try:
94
+ disassociateresponse = fVPC_client.disassociate_route_table(AssociationId=rRouteAssociation)
95
+ logging.critical("Disassociated Route Table ID: %s", rRouteTableId)
96
+ except ClientError as my_Error:
97
+ print(my_Error)
98
+ return 1
99
+
100
+ # pprint.pprint(vpc_route_tables_to_delete)
101
+ for RtTbl in vpc_route_tables_to_delete:
102
+ try:
103
+ deleteresponse = fVPC_client.delete_route_table(RouteTableId=RtTbl)
104
+ print("Deleted Route Table ID:", RtTbl)
105
+ vpc_route_tables_to_delete.remove(RtTbl)
106
+ except ClientError as my_Error:
107
+ print(my_Error)
108
+ return 1
109
+ return 0
110
+
111
+ def find_and_delete_vpc_nacls(fVPC_client, fVpcId, fRegion):
112
+ from botocore.exceptions import ClientError
113
+
114
+ vpc_nacls = fVPC_client.describe_network_acls(Filters=[{"Name": "vpc-id", "Values": [fVpcId]}])
115
+
116
+ for x in range(len(vpc_nacls["NetworkAcls"])):
117
+ if vpc_nacls["NetworkAcls"][x]["IsDefault"]:
118
+ continue
119
+ else:
120
+ try:
121
+ response = fVPC_client.delete_network_acl(NetworkAclId=vpc_nacls["NetworkAcls"][x]["NetworkAclId"])
122
+ # pprint.pprint(response)
123
+ except ClientError as my_Error:
124
+ print(my_Error)
125
+ return 1
126
+ return 0
127
+
128
+ def find_and_delete_subnets(fVPC_client, fVpcId, fRegion):
129
+ from botocore.exceptions import ClientError
130
+
131
+ subnets = fVPC_client.describe_subnets(Filters=[{"Name": "vpc-id", "Values": [fVpcId]}])
132
+ # print("Found",len(subnets['Subnets']),"Subnets")
133
+
134
+ # pprint.pprint(vpc_nacls)
135
+ # pprint.pprint(vpc_nacls['NetworkAcls'][0]['IsDefault'])
136
+ # print("There are "+str(len(subnets['Subnets']))+" subnets")
137
+ # print("There are "+str(len(vpc_route_tables['RouteTables'][0]['Routes']))+" routes in the first table")
138
+ for x in range(len(subnets["Subnets"])):
139
+ try:
140
+ rSubnetId = subnets["Subnets"][x]["SubnetId"]
141
+ response = fVPC_client.delete_subnet(SubnetId=rSubnetId)
142
+ # pprint.pprint(response)
143
+ except ClientError as my_Error:
144
+ print(my_Error)
145
+ return 1
146
+ return 0
147
+
148
+ def find_and_delete_NAT_gateways(fVPC_client, fVpcId, fRegion):
149
+ import time
150
+
151
+ from botocore.exceptions import ClientError
152
+
153
+ cyclesWaited = 0
154
+ nat_gateways = fVPC_client.describe_nat_gateways(
155
+ Filters=[{"Name": "vpc-id", "Values": [fVpcId]}, {"Name": "state", "Values": ["available"]}]
156
+ )
157
+ rNatGWList = []
158
+ if len(nat_gateways["NatGateways"]) > 0:
159
+ logging.info("Found %s NAT Gateways", len(nat_gateways["NatGateways"]))
160
+ for x in range(len(nat_gateways["NatGateways"])):
161
+ rNatGWList.append(nat_gateways["NatGateways"][x]["NatGatewayId"])
162
+ try:
163
+ deleteresponse = fVPC_client.delete_nat_gateway(NatGatewayId=rNatGWList[x])
164
+ except ClientError as my_Error:
165
+ print(my_Error)
166
+ return 1
167
+ print("Waiting for the NAT Gateways to be fully deleted")
168
+ verify_nat_gws_are_gone = nat_gateways
169
+ while len(verify_nat_gws_are_gone["NatGateways"]) > 0:
170
+ verify_nat_gws_are_gone = fVPC_client.describe_nat_gateways(
171
+ Filters=[{"Name": "state", "Values": ["available", "pending", "deleting", "failed"]}],
172
+ NatGatewayIds=rNatGWList,
173
+ )
174
+ cyclesWaited += 1
175
+ if cyclesWaited % 5 == 0:
176
+ logging.info("Still waiting on NAT Gateways to be deleted...")
177
+ # print(".", end='', flush=True)
178
+ time.sleep(10)
179
+ return 0
180
+
181
+ def find_and_delete_gateways(fVPC_client, fVpcId, fRegion):
182
+ from botocore.exceptions import ClientError
183
+
184
+ gateways = fVPC_client.describe_internet_gateways(Filters=[{"Name": "attachment.vpc-id", "Values": [fVpcId]}])
185
+ for x in range(len(gateways["InternetGateways"])):
186
+ rGatewayId = gateways["InternetGateways"][x]["InternetGatewayId"]
187
+ try:
188
+ detachresponse = fVPC_client.detach_internet_gateway(InternetGatewayId=rGatewayId, VpcId=fVpcId)
189
+ except ClientError as my_Error:
190
+ print(my_Error)
191
+ return 1
192
+ try:
193
+ deleteresponse = fVPC_client.delete_internet_gateway(InternetGatewayId=rGatewayId)
194
+ except ClientError as my_Error:
195
+ print(my_Error)
196
+ return 1
197
+ return 0
198
+
199
+ def find_and_delete_virtual_gateways(fVPC_client, fVpcId, fRegion):
200
+ import time
201
+
202
+ from botocore.exceptions import ClientError
203
+
204
+ cyclesWaited = 0
205
+ vgws = fVPC_client.describe_vpn_gateways(
206
+ Filters=[
207
+ {"Name": "attachment.vpc-id", "Values": [fVpcId]},
208
+ {"Name": "attachment.state", "Values": ["attached"]},
209
+ ]
210
+ )
211
+ rVPN_GatewayList = list()
212
+ for x in range(len(vgws["VpnGateways"])):
213
+ rVPN_GatewayList.append(vgws["VpnGateways"][x]["VpnGatewayId"])
214
+ try:
215
+ detachresponse = fVPC_client.detach_vpn_gateway(VpnGatewayId=rVPN_GatewayList[x], VpcId=fVpcId)
216
+ except ClientError as my_Error:
217
+ print(my_Error)
218
+ return 1
219
+
220
+ print("Waiting for the VPN Gateways to be fully detached")
221
+ verify_vgws_are_gone = vgws
222
+ while len(verify_vgws_are_gone["VpnGateways"]) > 0:
223
+ verify_vgws_are_gone = fVPC_client.describe_vpn_gateways(
224
+ Filters=[
225
+ {"Name": "attachment.state", "Values": ["attached", "detaching"]},
226
+ ],
227
+ VpnGatewayIds=rVPN_GatewayList,
228
+ )
229
+ cyclesWaited += 1
230
+ if cyclesWaited % 5 == 0:
231
+ logging.info("Still waiting on VGWS to be deleted...")
232
+ # pprint.pprint(verify_nat_gws_are_gone)
233
+ time.sleep(10)
234
+ return 0
235
+
236
+ def delete_vpc(fVPC_client, fVpcId, fRegion):
237
+ from botocore.exceptions import ClientError
238
+
239
+ try:
240
+ response = fVPC_client.delete_vpc(VpcId=fVpcId)
241
+ return 0
242
+ except ClientError as my_Error:
243
+ print(my_Error)
244
+ return 1
245
+
246
+ ###### Main ########################################
247
+ session_vpc = boto3.Session(
248
+ aws_access_key_id=ocredentials["AccessKeyId"],
249
+ aws_secret_access_key=ocredentials["SecretAccessKey"],
250
+ aws_session_token=ocredentials["SessionToken"],
251
+ region_name=fRegion,
252
+ )
253
+ client_vpc = session_vpc.client("ec2")
254
+ try:
255
+ # 1. Call EC2.Client.describe_vpc_endpoints. Filter on your VPC id. Call EC2.client.delete_vpc_endpoints on each
256
+ print(f"Deleting vpc in {fRegion}...", end="", flush=True)
257
+
258
+ logging.info(f"Deleting vpc-endpoints... for vpc {fVPCId} in region {fRegion}")
259
+ print(".", end="", flush=True)
260
+ ResultGood = find_and_delete_vpc_endpoints(client_vpc, fVPCId, fRegion) == 0
261
+ if not ResultGood:
262
+ logging.error("Something failed in the vpc_endpoints deletion script")
263
+ return 1 # out of the try
264
+ # 2. Call VPC.security_groups. Delete the group unless its group_name attribute is "main". The main security group will be deleted via VPC.delete().
265
+ logging.info("Deleting security groups...")
266
+ print(".", end="", flush=True)
267
+ ResultGood = find_and_delete_vpc_security_groups(client_vpc, fVPCId, fRegion) == 0
268
+ if not ResultGood:
269
+ logging.error("Something failed in the vpc_security_group deletion script")
270
+ return 1 # out of the try
271
+
272
+ # 3. Call EC2.Client.describe_vpc_peering_connections. Filter on your VPC id as the requester-vpc-info.vpc-id. (My VPC is a requester. There is also accepter-vpc-info.vpc-id among other filters.) Iterate through the entries keyed by VpcPeeringConnections. Get an instance of the peering connection by instantiating a EC2.ServiceResource.VpcPeeringConnection with the VpcPeeringConnectionId. Call VpcPeeringConnection.delete() to remove the peering connection.
273
+ logging.info("Deleting vpc peering connections...")
274
+ print(".", end="", flush=True)
275
+ ResultGood = find_and_delete_vpc_peering_connections(client_vpc, fVPCId, fRegion) == 0
276
+ if not ResultGood:
277
+ logging.error("Something failed in the vpc_peering_connection deletion script")
278
+ return 1 # out of the try
279
+
280
+ # Need to figure a way to wait on this operation, if there are Gateways to be deleted.
281
+ logging.info("Deleting NAT Gateways...")
282
+ print(".", end="", flush=True)
283
+ ResultGood = find_and_delete_NAT_gateways(client_vpc, fVPCId, fRegion) == 0
284
+ if not ResultGood:
285
+ logging.error("Something failed in the vpc_NAT_gateways deletion script")
286
+ return 1 # out of the try
287
+
288
+ # 4. Call vpc.route_tables.all() and iterate through the route tables. For each route table, iterate through its routes using the RouteTable.routes attribute. Delete the routes where route['Origin'] is 'CreateRoute'. I deleted using EC2.Client.delete_route using EC2.RouteTable.id and route['DestinationCidrBlock']. After removing the routes, call EC2.RouteTable.delete() to remove the route table itself. I set up exception handlers for each delete. Not every route table can be deleted, but I haven't cracked the code. Maybe next week.
289
+ logging.info("Deleting vpc route tables...")
290
+ print(".", end="", flush=True)
291
+ ResultGood = find_and_delete_vpc_route_tables(client_vpc, fVPCId, fRegion) == 0
292
+ if not ResultGood:
293
+ logging.error("Something failed in the vpc_route_tables deletion script")
294
+ return 1 # out of the try
295
+
296
+ # 5. Iterate through vpc.network_acls.all(), test12 the NetworkAcl.is_default attribute and call NetworkAcl.delete for non-default acls.
297
+ logging.info("Deleting vpc network access control lists...")
298
+ print(".", end="", flush=True)
299
+ ResultGood = find_and_delete_vpc_nacls(client_vpc, fVPCId, fRegion) == 0
300
+ if not ResultGood:
301
+ logging.error("Something failed in the vpc_nacls deletion script")
302
+ return 1 # out of the try
303
+
304
+ #
305
+ # 6. Iterate through vpc.subnets.all().network_interfaces.all(). Call EC2.NetworkInterface.delete() on each.
306
+ logging.info("Deleting subnets...")
307
+ print(".", end="", flush=True)
308
+ ResultGood = find_and_delete_subnets(client_vpc, fVPCId, fRegion) == 0
309
+ if not ResultGood:
310
+ logging.error("Something failed in the vpc_subnets deletion script")
311
+ return 1 # out of the try
312
+ #
313
+ # 7. Iterate through vpc.internet_gateways.all(). Call EC2.InternetGateway.delete() on each.
314
+ logging.info("Deleting Internet Gateways...")
315
+ print(".", end="", flush=True)
316
+ ResultGood = find_and_delete_gateways(client_vpc, fVPCId, fRegion) == 0
317
+ if not ResultGood:
318
+ logging.error("Something failed in the vpc_gateways deletion script")
319
+ return 1 # out of the try
320
+
321
+ # Virtual Gateway
322
+ logging.info("Deleting Virtual Customer Gateways...")
323
+ print(".", end="", flush=True)
324
+ ResultGood = find_and_delete_virtual_gateways(client_vpc, fVPCId, fRegion) == 0
325
+ if not ResultGood:
326
+ logging.error("Something failed in the vpc_virtual_gateways deletion script")
327
+ return 1 # out of the try
328
+
329
+ #
330
+ # 8. Call vpc.delete()
331
+ print("!")
332
+ ResultGood = delete_vpc(client_vpc, fVPCId, fRegion) == 0
333
+ if not ResultGood:
334
+ logging.error("Something failed in the final vpc deletion script")
335
+ return 1 # out of the try
336
+ except ClientError as my_Error:
337
+ print(my_Error)
338
+ print(f"{Fore.RED}What to do now?{Fore.RESET}")
339
+ return 1
340
+
341
+ return 0
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ AWS CloudFormation Stack Drift Detection Enablement Script
5
+
6
+ This enterprise-grade script provides comprehensive CloudFormation drift detection enablement
7
+ across multi-account AWS Organizations environments. Designed for infrastructure teams managing
8
+ CloudFormation stacks at scale, offering automated drift detection initialization, stack
9
+ discovery with filtering capabilities, and organizational governance support for infrastructure
10
+ configuration management and compliance monitoring.
11
+
12
+ Key Features:
13
+ - Multi-account, multi-region CloudFormation stack discovery and drift detection enablement
14
+ - Fragment-based stack filtering for targeted drift detection operations
15
+ - Stack status filtering supporting active and deleted stack analysis
16
+ - Enterprise governance support with organizational context and account exclusion
17
+ - Comprehensive error handling for authorization and access control issues
18
+ - Progress tracking and operational feedback for large-scale drift detection operations
19
+
20
+ Drift Detection Capabilities:
21
+ - Automated drift detection enablement for discovered CloudFormation stacks
22
+ - Stack-level drift detection initialization with operational logging
23
+ - Regional drift detection coverage for comprehensive infrastructure monitoring
24
+ - Fragment-based stack targeting for selective drift detection operations
25
+ - Status-based stack filtering for active vs historical stack analysis
26
+
27
+ Authentication & Access:
28
+ - AWS Organizations support for centralized CloudFormation stack management
29
+ - Cross-account role assumption for organizational stack visibility
30
+ - Regional validation and opt-in status verification for CloudFormation availability
31
+ - Profile-based authentication with comprehensive credential management
32
+
33
+ Performance & Scalability:
34
+ - Progress bars and operational feedback for large-scale drift detection operations
35
+ - Efficient credential management for multi-account stack enumeration
36
+ - Regional optimization with targeted CloudFormation API calls
37
+ - Memory-efficient processing for extensive stack inventories
38
+
39
+ Enterprise Use Cases:
40
+ - Infrastructure configuration drift detection and compliance monitoring
41
+ - CloudFormation stack governance and change management automation
42
+ - Organizational infrastructure audit and drift analysis
43
+ - Automated configuration compliance validation across organizational accounts
44
+
45
+ Security & Compliance:
46
+ - Account exclusion capabilities preventing drift detection on sensitive accounts
47
+ - Comprehensive audit logging for drift detection operations and stack access
48
+ - Regional access validation preventing unauthorized stack enumeration
49
+ - Safe credential handling with automatic session management
50
+
51
+ Dependencies:
52
+ - boto3: AWS SDK for CloudFormation API access
53
+ - colorama: Terminal output formatting and colored display
54
+ - Custom modules: Inventory_Modules, ArgumentsClass, account_class for enterprise operations
55
+
56
+ Output Format:
57
+ - Real-time progress feedback with account and region context
58
+ - Comprehensive drift detection summary with stack counts and regional coverage
59
+ - Operational logging for audit trails and troubleshooting
60
+
61
+ Future Enhancements:
62
+ - Drift detection status monitoring using describe_stack_drift_detection_status
63
+ - Enhanced progress tracking and drift detection result analysis
64
+ - Multi-threaded processing for improved performance at scale
65
+ """
66
+
67
+ import logging
68
+
69
+ import Inventory_Modules
70
+ from account_class import aws_acct_access
71
+ from ArgumentsClass import CommonArguments
72
+ from botocore.exceptions import ClientError
73
+ from colorama import Fore, init
74
+
75
+ init()
76
+ __version__ = "2023.05.04"
77
+
78
+ # Configure comprehensive argument parsing for CloudFormation drift detection operations
79
+ parser = CommonArguments()
80
+ parser.singleprofile() # Single AWS profile for organizational drift detection management
81
+ parser.multiregion() # Multi-region support for comprehensive CloudFormation coverage
82
+ parser.verbosity() # Logging verbosity controls for operational visibility
83
+ parser.version(__version__)
84
+
85
+ # Enhanced CLI arguments for targeted CloudFormation drift detection operations
86
+ parser.my_parser.add_argument(
87
+ "-f",
88
+ "--fragment",
89
+ dest="pstackfrag",
90
+ metavar="CloudFormation stack fragment",
91
+ default="all",
92
+ help="String fragment of CloudFormation stack names for targeted drift detection filtering. "
93
+ "Supports partial name matching for flexible stack identification and selective drift analysis.",
94
+ )
95
+ parser.my_parser.add_argument(
96
+ "-s",
97
+ "--status",
98
+ dest="pstatus",
99
+ metavar="CloudFormation status",
100
+ default="active",
101
+ help="Stack status filter determining drift detection scope. "
102
+ "'active' for CREATE_COMPLETE stacks only, 'all' includes DELETE_COMPLETE stacks for historical analysis.",
103
+ )
104
+ parser.my_parser.add_argument(
105
+ "-k",
106
+ "--skip",
107
+ dest="pSkipAccounts",
108
+ nargs="*",
109
+ metavar="Accounts to leave alone",
110
+ default=[],
111
+ help="Account exclusion list preventing drift detection on sensitive or core organizational accounts. "
112
+ "Provides safety controls for production and management account protection.",
113
+ )
114
+ args = parser.my_parser.parse_args()
115
+
116
+ # Extract and configure parameters from parsed command-line arguments
117
+ pProfile = args.Profile # AWS profile for organizational CloudFormation access
118
+ pRegionList = args.Regions # Target regions for comprehensive drift detection coverage
119
+ pstackfrag = args.pstackfrag # Stack name fragments for targeted drift detection filtering
120
+ pstatus = args.pstatus # Stack status filter for active vs historical analysis
121
+ AccountsToSkip = args.pSkipAccounts # Account exclusion list for production safety
122
+ verbose = args.loglevel # Logging verbosity for operational visibility
123
+
124
+ # Configure comprehensive logging for CloudFormation drift detection operations
125
+ logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
126
+ logging.getLogger("boto3").setLevel(logging.CRITICAL) # Suppress AWS SDK logging
127
+ logging.getLogger("botocore").setLevel(logging.CRITICAL) # Suppress AWS core logging
128
+ logging.getLogger("s3transfer").setLevel(logging.CRITICAL) # Suppress S3 transfer logging
129
+ logging.getLogger("urllib3").setLevel(logging.CRITICAL) # Suppress HTTP logging
130
+
131
+ """
132
+ Future Enhancement: Drift Detection Status Monitoring
133
+ We should eventually create an argument here that would check on the status of the drift-detection using
134
+ "describe_stack_drift_detection_status", but we haven't created that function yet...
135
+ https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html#CloudFormation.Client.describe_stack_drift_detection_status
136
+
137
+ This would enable:
138
+ - Real-time drift detection progress monitoring
139
+ - Drift detection result analysis and reporting
140
+ - Automated drift detection completion verification
141
+ - Enhanced operational visibility for large-scale drift operations
142
+ """
143
+
144
+ ##########################
145
+ ERASE_LINE = "\x1b[2K" # Terminal control for dynamic output updates
146
+
147
+ # Initialize AWS account access and organizational context for drift detection
148
+ aws_acct = aws_acct_access(pProfile)
149
+ sts_client = aws_acct.session.client("sts")
150
+
151
+ # Handle organizational vs single account drift detection scope
152
+ if aws_acct.AccountType == "Root":
153
+ print()
154
+ # Default to organizational drift detection for automation compatibility
155
+ try:
156
+ # Interactive prompt for organizational drift detection scope selection
157
+ answer = input(
158
+ f"You've specified a root account to check. \n"
159
+ f"Do you want to check the entire Org or only the root account? (Enter 'root' for whole Org):"
160
+ )
161
+ except EOFError:
162
+ # Handle non-interactive mode (automated testing)
163
+ print("Non-interactive mode detected, defaulting to root account only")
164
+ answer = "single"
165
+
166
+ if answer == "root":
167
+ # Enable organizational drift detection across all child accounts
168
+ ChildAccounts = aws_acct.ChildAccounts
169
+ else:
170
+ # Restrict drift detection to management account only
171
+ ChildAccounts = [
172
+ {
173
+ "MgmtAccount": aws_acct.acct_number, # Management account identifier
174
+ "AccountId": aws_acct.acct_number, # Account number for single account scope
175
+ "AccountEmail": aws_acct.MgmtEmail, # Management email for audit context
176
+ "AccountStatus": aws_acct.AccountStatus, # Account operational status
177
+ }
178
+ ]
179
+
180
+ # Initialize drift detection operation tracking and regional scope configuration
181
+ NumStacksFound = 0 # Counter for tracking total stacks processed for drift detection
182
+ print()
183
+
184
+ # Configure regional scope for comprehensive CloudFormation drift detection coverage
185
+ RegionList = Inventory_Modules.get_service_regions("cloudformation", pRegionList)
186
+
187
+ # Note: Alternative account discovery approaches for organizational drift detection
188
+ # ChildAccounts = Inventory_Modules.find_child_accounts2(pProfile) # Direct account discovery
189
+ # ChildAccounts = Inventory_Modules.RemoveCoreAccounts(ChildAccounts, AccountsToSkip) # Account filtering
190
+
191
+ # Configure display formatting for drift detection progress and results
192
+ fmt = "%-20s %-15s %-15s %-50s"
193
+ print(fmt % ("Account", "Region", "Stack Status", "Stack Name"))
194
+ print(fmt % ("-------", "------", "------------", "----------"))
195
+
196
+ # Execute comprehensive CloudFormation drift detection across organizational accounts and regions
197
+ StacksFound = [] # Aggregated list for discovered stacks requiring drift detection
198
+ for account in ChildAccounts:
199
+ # Note: Alternative role ARN construction for cross-account CloudFormation access
200
+ # role_arn = f"arn:aws:iam::{account['AccountId']}:role/AWSCloudFormationStackSetExecutionRole"
201
+ # logging.info(f"Role ARN: {role_arn}")
202
+
203
+ try:
204
+ # Establish cross-account credentials for CloudFormation stack access
205
+ account_credentials = Inventory_Modules.get_child_access3(
206
+ aws_acct,
207
+ account["AccountId"],
208
+ )
209
+
210
+ # Validate account access and skip failed credential attempts
211
+ if account_credentials["AccessError"]:
212
+ logging.error(f"Accessing account {account['AccountId']} didn't work, so we're skipping it")
213
+ continue
214
+
215
+ except ClientError as my_Error:
216
+ # Handle comprehensive AWS API authorization and access errors
217
+ if "AuthFailure" in str(my_Error):
218
+ print(f"{pProfile}: Authorization Failure for account {account['AccountId']}")
219
+ elif str(my_Error).find("AccessDenied") > 0:
220
+ print(f"{pProfile}: Access Denied Failure for account {account['AccountId']}")
221
+ else:
222
+ print(f"{pProfile}: Other kind of failure for account {account['AccountId']}")
223
+ print(my_Error)
224
+ break
225
+
226
+ # Iterate through regions for comprehensive regional drift detection coverage
227
+ for region in RegionList:
228
+ Stacks = [] # Regional stack collection for drift detection processing
229
+ try:
230
+ StackNum = 0 # Regional stack counter for progress tracking
231
+
232
+ # Discover CloudFormation stacks with fragment and status filtering
233
+ Stacks = Inventory_Modules.find_stacks2(account_credentials, region, pstackfrag, pstatus)
234
+
235
+ # Log regional stack discovery progress for operational visibility
236
+ logging.warning(f"Account: {account['AccountId']} | Region: {region} | Found {StackNum} Stacks")
237
+ logging.info(
238
+ f"{ERASE_LINE}{Fore.RED}Account: {account['AccountId']} Region: {region} Found {StackNum} Stacks{Fore.RESET}"
239
+ )
240
+
241
+ except ClientError as my_Error:
242
+ # Handle regional authorization failures during stack discovery
243
+ if "AuthFailure" in str(my_Error):
244
+ print(f"{account['AccountId']}: Authorization Failure")
245
+
246
+ # Process discovered stacks for drift detection enablement
247
+ for Stack in Stacks:
248
+ # Extract stack metadata for drift detection operations
249
+ StackName = Stack["StackName"] # Human-readable stack identifier
250
+ StackStatus = Stack["StackStatus"] # Current operational status
251
+ StackID = Stack["StackId"] # Unique CloudFormation stack identifier
252
+
253
+ # Enable drift detection on discovered CloudFormation stack
254
+ DriftStatus = Inventory_Modules.enable_drift_on_stacks2(account_credentials, region, StackName)
255
+
256
+ # Log drift detection enablement for audit trail and operational tracking
257
+ logging.error(
258
+ f"Enabled drift detection on {StackName} in account {account_credentials['AccountNumber']} in region {region}"
259
+ )
260
+ NumStacksFound += 1 # Increment total drift detection counter
261
+
262
+ # Provide comprehensive operational summary with drift detection metrics
263
+ print(ERASE_LINE)
264
+ print(
265
+ f"{Fore.RED}Looked through {NumStacksFound} Stacks across {len(ChildAccounts)} accounts across "
266
+ f"{len(RegionList)} regions{Fore.RESET}"
267
+ )
268
+ print()
269
+
270
+ # Display completion message for drift detection operation
271
+ print("Thanks for using this script...")
272
+ print()