Skip to content

Instantly share code, notes, and snippets.

@r2el
Forked from bkozora/lambdaAMIBackups.py
Last active April 18, 2019 11:11
Show Gist options
  • Save r2el/66cad5ec5a0a6d33b4456d72e3773bd1 to your computer and use it in GitHub Desktop.
Save r2el/66cad5ec5a0a6d33b4456d72e3773bd1 to your computer and use it in GitHub Desktop.
AWS Lambda AMI Backups
# Automated AMI Backups
#
# @author Robert Kozora <bobby@kozora.me>
#
# This script will search for all instances having a tag with "Backup" or "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
import time
def lambda_handler(event, context):
regions = boto3.session.Session().get_available_regions('ec2') #ec2.regions()
for x in regions:
print "Region_name %s" % x
for x in regions:
print "Region_name %s" % x
ec = boto3.client('ec2', region_name=x)
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)
inDone = []
for instance in instances:
# BlockDeviceMappings=[
#{
#'DeviceName': '/dev/sdf',
#'NoDevice': '',
#},
#],
try:
no_devices_tag = [
str(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'DoNotImage'][0]
except IndexError:
no_devices_tag = ''
try:
no_device_array = []
if len(no_devices_tag) > 0:
no_devices = no_devices_tag.split(',')
for no_device in no_devices:
no_device_array.append({'DeviceName':no_device.strip(),'NoDevice':''})
print "DoNotImage values to process %s " % (len(no_device_array))
except:
no_device_array = []
try:
ami_name = [
str(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Name'][0]
except IndexError:
ami_name = ''
try:
retention_days = [
int(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Retention'][0]
except IndexError:
retention_days = 14
finally:
#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)
try:
if str(instance['InstanceId']) not in inDone:
if len(no_device_array) > 0:
AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " - " + ami_name + " from " + create_fmt, Description="Lambda created AMI of instance " + instance['InstanceId'] + " from " + create_fmt, NoReboot=True, DryRun=False, BlockDeviceMappings=no_device_array)
else:
AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " - " + ami_name + " 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'])
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,
)
except Exception as e:
print "Error: "+ str(e)
pass
#except:
# print "Unexpected error:", sys.exc_info()[0]
# pass
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},
]
)
# Automated AMI and Snapshot Deletion
#
# @author Robert Kozora <bobby@kozora.me>
#
# This script will search for all instances having a tag with "Backup" or "backup"
# on it. 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.
#ec = boto3.client('ec2', 'us-east-1')
# Moved out of main backup region loop, we don't want errors deleting images preventing backups
for x in regions:
ec2 = boto3.resource('ec2', x)
images = ec2.images.filter(Owners=["self"])
ec = boto3.client('ec2', region_name=x)
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 evaluated" % len(instances)
to_tag = collections.defaultdict(list)
date = datetime.datetime.now() - datetime.timedelta(days = 1)
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:
try:
ami_name = [
str(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Name'][0]
except IndexError:
ami_name = ''
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.startswith('Lambda - ' + instance['InstanceId']):
print "FOUND IMAGE " + image.id + " FOR INSTANCE " + instance['InstanceId']
# Count this image's occcurance
imagecount = imagecount + 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, "%m-%d-%Y")
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')
# 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.endswith(date_fmt):
# Our latest backup from our other Lambda Function succeeded
backupSuccess = True
print "Latest backup from " + date_fmt + " was a success"
else:
print "Latest backup from " + date_fmt + " was not a success in this AMI"
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
try:
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 "-------------"
except Exception as e:
print "Error: "+ str(e)
pass
else:
print "No current backup found. Termination suspended."
# Automated AMI Backups
#
# @author Robert Kozora <bobby@kozora.me>
#
# This script will search for all instances having a tag with "Backup" or "backup"
# on it in all regions. 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 14 days default value for each AMI.
#
# Use of DoNotImage tag will not image specific volumes, ie "/dev/sdf,/dev/sdg" in a CSV format.
#
# After creating the AMI it creates a "DeleteOn" tag on the AMI indicating when
# it will be deleted using the Retention value with the same Lambda function
#
# Important set high timeout value on lambda script due to enumeration of multiple/regions
# and potentially many instances.
import boto3
import collections
import datetime
import sys
import pprint
import time
def lambda_handler(event, context):
regions = boto3.session.Session().get_available_regions('ec2') #ec2.regions()
for x in regions:
print "Region_name %s" % x
for x in regions:
print "Region_name %s" % x
ec = boto3.client('ec2', region_name=x)
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)
inDone = []
for instance in instances:
# BlockDeviceMappings=[
#{
#'DeviceName': '/dev/sdf',
#'NoDevice': '',
#},
#],
try:
no_devices_tag = [
str(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'DoNotImage'][0]
except IndexError:
no_devices_tag = ''
try:
no_device_array = []
if len(no_devices_tag) > 0:
no_devices = no_devices_tag.split(',')
for no_device in no_devices:
no_device_array.append({'DeviceName':no_device.strip(),'NoDevice':''})
print "DoNotImage values to process %s " % (len(no_device_array))
except:
no_device_array = []
try:
ami_name = [
str(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Name'][0]
except IndexError:
ami_name = ''
try:
retention_days = [
int(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Retention'][0]
except IndexError:
retention_days = 14
finally:
#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)
try:
if str(instance['InstanceId']) not in inDone:
if len(no_device_array) > 0:
AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " - " + ami_name + " from " + create_fmt, Description="Lambda created AMI of instance " + instance['InstanceId'] + " from " + create_fmt, NoReboot=True, DryRun=False, BlockDeviceMappings=no_device_array)
else:
AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId'] + " - " + ami_name + " 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'])
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,
)
except Exception as e:
print "Error: "+ str(e)
pass
#except:
# print "Unexpected error:", sys.exc_info()[0]
# pass
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},
]
)
# Automated AMI and Snapshot Deletion
#
# @author Robert Kozora <bobby@kozora.me>
#
# This script will search for all instances having a tag with "Backup" or "backup"
# on it. 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.
#ec = boto3.client('ec2', 'us-east-1')
# Moved out of main backup region loop, we don't want errors deleting images preventing backups
for x in regions:
ec2 = boto3.resource('ec2', x)
images = ec2.images.filter(Owners=["self"])
ec = boto3.client('ec2', region_name=x)
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 evaluated" % len(instances)
to_tag = collections.defaultdict(list)
date = datetime.datetime.now() - datetime.timedelta(days = 1)
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:
try:
ami_name = [
str(t.get('Value')) for t in instance['Tags']
if t['Key'] == 'Name'][0]
except IndexError:
ami_name = ''
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.startswith('Lambda - ' + instance['InstanceId']):
print "FOUND IMAGE " + image.id + " FOR INSTANCE " + instance['InstanceId']
# Count this image's occcurance
imagecount = imagecount + 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, "%m-%d-%Y")
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')
# 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.endswith(date_fmt):
# Our latest backup from our other Lambda Function succeeded
backupSuccess = True
print "Latest backup from " + date_fmt + " was a success"
else:
print "Latest backup from " + date_fmt + " was not a success in this AMI"
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
try:
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 "-------------"
except Exception as e:
print "Error: "+ str(e)
pass
else:
print "No current backup found. Termination suspended."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment