Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
config.yml
*.ignore
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,22 @@ This utility tool will delete all resources from your AWS account. Whitelisted r
- EC2 key pairs
- EC2 AMI images
- EC2 security groups
- EC2 instances
- EBS snapshots
- CloudWatch alarms
- SNS topics
- S3 buckets

### Region usage
# Assertions must pass before any resources are deleted
assertions:
account_id: "012345678901"
account_alias: your-account-iam-alias
iam_username: your-iam-username
regions:
- us-east-1
- us-east-2

### Usage

Create a config from the sample file and edit it:
Expand Down
100 changes: 74 additions & 26 deletions clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import print_function
import sys
import yaml
import pprint

class Cleaner:

Expand Down Expand Up @@ -54,10 +55,11 @@ def _simple_delete(self, describe_function, delete_function, preserve_key, list_
deletables = self._get_deletable_resources(describe_function, describe_args, preserve_key, list_key, item_key, filter_function)
self._delete_generic_resource(deletables, list_key, delete_function, item_key)

def run_safety_checks(self, sts, iam, iam_resource):
# AWS Account ID in config.yml must match the account we are accessing using an API key
def run_safety_checks(self, sts, iam, iam_resource, aws_regions_list):
# AWS Account ID in config.yml must match the account we are accessing using an API key (if null then use only account_aliases)
account_id = sts.get_caller_identity().get("Account")
assert account_id == self.config.get("assertions").get("account_id"), "Unexpected AWS Account ID, check configuration!"
if self.config.get("assertions").get("account_id"):
assert account_id == self.config.get("assertions").get("account_id"), "Unexpected AWS Account ID, check configuration!"

# AWS Account alias in config.yml must match the account alias
account_aliases = iam.list_account_aliases().get("AccountAliases")
Expand All @@ -69,7 +71,7 @@ def run_safety_checks(self, sts, iam, iam_resource):
current_user = iam_resource.CurrentUser().user_name
assert current_user == self.config.get("assertions").get("iam_username"), "Unexpected IAM User name, check configuration!"

print("You are {} on account {} ({})".format(current_user, account_id, account_alias))
print("You are {} on account {} ({}) and included regions are {}".format(current_user, account_id, account_alias, aws_regions_list))
if not self._ask("Proceed?", "no"): sys.exit()

def delete_cloudformation_stacks(self, cf):
Expand All @@ -87,6 +89,34 @@ def delete_cloudformation_stacks(self, cf):
}
self._simple_delete(cf.list_stacks, cf.delete_stack, "cloudformation", "StackSummaries", "StackName", args)

def delete_ec2_instances(self, ec2):
instances = ec2.describe_instances(
Filters=[{
'Name': 'instance-state-name',
'Values': ['running', 'stopped', 'stopping'],
}]
)
instance_list = []
#pprint.pprint(instances)
for reservation in instances["Reservations"]:
for instance in reservation["Instances"]:
instance_list.append(instance["InstanceId"])
print(instance["InstanceId"] + ":")
print("\tInstanceType: " + instance["InstanceType"])
print("\tAvailabilityZone: " + instance["Placement"]["AvailabilityZone"])
#pprint.pprint(instance_list)
if instance_list:
if self._ask("\nDelete EC2 Instances?", "no"):
response = ec2.terminate_instances(
InstanceIds=
instance_list
,
DryRun=False
)
waiter = ec2.get_waiter('instance_terminated')
waiter.wait(InstanceIds=instance_list)
#print("Response was: ", response)

def delete_key_pairs(self, ec2):
self._simple_delete(ec2.describe_key_pairs, ec2.delete_key_pair, "ec2_key_pairs", "KeyPairs", "KeyName")

Expand Down Expand Up @@ -118,6 +148,7 @@ def delete_cloudwatch_alarms(self, cloudwatch):
def delete_buckets(self, s3, s3_resource):
def delete_bucket_and_its_objects(Name):
bucket = s3_resource.Bucket(Name)
print("Bucket name: {}".format(bucket))
bucket.object_versions.delete()
bucket.delete()
self._simple_delete(s3.list_buckets, delete_bucket_and_its_objects, "s3_buckets", "Buckets", "Name")
Expand All @@ -144,33 +175,50 @@ def _get_config_from_file(filename):
config = yaml.load(stream)
return config

def get_boto_session(profile_name):
def get_boto_session(profile_name, aws_region):
import boto3
return boto3.Session(profile_name=profile_name)
return boto3.Session(profile_name=profile_name, region_name=aws_region)

if __name__ == "__main__":
config = _get_config_from_file(sys.argv[1])
cleaner = Cleaner(config)
print("Current configuration:\n", yaml.dump(config, default_flow_style=False))

boto_session = get_boto_session(config["profile_name"])

cf = boto_session.client("cloudformation")
cloudwatch = boto_session.client("cloudwatch")
ec2 = boto_session.client("ec2")
iam = boto_session.client("iam")
# Get all AWS regions
aws_regions_list = config.get("assertions").get("regions", [])
#pprint.pprint("Running for regions: %l", aws_regions_list)
#print("Running regions: {}".format(aws_regions_list))

# Query IAM and execute run_safety_checks
default_aws_region = "us-east-1"
boto_session = get_boto_session(config["profile_name"], default_aws_region)
iam = boto_session.client("iam", region_name=default_aws_region)
iam_resource = boto_session.resource("iam")
s3 = boto_session.client("s3")
s3_resource = boto_session.resource("s3")
sts = boto_session.client("sts")
sns = boto_session.client("sns")

cleaner.run_safety_checks(sts, iam, iam_resource)
cleaner.delete_cloudformation_stacks(cf)
cleaner.delete_cloudwatch_alarms(cloudwatch)
cleaner.delete_sns_topics(sns)
cleaner.delete_amis(sts, ec2)
cleaner.delete_snapshots(sts, ec2)
cleaner.delete_securitygroups(ec2)
cleaner.delete_key_pairs(ec2)
sts = boto_session.client("sts", region_name=default_aws_region)
cleaner.run_safety_checks(sts, iam, iam_resource, aws_regions_list)

# Execute for each AWS region
for aws_region in aws_regions_list:
print("== Working region: " + aws_region)
#print("Default region: " + region)
boto_session = get_boto_session(config["profile_name"], aws_region)
cf = boto_session.client("cloudformation", region_name=aws_region)
cloudwatch = boto_session.client("cloudwatch", region_name=aws_region)
ec2 = boto_session.client("ec2", region_name=aws_region)
iam = boto_session.client("iam", region_name=aws_region)
s3_resource = boto_session.resource("s3", region_name=default_aws_region)
sts = boto_session.client("sts", region_name=aws_region)
sns = boto_session.client("sns", region_name=aws_region)

cleaner.delete_cloudformation_stacks(cf)
cleaner.delete_cloudwatch_alarms(cloudwatch)
cleaner.delete_sns_topics(sns)
cleaner.delete_amis(sts, ec2)
cleaner.delete_snapshots(sts, ec2)
cleaner.delete_ec2_instances(ec2)
cleaner.delete_securitygroups(ec2)
cleaner.delete_key_pairs(ec2)

s3 = boto_session.client("s3", region_name=default_aws_region)
cleaner.delete_buckets(s3, s3_resource)


1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
boto3==1.4.4
pyyaml==3.12