title: Cleaning up AMIs and EBS Snapshots via AWS Lambda
body: <p>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.</p>
  <p>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.</p>
  <p>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.</p>
  <pre><code>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 &quot;DoNotDelete=true&quot; 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 &lt; (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') + &quot; of &quot;+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')
  </code></pre>