Using Boto3 To Clean Up Snapshots From Volumes That Are Unattached To Any Instances In AWS
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.