AWS Cost Optimization: Clean Up Unused AMIs and Snapshots for Better Cloud Hygiene

Alok ShankarAlok Shankar
5 min read

Introduction

In a typical AWS environment, AMIs (Amazon Machine Images) and EBS snapshots are used to back up and replicate EC2 instances. However, over time, many of these AMIs and snapshots become obsolete, unused, or outdated. Retaining them indefinitely not only consumes storage but also increases your monthly AWS bill and clutters your environment.

In this article, I will explain how to identify and delete unutilized EBS-backed AMIs and their associated snapshots using a simple Bash script.

This step-by-step guide will help you automate resource cleanup, improve security, and reduce costs.

Challenges Faced in Manual process:

• To delete the unused AMI and associated EBS block storage , First we have to verify AMI should not in-use then deregister the AMI .When you deregister the AMI, it is simply deleted the AMI only. However, all the snapshots that were attached to the AMI remain and need to be deleted manually.

• Manual Process is time consuming and error prone.

Benefits of Deleting Unutilized EBS-Based AMIs and Corresponding Snapshots:

BenefitDescription

Cost Optimization

Reduces AWS storage costs

Storage Management

Keeps only relevant data

Compliance & Governance

Adheres to data retention policies

Security Improvement

Minimizes attack surface

Performance Enhancement

Simplifies resource management

Resource Lifecycle Management

Automates unused resource cleanup

Environmental Sustainability

Supports green cloud initiatives

Prerequisites :

Before we dive into the script, make sure the following are in place:

• AWS CLI installed

• Configured AWS credentials (aws configure)

• IAM user/role with permissions:

o ec2:DeregisterImage

o ec2:DeleteSnapshot

o ec2:DescribeImages

o ec2:DescribeInstances

o ec2:DescribeVolumes

o ec2:DescribeSnapshots

Step 1: Install AWS CLI

On Linux/macOS:

curl "https://awscli.amazonaws.com/aws-cli-v2.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

Configure CLI:

aws configure

Enter your Access Key, Secret Key, default region, and output format.

Step 2: Create IAM Role and attached the role with EC2 instance

IAM Role Name : Delete-Unused-AMI-EBS-Snapshots-role:

Step 3: Create the Bash Script

Save the following script as delete_old_amis.sh

#!/bin/bash

# Log file path
LOG_FILE="/home/ec2-user/delete-ami/ami_deletion.log"

# Function to log messages
log_message() {
    local message="$1"
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $message" | tee -a "$LOG_FILE"
}

# Function to check if AWS CLI is installed
check_dependencies() {
    if ! command -v aws &>/dev/null; then
        log_message "ERROR: AWS CLI is not installed. Please install it and try again."
        exit 1
    fi
}

# Function to delete unused AMIs and associated snapshots
delete_unused_amis_and_snapshots() {
    local cutoff_date="$1"

    log_message "Fetching AMIs created before $cutoff_date..."

    # Fetch all AMIs created before the provided date
    ami_ids=$(aws ec2 describe-images --owners "<ACCOUNT-ID>" --query "Images[?CreationDate<='$cutoff_date'].ImageId" --output text)

    if [ -z "$ami_ids" ]; then
        log_message "No AMIs found created before $cutoff_date."
        return
    fi

    for ami_id in $ami_ids; do
        # Check if the AMI is in use
        log_message "Checking if AMI $ami_id is in use..."
        usage_count=$(aws ec2 describe-instances --filters "Name=image-id,Values=$ami_id" --query "Reservations[].Instances[].InstanceId" --output text | wc -w)

        if [ "$usage_count" -eq 0 ]; then
            log_message "AMI $ami_id is not in use. Proceeding to deregister and delete associated snapshots..."

            # Fetch associated snapshots and delete them
            snapshot_ids=$(aws ec2 describe-images --image-ids "$ami_id" --query "Images[].BlockDeviceMappings[*].Ebs.SnapshotId" --output text)
            log_message "Fetched associated snapshot id are :$snapshot_ids"
            # Deregister the AMI
            log_message "Deregistering AMI $ami_id..."
            aws ec2 deregister-image --image-id "$ami_id"
            if [ $? -eq 0 ]; then
                log_message "Successfully deregistered AMI $ami_id."
            else
                log_message "ERROR: Failed to deregister AMI $ami_id."
                continue
            fi

            # Fetch associated snapshots and delete them
           # snapshot_ids=$(aws ec2 describe-images --image-ids "$ami_id" --query "Images[].BlockDeviceMappings[*].Ebs.SnapshotId" --output text)
            if [ -n "$snapshot_ids" ]; then
                for snapshot_id in $snapshot_ids; do
                    log_message "Deleting snapshot $snapshot_id..."
                    aws ec2 delete-snapshot --snapshot-id "$snapshot_id"
                    if [ $? -eq 0 ]; then
                        log_message "Successfully deleted snapshot $snapshot_id."
                    else
                        log_message "ERROR: Failed to delete snapshot $snapshot_id."
                    fi
                done
            else
                log_message "No snapshots found for AMI $ami_id."
            fi
        else
            log_message "AMI $ami_id is currently in use by $usage_count instance(s). Skipping."
        fi
    done
}

