aashitvyas
aashitvyas

Reputation: 1038

Cleaning up AMIs and EBS Snapshots via AWS Lambda

I have created following lambda function on my local machine so I can deploy it and run it through cloudwatch event cron expression on daily basis to cleanup the desired AMI and its SnapShots. It also takes care of abandon EBS SnapShots as well.

The criteria of deleting the AMI is first find the AMI that doesn't have DoNotDelete:true tag, and if its more than 7 days old, mark it for deletion. The function exempt the AMI which is currently being used by AWS Launch Configuration.

I am sure there are few ways to optimize this lambda function and code and I would like to know how can I improve/optimize this further.

import boto3
from datetime import timedelta, datetime, timezone
import logging
import botocore

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



def ami_cleanup(event,context):
    '''Clean AMIs and its associated SnapShots which are older than 7 Days and without "DoNotDelete=true" tag in a AWS Region
    Exempt AMI which is currently being used in AWS Launch Config'''
    ec2 = boto3.client('ec2')
    autoscaling = boto3.client('autoscaling')
    ami_response = ec2.describe_images(Owners=['self'])
    snapshot_response = ec2.describe_snapshots(OwnerIds=['self'])
    lc_response = autoscaling.describe_launch_configurations()
    amis = {}
    amidnd = []
    for i in ami_response['Images']:
        for tag in i.get('Tags',''):
             if 'DoNotDelete' in tag.values():
                 amidnd.append(i.get('ImageId'))
                 break
    for ami in lc_response['LaunchConfigurations']:
        if ami['ImageId'] not in amidnd:
            amidnd.append(ami['ImageId'])
    for i in ami_response['Images']:
        if i.get('Tags') == None or i['ImageId'] not in amidnd:
            amis[i.get('ImageId')] = i.get('CreationDate')
    if not amis:
        logger.info('No AMIs and SnapShots found to be deregister')
    else:
        for ami,cdate in amis.items():
            if cdate < (datetime.now(timezone.utc)-timedelta(days=7)).isoformat():
                logger.info('De-registering...'+ami)
                ec2.deregister_image(ImageId=ami)
                for snapshot in snapshot_response['Snapshots']:
                    if  ami in snapshot.get('Description',''):
                        logger.info('Deleting '+snapshot.get('SnapshotId') + " of "+ami)
                        ec2.delete_snapshot(SnapshotId=snapshot.get('SnapshotId'))
            else:
                logger.info('No AMIs and SnapShots found to be older than 7 days')
                break
    abandon_snap_clean(ami_response,snapshot_response)


def abandon_snap_clean(ami_response,snapshot_response):
    '''Clean abandon ebs snapshots of which no AMI has been found'''
    snapdndids = []
    for i in ami_response['Images']:
        for snap in i['BlockDeviceMappings']:
            if 'Ebs' in snap.keys():
                snapdndids.append(snap['Ebs']['SnapshotId'])
    for snapid in snapshot_response['Snapshots']:
        if snapid['SnapshotId'] not in snapdndids:
            try:
                logger.info('Deleting abandon snapshots '+snapid['SnapshotId'])
                ec2.delete_snapshot(SnapshotId=snapid['SnapshotId'])
            except botocore.exceptions.ClientError as error:
                if error.response['Error']['Code'] == 'InvalidSnapshot.InUse':
                    logger.info('SnapShotId '+snapid['SnapShotId']+' is already being used by an AMI')
                else:
                    raise error
        else:
            logger.info('No abandon EBS SnapShots found to clean up')
            break
    else:
        logger.info('No SnapShots found')

Upvotes: 0

Views: 2133

Answers (1)

jellers
jellers

Reputation: 21

It does seem that you have a logic issue here, if you come across an image that isn't more than 7 days old, the loop breaks while there could still be other images that are older than 7 days. Switch the break to continue

           if cdate < (datetime.now(timezone.utc)-timedelta(days=7)).isoformat():
               logger.info('De-registering...'+ami)
               ec2.deregister_image(ImageId=ami)
               for snapshot in snapshot_response['Snapshots']:
                   if  ami in snapshot.get('Description',''):
                       logger.info('Deleting '+snapshot.get('SnapshotId') + " of "+ami)
                       ec2.delete_snapshot(SnapshotId=snapshot.get('SnapshotId'))
           else:
               logger.info('No AMIs and SnapShots found to be older than 7 days')
               continue

Upvotes: 2

Related Questions