Using Boto3 To Clean Up Snapshots From Volumes That Are Unattached To Any Instances In AWS

Paul Zhao
Paul Zhao Projects
Published in
5 min readJan 24, 2024

This is a handy boto3 script tested to clean up snapshots from volumes that are unattached to Any Instances in AWS

Please follow below instructions step by step to accomplish what is intended

Step 1: Create the function

Step 2: Create IAM policy needed and attach it to IAM role

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"s3:PutObject",
"ec2:DescribeInstances",
"ec2:DeleteSnapshot",
"ec2:DescribeVolumes",
"ec2:DescribeSnapshots"
],
"Resource": [
"arn:aws:logs:<region>:<awsaccountid>:*",
"<arn of your lambda function created for this task>:*",
"*"
]
}
]
}

Find out your Lambda Function ARN as shown below

FYI: To follow the best security practice in AWS, we will grant the least privilege to accomplish the task intended.

Please replace region and awsaccountid as intended

Double check permissions granted with the name

Attaching this policy to the new role for this task as shown below

FYI: Make sure it’s a service role and related to Lambda as shown above

Please be sure that you adjust timeout since the default 3 seconds wouldn’t be sufficient to accomplish this task (But you may not want to provide a number that’s way bigger than needed since Lambda function charges you based on the time used to run the function)

Also, double check the role is assigned to this function as expected

Place below codes into Code section in Lambda Function

import boto3
import datetime
import logging
import os

# Initialize Boto3 client for EC2
ec2 = boto3.client('ec2')
s3 = boto3.client('s3')

# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
# Get the current date and time in UTC
current_datetime = datetime.datetime.utcnow()

# Format the date as YYYY-MM-DD
date_string = current_datetime.strftime('%Y-%m-%d')

# Initialize the S3 bucket and object key
bucket_name = os.environ.get('s3_bucket_name')
log_name = os.environ.get('log_name', 'default_value_if_not_found')
object_key = f'logs/{date_string}/{log_name}'

# Get a list of all snapshots in your AWS account
snapshots = ec2.describe_snapshots(OwnerIds=['self'])['Snapshots']

# Get a list of all volumes in your AWS account
volumes = ec2.describe_volumes()['Volumes']

# Create a list of attached volume IDs
attached_volume_ids = []

# Iterate through instances and collect attached volume IDs
reservations = ec2.describe_instances()['Reservations']
for reservation in reservations:
for instance in reservation.get('Instances', []):
block_device_mappings = instance.get('BlockDeviceMappings', [])
for mapping in block_device_mappings:
ebs = mapping.get('Ebs', {})
volume_id = ebs.get('VolumeId')
if volume_id:
attached_volume_ids.append(volume_id)

# Create a set of unique attached volume IDs
attached_volume_ids_set = set(attached_volume_ids)

# Initialize a list to store log messages
log_messages = []

# Iterate through snapshots
for snapshot in snapshots:
# Check if the snapshot's volume ID is not in the attached volume IDs set
if snapshot['VolumeId'] not in attached_volume_ids_set:
# Delete the snapshot
log_message = f"Deleting snapshot {snapshot['SnapshotId']} from volume {snapshot['VolumeId']}"
log_messages.append(log_message)
ec2.delete_snapshot(SnapshotId=snapshot['SnapshotId'])

# Combine log messages into a single string
log_data = '\n'.join(log_messages)

# Upload log data to the S3 bucket with the date-based object key
s3.put_object(
Bucket=bucket_name,
Key=object_key,
Body=log_data
)

logger.info("Snapshot cleanup completed.")

The above script can be added to Lambda Function created as shown below

There are 2 variables needed to run this function, so please edit Environment Variables as shown below

FYI: You may need to create s3_bucket_name and logs object in S3 bucket and update variables as needed, also the log_name is subject to change as needed (s3 bucket name has to be globally unique. You may need to add strings or numbers to make it unique)

Final Step: Create the trigger (EventBridge (CloudWatch Events))

In Lambda function, you’re able to create EventBridge (CloudWatch Events) as shown below

This schedule expression is like regular cron configuration, you may adjust based on your business requirements

cron(0 0 ? * MON-SUN *)

Monday through Sunday 12:00 UTC

Make sure you double check the schedules

Here’s a test in Lambda function

FYI: The scheduled task will not be seen, but the logs will be located in S3 bucket as shown below

Here are 2 sample files as reference

When there is no snapshot that was created from volumes unattached to any instances

Blank file

When there are snapshots that were created from volumes unattached to any instances

That’s it! Hopefully this document may save some time for you when deleting snapshots unused.

--

--

Paul Zhao
Paul Zhao Projects

Amazon Web Service Certified Solutions Architect Professional & Devops Engineer