Old AMIs and the EBS snapshots behind them accumulate storage costs indefinitely if never cleaned. This script enforces a retention window safely. It computes a cutoff epoch from MAX_AGE_DAYS, then lists only images you own with describe-images and --owners self, so shared and public AMIs are untouched. Each image's creation date is converted to epoch seconds and compared against the cutoff; anything newer is skipped immediately. For older images it first records the snapshot IDs from the block device mappings, because once an AMI is deregistered you lose the easy link to its snapshots. Ordering matters: snapshots cannot be deleted while still referenced by a registered image, so we deregister the AMI before deleting each snapshot. The whole flow is gated behind DRY_RUN, which defaults to true and merely prints intended actions; you must set DRY_RUN=false to delete. This makes a genuinely destructive operation reviewable before you commit to it.
Clean up old AMIs and snapshots
Deregister your old AMIs past a retention window and delete their backing EBS snapshots.
11 views
Share
Script
Download .shbash
#!/usr/bin/env bash
# Deregister AMIs older than N days that you own, then delete their snapshots.
# Dry run by default to prevent accidental deletion.
set -euo pipefail
REGION="${AWS_REGION:-us-east-1}"
MAX_AGE_DAYS="${MAX_AGE_DAYS:-30}"
DRY_RUN="${DRY_RUN:-true}" # set DRY_RUN=false to actually delete
command -v aws >/dev/null 2>&1 || { echo "aws CLI not found"; exit 1; }
CUTOFF="$(date -u -d "${MAX_AGE_DAYS} days ago" +%s)"
# List self-owned AMIs with their creation date and ID.
aws ec2 describe-images --region "${REGION}" --owners self \
--query "Images[].[ImageId, CreationDate]" --output text |
while IFS=$'\t' read -r ami created; do
created_epoch="$(date -u -d "${created}" +%s)"
[ "${created_epoch}" -ge "${CUTOFF}" ] && continue # too new, keep it
echo "Old AMI: ${ami} (created ${created})"
# Capture the snapshot IDs backing this AMI before we deregister it.
snaps="$(aws ec2 describe-images --region "${REGION}" --image-ids "${ami}" \
--query "Images[].BlockDeviceMappings[].Ebs.SnapshotId" --output text)"
if [ "${DRY_RUN}" = "true" ]; then
echo " [DRY RUN] would deregister ${ami} and delete snapshots: ${snaps:-none}"
continue
fi
# Deregister the AMI first; snapshots cannot be deleted while in use.
aws ec2 deregister-image --region "${REGION}" --image-id "${ami}"
for snap in ${snaps}; do
aws ec2 delete-snapshot --region "${REGION}" --snapshot-id "${snap}"
echo " deleted snapshot ${snap}"
done
echo " deregistered ${ami}"
doneHow to run
Review the script first, then download or copy it and run it in your environment.
You might also like
bashlinuxBeginner
List GCP compute instances
Show every Compute Engine VM in a project with zone, machine type, status, and IPs.
bashlinuxIntermediate
Backup All MySQL Databases with mysqldump
Dumps each MySQL database to its own compressed SQL file.
bashlinuxIntermediate
Bulk Rename Files by Pattern
Renames many files at once using a sed substitution, with dry-run preview.