Skip to content

Commit d847932

Browse files
committed
add cloudformation deletion script
1 parent 7208f19 commit d847932

File tree

3 files changed

+58
-4
lines changed

3 files changed

+58
-4
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ share/python-wheels/
2626
.installed.cfg
2727
*.egg
2828
MANIFEST
29-
29+
.vscode/
3030
# PyInstaller
3131
# Usually these files are written by a python script from a template
3232
# before PyInstaller builds the exe, so as to inject date/other infos into it.

aws/reporting/cloudformation.py

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import boto3
2+
import logging
3+
from common import get_all_regions
4+
5+
DELETEABLE_STATUS = ["CREATE_FAILED", "DELETE_FAILED"]
6+
7+
AWS_RESOURCE_TYPE_EC2_INSTANCE = "AWS::EC2::Instance"
8+
9+
AWS_EKS_MANAGED_TAGS = ["alpha.eksctl.io/cluster-name"]
10+
11+
logger = logging.getLogger(__name__)
12+
13+
def get_deleteable_cf_templates(client):
14+
deleteable_stacks = []
15+
response = client.describe_stacks()
16+
for stack in response.get('Stacks', []):
17+
stackName = stack.get("StackName", "")
18+
if stackName != "":
19+
is_eks_managed = False
20+
for tag in stack.get('Tags', []):
21+
if tag.get('Key', '') in AWS_EKS_MANAGED_TAGS:
22+
is_eks_managed = True
23+
logger.info("{} Found EKS managed stack {}".format(client.meta.region_name, stackName))
24+
if not is_eks_managed:
25+
if stack.get('StackStatus', '') in DELETEABLE_STATUS:
26+
deleteable_stacks.append(stack)
27+
logger.info("{} Found stack with deleteable status {}".format(client.meta.region_name, stackName))
28+
elif not does_cf_template_have_ec2_instances(client, stackName):
29+
deleteable_stacks.append(stack)
30+
logger.info("{} Found stack without instances {}".format(client.meta.region_name, stackName))
31+
return deleteable_stacks
32+
33+
def does_cf_template_have_ec2_instances(client, stack_name: str):
34+
for resource in client.describe_stack_resources(StackName=stack_name)['StackResources']:
35+
if resource.get('ResourceType', '') == AWS_RESOURCE_TYPE_EC2_INSTANCE:
36+
return True
37+
return False
38+
39+
def delete_stacks(dry_run = False):
40+
for region in get_all_regions():
41+
client = boto3.client('cloudformation', region_name=region)
42+
stacks = get_deleteable_cf_templates(client)
43+
for stack in stacks:
44+
stackName = stack.get("StackName", "")
45+
if stackName != "":
46+
try:
47+
logger.info("{} Attempting to delete stack {}".format(region, stackName))
48+
if not dry_run:
49+
client.delete_stack(StackName=stackName)
50+
logger.info("{} Deleted stack {}".format(region, stackName))
51+
except:
52+
logger.info("{} Failed deleting stack {}".format(region, stackName))

aws/reporting/main.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from common import save_to_file, load_from_file
1414
from s3 import get_all_buckets, reformat_buckets_data
1515
from vpc import get_all_vpcs, delete_orphan_vpcs
16+
from cloudformation import delete_stacks
1617

1718
# number of days to qualify an instance as old
1819
OLD_INSTANCE_THRESHOLD = 30
@@ -147,11 +148,11 @@ def get_old_instances_email_summary(oldInstancesSheet, allInstancesSheet, summar
147148
summary_email += "- {} unsaved instances owned by {} associated with GUIDs <b>{}</b><br>"\
148149
.format(v.get('count'), k, v.get('guids'))
149150
if len(orphan_instances) > 0:
150-
summary_email += "\n Following instances could not be associated with owners:\n"
151+
summary_email += "<br><br> Following instances could not be associated with owners:<br>"
151152
for inst in orphan_instances:
152153
summary_email += "- Instance Id : {}, Region : {}".format(inst['InstanceId'], inst['AvailabilityZone'])
153154
if total_ec2_deleted is not None:
154-
summary_email += "\n Total EC2 instances deleted so far: <b>{}</b>".format(str(total_ec2_deleted))
155+
summary_email += "<br><br> Total EC2 instances deleted so far: <b>{}</b><br>".format(str(total_ec2_deleted))
155156
return message.format(sheet_link, sheet_link, scheduled, summary_email)
156157

157158
if __name__ == "__main__":
@@ -242,7 +243,8 @@ def get_old_instances_email_summary(oldInstancesSheet, allInstancesSheet, summar
242243
elif args[1] == 'purge_instances':
243244
numberOfInstancesDeleted = terminate_instances(oldInstancesSheet, allInstancesSheet)
244245
summaryRow['EC2 Cleanup'] = 'Deleted {} instances'.format(numberOfInstancesDeleted)
245-
246+
delete_stacks()
247+
246248
elif args[1] == 'generate_ec2_deletion_summary':
247249
summaryEmail = get_old_instances_email_summary(oldInstancesSheet, allInstancesSheet, summarySheet)
248250
if summaryEmail is not None:

0 commit comments

Comments
 (0)