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


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:
Benefit | Description |
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.
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.