1
votes

I'm newer to Python programming, and am trying to develop an AWS Lambda. It's written in Python 3.6 that creates snapshots of available EBS volumes for deletion at a later time using boto3 calls. I want the logic for the Lambda to iterate over the volume tags. If there's a specific tag in the list of tags, then process some evaluation logic based off a date for the volume to be deleted. If no specified tag exists in collection, or tags is None proceed with creating snapshot.

My Lambda will work with the logic for the specified tag and creating a snapshot fine. I'm struggling with the correct loop syntax to handle all volume tags as a dictionary. It wants to loop through each tag individually for the volume vs. all at once as a collection.

    for vol in ec2.volumes.all():
        if vol.state == 'available':
            volid = vol.id
            tags = {}
            for tag in vol.tags:
                if tag['Key'] == 'DeleteMeAfter':
                    print("===================")
                    print(", ".join((
                        volid,
                        tag['Key'],
                        tag['Value']
                    )))
                    ### Process additional logic on tag['Value'] ###

When I add an elif/else statement to the if tag statement it tries to create multiple snapshots of the same volume.

"An error occurred (SnapshotCreationPerVolumeRateExceeded) when calling the CreateSnapshot operation: The maximum per volume CreateSnapshot request rate has been exceeded."

Any help would be greatly appreciated!

2
Are you trying to create a snapshot or delete a snapshot? - hephalump
So confusing question but the error let you know that you reach the limit. - Lamanus
Just create the snapshot in this Lambda. There's a separate Lambda that will delete the volume once I've created a successful snapshot. The problem is the way I have written the code thus far it iterates over the tags trying to create multiple snapshots of the same volume. Hence running into the error. - hawk3yez

2 Answers

0
votes
  • This should work
  • blog attached blog
# Backup all in-use volumes in all regions

import boto3

def lambda_handler(event, context):
    ec2 = boto3.client('ec2')

    # Get list of regions
    regions = ec2.describe_regions().get('Regions',[] )

    # Iterate over regions
    for region in regions:
        print "Checking region %s " % region['RegionName']
        reg=region['RegionName']

        # Connect to region
        ec2 = boto3.client('ec2', region_name=reg)

        # Get all in-use volumes in all regions  
        result = ec2.describe_volumes( Filters=[{'Name': 'status', 'Values': ['in-use']}])

        for volume in result['Volumes']:
            print "Backing up %s in %s" % (volume['VolumeId'], volume['AvailabilityZone'])

            # Create snapshot
            result = ec2.create_snapshot(VolumeId=volume['VolumeId'],Description='Created by Lambda backup function ebs-snapshots')

            # Get snapshot resource 
            ec2resource = boto3.resource('ec2', region_name=reg)
            snapshot = ec2resource.Snapshot(result['SnapshotId'])

            volumename = 'N/A'

            # Find name tag for volume if it exists
            if 'Tags' in volume:
                for tags in volume['Tags']:
                    if tags["Key"] == 'Name':
                        volumename = tags["Value"]

            # Add volume name to snapshot for easier identification
            snapshot.create_tags(Tags=[{'Key': 'Name','Value': volumename}])

0
votes

I was able to get this working properly with the following code. Thanks to the help of a friend and @amittn 's blog/post.

def examine_volumes():
    try:
        first_call = True;
        next_token = 'bc6ed4ae04d2'
        while next_token is not None:
            if first_call:
                first_call = False
                r = ec2_client.describe_volumes(Filters=[{'Name': 'status', 'Values': ['available']}])
            else:
                r = ec2_client.describe_volumes(NextToken=next_token)
            next_token = r.get('NextToken', None)
            for vol in r.get('Volumes', []):
                if is_interesting(vol):
                    logger.info(f'working on:')
                    #logger.info(json.dumps(vol, indent=2, default=date_converter))
                else:
                    logger.info(f'volume is not interesting')
                    volid = vol['VolumeId']
                    make_snapshots(volid)
    except Exception

def is_interesting(volume):
    for tag in volume.get('Tags', []):
        if (tag.get('Key', None)) == 'DeleteMeAfter':
            print(", ".join((volume['VolumeId'], tag['Key'], tag['Value'])))
            ### Process logic for tag-value ###
            make_snapshots(volid)

I call another function make_snapshots(volid) based off the 'VolumeId'. This way it will create a snapshot based off the tag value I identify with datetime logic, skips the volume if not ready, and still snapshots other volumes without the "DeleteMeAfter" tag. Thanks everyone for the assistance!