AWS Cost Optimization in Action: Auto-Delete Old EBS Snapshots


If you’ve ever launched an EC2 instance on AWS, chances are you’ve created EBS snapshots—backups of your volumes that let you recover data later. These snapshots are great for disaster recovery, but they have a sneaky side effect: they stick around even after the volume or EC2 instance is gone, silently adding to your AWS bill.
What if we could automatically detect and delete unused (stale) EBS snapshots?
That’s exactly what I did using AWS Lambda, Python (Boto3), and a bit of automation magic. In this post, I’ll show you how to build it yourself.
Step 1: Understand What an EBS Snapshot Is
When you create an EC2 instance, AWS automatically attaches an EBS volume to it (usually for your OS). From the EC2 dashboard, you can:
View that volume under "Volumes"
Create a snapshot by selecting it and choosing "Create Snapshot"
A snapshot is just a copy of that volume at a moment in time. You can later use it to create new volumes or restore deleted ones.
But here’s the catch: if you delete the EC2 instance or volume, AWS does not delete the snapshot. You continue paying for it even if you don’t need it anymore.
Step 2: Automate Snapshot Cleanup with AWS Lambda
Let’s automate the process of deleting snapshots that are no longer associated with any active EC2 instance or volume.
Here’s the plan:
Write a Lambda function in Python using Boto3
Give it the necessary permissions
Test it on your account
Schedule it using CloudWatch
Writing the Lambda Function
Go to the AWS Lambda console and create a new function.
Choose:
Author from scratch
Python 3.11 as the runtime
Paste this code in the editor:
import boto3
def lambda_handler(event, context):
ec2 = boto3.client('ec2')
# Get all EBS snapshots
response = ec2.describe_snapshots(OwnerIds=['self'])
# Get all active EC2 instance IDs
instances_response = ec2.describe_instances(Filters=[{'Name': 'instance-state-name', 'Values': ['running']}])
active_instance_ids = set()
for reservation in instances_response['Reservations']:
for instance in reservation['Instances']:
active_instance_ids.add(instance['InstanceId'])
# Iterate through each snapshot and delete if it's not attached to any volume or the volume is not attached to a running instance
for snapshot in response['Snapshots']:
snapshot_id = snapshot['SnapshotId']
volume_id = snapshot.get('VolumeId')
if not volume_id:
# Delete the snapshot if it's not attached to any volume
ec2.delete_snapshot(SnapshotId=snapshot_id)
print(f"Deleted EBS snapshot {snapshot_id} as it was not attached to any volume.")
else:
# Check if the volume still exists
try:
volume_response = ec2.describe_volumes(VolumeIds=[volume_id])
if not volume_response['Volumes'][0]['Attachments']:
ec2.delete_snapshot(SnapshotId=snapshot_id)
print(f"Deleted EBS snapshot {snapshot_id} as it was taken from a volume not attached to any running instance.")
except ec2.exceptions.ClientError as e:
if e.response['Error']['Code'] == 'InvalidVolume.NotFound':
# The volume associated with the snapshot is not found (it might have been deleted)
ec2.delete_snapshot(SnapshotId=snapshot_id)
print(f"Deleted EBS snapshot {snapshot_id} as its associated volume was not found.")
Click Deploy when done.
Add Permissions for Lambda
If you test this Lambda now, it’ll likely fail. That’s because it doesn’t yet have permission to:
Describe or delete snapshots
Access volumes or instances
Here’s how to fix that:
In the Lambda page, under Configuration → Permissions, click the Execution Role
Go to IAM → Create a new policy
Choose EC2 as the service and give these actions:
DescribeInstances
DescribeVolumes
DescribeSnapshots
DeleteSnapshot
Set the resource to
*
(all resources) and name the policy something likecost_optimization_ebs
Attach the policy to your Lambda role
Now your Lambda has everything it needs.
Increase Lambda Timeout (Just a Little)
Go to your Lambda’s General configuration and set the timeout to 10 seconds.
Lambda pricing depends on runtime duration, so keep it minimal—just enough to finish the task.
Step 3: Test It
Now let’s see it in action.
Delete your EC2 instance (and confirm the volume is gone)
The snapshot you created earlier still exists
Run your Lambda function again
The snapshot should be deleted!
Why? Because its associated volume no longer exists, and the function is designed to clean up exactly this kind of leftover.
How the Code Works (In Simple Terms)
It checks all your EC2 instances and stores the ones currently running
It looks through all your snapshots
For each snapshot:
If the related volume doesn’t exist → delete snapshot
If the volume exists but is not attached → delete snapshot
If the volume is attached to a stopped or terminated instance → delete snapshot
Simple logic, powerful results.
Schedule with CloudWatch (Optional)
Want to run this every day automatically?
Go to Amazon EventBridge (CloudWatch Events)
Create a new rule
Use a fixed schedule like
rate(1 day)
Set your Lambda function as the target
Wrap-Up
By cleaning up unused EBS snapshots, you can save money, reduce clutter, and automate part of your cloud management.
All it takes is:
One Lambda function
A bit of IAM setup
Scheduling
It’s a great example of how a small automation can have a big financial impact in the long run.
Subscribe to my newsletter
Read articles from Titus James directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