# Main script logic
main() {
    check_dependencies

    log_message "Starting process to delete unutilized AMIs and associated snapshots."

    echo "Enter the cutoff date (in YYYY-MM-DD format):"
    read -r cutoff_date

    # Validate the input date format
    if ! [[ "$cutoff_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
        log_message "ERROR: Invalid date format. Please use YYYY-MM-DD."
        exit 1
    fi

    delete_unused_amis_and_snapshots "$cutoff_date"

    log_message "Process completed."
}

# Run the script
main

Note : Replace "<ACCOUNT-ID>" with Your AWS Account ID.

Step 4: Make the Script Executable

Run the following command to grant execute permissions to the script:

chmod +x delete_old_amis.sh

Step 5: Run the Script

Execute the script in your terminal:

./delete_old_amis.sh

Step 6 : Script Flow :

Step 1: The created script will take input as DATE . Once Date provided , it will fetch AMIs created before provided date.

Enter a date in YYYY-MM-DD format when prompted

Step 2: After that it will check whether the AMI is currently in use by any running instance.

Step 3: If it use by any running instance it will skip and take another AMI ID.

Step 4: If it is not used by any running instance (Unused) Then it will deregister the AMI.

Step 5 : Once it deregistered . It will delete the EBS Snapshots.

Step 6: Check Logs

After the script runs, logs will be written to ami_deletion.log. You can view the logs using:

cat ami_deletion.log

If the script successfully runs, the log file (ami_deletion.log) will contain output similar to:

2025-01-27 10:00:00 - Starting AMI deletion process...
2025-01-27 10:00:01 - Enter the AMI ID to check and delete if unused:
2025-01-27 10:00:10 - Checking if AMI: ami-1234567890abcdef is in use...
2025-01-27 10:00:12 - AMI: ami-1234567890abcdef is not in use. Proceeding to delete...
2025-01-27 10:00:13 - Deregistering AMI: ami-1234567890abcdef
2025-01-27 10:00:14 - Successfully deregistered AMI: ami-1234567890abcdef
2025-01-27 10:00:15 - Fetching snapshots associated with AMI: ami-1234567890abcdef
2025-01-27 10:00:16 - Deleting snapshot: snap-0abcdef1234567890
2025-01-27 10:00:17 - Successfully deleted snapshot: snap-0abcdef1234567890
2025-01-27 10:00:18 - AMI deletion process completed.

Conclusion

Automating the deletion of unused AMIs and snapshots is a great DevOps practice that helps maintain a clean, secure, and cost-efficient AWS environment. With this script, you can integrate cleanup routines into your CI/CD pipelines or run it periodically to manage cloud resources proactively.

Note : Test the script in a non-production environment before deploying it in production.

9
Subscribe to my newsletter

Read articles from Alok Shankar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Alok Shankar
Alok Shankar

Dedicated and highly skilled AWS DevOps and Linux professional with over 10+ years of experience in designing, implementing, and maintaining cloud infrastructure and CICD pipelines. Proficient in optimizing processes, automating workflows, and ensuring the reliability and scalability of cloud-based systems. Demonstrated expertise in Kubernetes and containerization technologies. Proven ability to understand and execute the complete deployment lifecycle. Proven expertise in real-time troubleshooting and leading cross functional teams to success.