Skip to content

Instantly share code, notes, and snippets.

@bkozora
Last active May 29, 2024 09:07
Show Gist options
  • Save bkozora/724e01903a9ad481d21e to your computer and use it in GitHub Desktop.
Save bkozora/724e01903a9ad481d21e to your computer and use it in GitHub Desktop.
AWS Lambda AMI Backups
# Automated AMI Backups
#
# @author Bobby Kozora
#
# This script will search for all instances having a tag with the name "backup"
# and value "Backup" on it. As soon as we have the instances list, we loop
# through each instance
# and create an AMI of it. Also, it will look for a "Retention" tag key which
# will be used as a retention policy number in days. If there is no tag with
# that name, it will use a 7 days default value for each AMI.
#
# After creating the AMI it creates a "DeleteOn" tag on the AMI indicating when
# it will be deleted using the Retention value and another Lambda function
import boto3
import collections
import datetime
import sys
import pprint
ec = boto3.client('ec2')
#image = ec.Image('id')
def lambda_handler(event, context):
reservations = ec.describe_instances(Filters=[
{
'Name': 'tag-key',
'Values': ['backup', 'Backup']
},
]).get('Reservations', [])
instances = sum([[i for i in r['Instances']] for r in reservations], [])
print("Found %d instances that need backing up" % len(instances))
to_tag = collections.defaultdict(list)
for instance in instances:
try:
retention_days = [
int(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Retention'
][0]
except IndexError:
retention_days = 7
#for dev in instance['BlockDeviceMappings']:
# if dev.get('Ebs', None) is None:
# continue
# vol_id = dev['Ebs']['VolumeId']
# print "Found EBS volume %s on instance %s" % (
# vol_id, instance['InstanceId'])
#snap = ec.create_snapshot(
# VolumeId=vol_id,
#)
#create_image(instance_id, name, description=None, no_reboot=False, block_device_mapping=None, dry_run=False)
# DryRun, InstanceId, Name, Description, NoReboot, BlockDeviceMappings
create_time = datetime.datetime.now()
create_fmt = create_time.strftime('%Y-%m-%d')
AMIid = ec.create_image(
InstanceId=instance['InstanceId'],
Name="Lambda - " + instance['InstanceId'] + " from " +
create_fmt,
Description="Lambda created AMI of instance " +
instance['InstanceId'] + " from " + create_fmt,
NoReboot=True,
DryRun=False)
pprint.pprint(instance)
#sys.exit()
#break
#to_tag[retention_days].append(AMIid)
to_tag[retention_days].append(AMIid['ImageId'])
print("Retaining AMI %s of instance %s for %d days" % (
AMIid['ImageId'],
instance['InstanceId'],
retention_days,
))
print(to_tag.keys())
for retention_days in to_tag.keys():
delete_date = datetime.date.today() + datetime.timedelta(
days=retention_days)
delete_fmt = delete_date.strftime('%m-%d-%Y')
print("Will delete %d AMIs on %s" %
(len(to_tag[retention_days]), delete_fmt))
#break
ec.create_tags(Resources=to_tag[retention_days],
Tags=[
{
'Key': 'DeleteOn',
'Value': delete_fmt
},
])
@kenlee-memberson
Copy link

hi bkozora,
Firstly let me thank you for your contribution. I have been using this script for auto AMI backup since a year ago and it works great. However since python 2 is ending support, I have to change to use the latest updated script (python 3.8). The Lambda runs and there is no specific error, I'm only getting the below message from cloudwatch but the AMI backups will not appear. Can you please help?


06:45:10
START RequestId: e87b6746-8746-4bac-ab31-6afda9ee6515 Version: $LATEST

06:45:10
Found 14 instances that need backing up

06:45:10
dict_keys([])

@sivahome
Copy link

Hi Guys,
I took reference of this script and did ami deletion based on the tag .
But the requirement is Based on tag as well , that Tagged instance backup should be retained 30 days if more than 30 days it should delete.

Could you please help me here with logic

Code below
++++++++++
import os
import re
from datetime import datetime, timedelta
import boto3
import collections
import datetime
import logging
import time
import sys

ec = boto3.client('ec2',)
ec2 = boto3.resource('ec2',)
images = ec2.images.filter(Owners=["self"])

def lambda_handler(event, context):

to_tag = collections.defaultdict(list)
date = datetime.datetime.now()
date_fmt = date.strftime('%Y-%m-%d')

imagesList = []

for image in images:

       try:
            if image.tags is not None:
                deletion_date = [
                    t.get('Value') for t in image.tags
                    if t['Key'] == 'backup'
                ][0]
                imagesList.append(image.id)
                
                
       except IndexError:
           deletion_date = False
           delete_date = False

       today_time = datetime.datetime.now().strftime('%m-%d-%Y')
        # today_fmt = today_time.strftime('%m-%d-%Y')
       today_date = time.strptime(today_time, '%m-%d-%Y')

                     
       
print ("=============")

print ("About to process the following AMIs:")
print ('imagesList')

myAccount = boto3.client('sts').get_caller_identity()['Account']

snapshots = ec.describe_snapshots(MaxResults=1000, OwnerIds=[myAccount])['Snapshots']

# loop through list of image IDs
for image in imagesList:
    try:
        print ("deregistering image %s" % image)
        amiResponse = ec.deregister_image(
        DryRun=False,
        ImageId=image,
       )
    except ClientError:
        print ("ClientError, likely AMI not found")
        continue

    for snapshot in snapshots:
        if snapshot['Description'].find(image) > 0:
            snap = ec.delete_snapshot(SnapshotId=snapshot['SnapshotId'])
            print ("Deleting snapshot " + snapshot['SnapshotId'])

print ("-------------")
+++++++++++++++

@ssreddygadiko
Copy link

How to tag snapshot by using ami id created by this script

@shivangitripathi
Copy link

Thank you for the nice script. :)

@phani44
Copy link

phani44 commented Jun 8, 2021

Hi thanks for this nice script. I have made a small enhancement on the script, so it also supports instances with multiple EBS volumes attached.

On line 42 i have added a new empty list, that will hold all instances that where already done :
inDone = []

And the line 67 should be surrounded by a check. Just replace the line 67 with:

            if str(instance['InstanceId']) not in inDone:

              AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " from " + create_fmt, Description="Lambda created AMI of instance " + instance['InstanceId'] + " from " + create_fmt, NoReboot=True, DryRun=False)

              inDone.insert(0,str(instance['InstanceId']))

              print "Created AMI %s of instance %s " % (AMIid['ImageId'], instance['InstanceId'])

            else:

              print "We already got an AMI of instance %s " % (instance['InstanceId'])

Thanks again!
Regards, Frank

Hello Frank,

Can you paste me the full script along with modifications you made

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment