AWS - Clean up left over EBS volumes

Sometimes you mess up and forget to have EC2 instances delete their volumes on termination. When this happens you may need to clean them up.  If you have a list of the AMI IDs that were used in each region, here is a script that lets you find volumes using the AMI snapshot ids that are no longer mounted and need to be cleaned up.

ebs_cleanup.py

import boto3
import botocore.exceptions as boto_exc

AMI_MAP = {
    "us-east-1":"ami-0123456789abcef12",
    "us-east-2":"ami-0123456789abcef13",
    "us-west-1":"ami-0123456789abcef14",
    "us-west-2":"ami-0123456789abcef15",
}


def get_snapshot_from_id(client, ami_id):
    resp = client.describe_images(
        ImageIds=[ami_id]
    )
    return resp["Images"][0]["BlockDeviceMappings"][0]["Ebs"]["SnapshotId"]

session = boto3.Session()

ec2 = session.client("ec2")

count = 0
for region in ec2.describe_regions()["Regions"]:
    region_name = region["RegionName"]
    if region_name not in AMI_MAP:
        print(f"Skipping unsupported region {region_name}")
        continue
    print(f"Cleaning up {region_name}")
    ec2client = session.client("ec2", region_name=region_name)
    snapshot_id = get_snapshot_from_id(ec2client, AMI_MAP[region_name])

    paginator = ec2client.get_paginator("describe_volumes")
    pages = paginator.paginate(
        Filters=[
            {
                'Name': 'status',
                'Values': ['available']
            },
            {
                'Name': 'snapshot-id',
                'Values': [snapshot_id],
            },
        ]
    )
    volumes_to_delete = []
    for page in pages:
        volumes_to_delete.extend([
            volume["VolumeId"] for volume in page["Volumes"] if not volume["Attachments"]
        ])
    if len(volumes_to_delete) == 0:
        print(f"No volumes to remove for {region_name}")
        continue
    count = count + len(volumes_to_delete)
    for volume in volumes_to_delete:
        print(f"Deleting {volume}")
        try:
            ec2client.delete_volume(VolumeId=volume, DryRun=False)
        except boto_exc.ClientError as e:
            if 'DryRunOperation' not in str(e):
                raise

if count > 0:
    print(f"Deleted {count} unattached volumes")

Popular posts from this blog

Ansible - Task executed multiple times due to a Broken Pipe

Vyatta -- SIP Connection Tracking for VOIP

mysql -- Error reading master configuration