BespinGlobal에서 사용 중인 AMI로 EC2에 대한 AMI Backup을 매주 실행하는 Lambda Function 스크립트입니다. 



# 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:Backup',

            'Values': ['Weekly']            # 30일 마다 변경 할 때, 해당 Tag를 Monthly로 변경

        },

    ]).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           # 7일마다 Backup 하도록 일자 지정


            #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')

            Name_Tags = [result['Value'] for result in instance['Tags'] if result['Key'] == 'Name'][0]

            

            AMIid = ec.create_image(

                InstanceId=instance['InstanceId'],

                Name= create_fmt + "_" + Name_Tags + " (" + instance['InstanceId'] + ")" + " / " + "Weekly",

                Description="Created with Lambda backup function",

                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('%Y-%m-%d')

        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': 'Name',

                               'Value': create_fmt + "_" + Name_Tags

                           },

                           {


                               'Key': 'DeleteOn',

                               'Value': delete_fmt

                           },                           

                       ])




AMI Backup 된 것을 삭제 하는 것에 대한 Lambda Script

# Automated AMI and Snapshot Deletion

#

# @author Bobby Kozora

#

# This script will search for all instances having a tag named "Backup" with a value of "Backup".

# As soon as we have the instances list, we loop through each instance

# and reference the AMIs of that instance. We check that the latest daily backup

# succeeded then we store every image that's reached its DeleteOn tag's date for

# deletion. We then loop through the AMIs, deregister them and remove all the

# snapshots associated with that AMI.


import boto3

import collections

import datetime

import time

import sys


ec = boto3.client('ec2', 'ap-northeast-2')

ec2 = boto3.resource('ec2', 'ap-northeast-2')

images = ec2.images.filter(Owners=["self"])



def lambda_handler(event, context):


    reservations = ec.describe_instances(Filters=[

        {

            'Name': 'tag:Backup',

            'Values': ['Weekly']},              # 30일 마다 변경 할 때, 해당 Tag를 Monthly로 변경

            ]

        ).get(

        'Reservations', []

        )


    instances = sum([[i for i in r['Instances']] for r in reservations], [])


    print("Found %d instances that need evaluated" % len(instances))


    to_tag = collections.defaultdict(list)


    date = datetime.datetime.now()

    date_fmt = date.strftime('%Y-%m-%d')


    imagesList = []


    # Set to true once we confirm we have a backup taken today

    backupSuccess = False


    # Loop through all of our instances with a tag named "Backup"

    for instance in instances:

        imagecount = 0


        # Loop through each image of our current instance

        for image in images:


            # Our other Lambda Function names its AMIs Lambda - i-instancenumber.

            # We now know these images are auto created

            if image.name.endswith(" / " + "Weekly"):


                # print "FOUND IMAGE " + image.id + " FOR INSTANCE " + instance['InstanceId']


                # Count this image's occcurance

                imagecount = imagecount + 1               # AMI Image 에 대해서 1개 이상일 때 1개를 남기고 삭제 


                try:

                    if image.tags is not None:

                        deletion_date = [

                            t.get('Value') for t in image.tags

                            if t['Key'] == 'DeleteOn'

                        ][0]

                        delete_date = time.strptime(deletion_date, "%Y-%m-%d")

                except IndexError:

                    deletion_date = False

                    delete_date = False


                today_time = datetime.datetime.now().strftime('%Y-%m-%d')

                # today_fmt = today_time.strftime('%Y-%m-%d')

                today_date = time.strptime(today_time, '%Y-%m-%d')


                # If image's DeleteOn date is less than or equal to today,

                # add this image to our list of images to process later

                if delete_date <= today_date:

                    imagesList.append(image.id)


                # Make sure we have an AMI from today and mark backupSuccess as true

                if image.name.startswith(date_fmt):

                    # Our latest backup from our other Lambda Function succeeded

                    backupSuccess = True

                    print("Latest backup from " + date_fmt + " was a success")


        print("instance " + instance['InstanceId'] + " has " +

              str(imagecount) + " AMIs")


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


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

    print(imagesList)


    if backupSuccess == True:


        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:

            print("deregistering image %s" % image)

            amiResponse = ec.deregister_image(

                DryRun=False,

                ImageId=image,

            )


            for snapshot in snapshots:

                if snapshot['Description'].find(image) > 0:

                    snap = ec.delete_snapshot(

                        SnapshotId=snapshot['SnapshotId'])

                    print("Deleting snapshot " + snapshot['SnapshotId'])

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


    else:

        print("No current backup found. Termination suspended.")