Reputation: 1171
This is my code for creating a snapshot via AWS Lambda.
import boto3
import collections
import datetime
ec = boto3.client('ec2')
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 = 14
for volume in ec.volumes.filter(Filters=[
{'Name': 'attachment.instance-id', 'Values': [instance.id]}
]):
description = 'scheduled-%s.%s-%s' % (instance_name, volume.volume_id, datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
print 'description: %s' % (description)
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,
)
to_tag[retention_days].append(snap['SnapshotId'])
print "Retaining snapshot %s of volume %s from instance %s for %d days" % (
snap['SnapshotId'],
vol_id,
instance['InstanceId'],
retention_days,
)
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 snapshots on %s" % (len(to_tag[retention_days]), delete_fmt)
ec.create_tags(
Resources=to_tag[retention_days],
Tags=[
{'Key': 'DeleteOn', 'Value': delete_fmt},
]
)
I got the following response :
'EC2' object has no attribute 'volumes': AttributeError
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 34, in lambda_handler
for volume in ec.volumes.filter(Filters=[
AttributeError: 'EC2' object has no attribute 'volumes'
Whgen I used ec = boto3.resource('ec2') instead of ec = boto3.client('ec2'), I get the description but some others such as describe_instances don't work
So, please tell me what the replacement for volumes is in boto3.client('ec2')
Upvotes: 0
Views: 1073
Reputation: 31
Just copy the function and find description in the code and replace it with your custom description in single quotes.
Hope it helps!
#Tag to folllow
#Retention number of days here
#backup
#backup-monthly
import boto3
import collections
import datetime
ec = boto3.client('ec2')
def lambda_handler(event, context):
reservations = ec.describe_instances(
Filters=[
{'Name': 'tag-key', 'Values': ['backup', 'Backup']},
# Uncomment this line if need to take snaphsot of running instances only
# {'Name': 'instance-state-name', 'Values': ['running']},
]
).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'])
instance_id = instance['InstanceId']
snapshot_name = 'N/A'
if 'Tags' in instance:
for tags in instance['Tags']:
if tags["Key"] == 'Name':
snapshot_name = tags["Value"]
print "Tagging snapshot with Name: {} and Instance ID {}".format(snapshot_name, instance_id)
snap = ec.create_snapshot(
Description = 'Description goes here',
VolumeId = vol_id,
TagSpecifications = [{
'ResourceType': 'snapshot',
'Tags': [{
'Key': 'Name',
'Value': snapshot_name
}, ]
}, ]
# DryRun = False
)
to_tag[retention_days].append(snap['SnapshotId'])
print "Retaining snapshot %s of volume %s from instance %s for %d days" % (
snap['SnapshotId'],
vol_id,
instance['InstanceId'],
retention_days,
)
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 snapshots on %s" % (len(to_tag[retention_days]), delete_fmt)
ec.create_tags(
Resources=to_tag[retention_days],
Tags=[
{'Key': 'DeleteOn', 'Value': delete_fmt},
]
)
Upvotes: 0
Reputation: 212
I found the solution online https://serverlesscode.com/post/lambda-schedule-ebs-snapshot-backups/ did not fit my needs so I've written the following backup script. This is still my first draft but I find it's much easier to read and also a better implementation for me because rather than targeting instances, I target volumes. Tagging is also improved.
import boto3
import collections
import datetime
import os
ec = boto3.client('ec2')
ec2 = boto3.resource('ec2')
TAG = os.environ['TAG']
def lambda_handler(event, context):
volumes = ec.describe_volumes(
Filters=[
{'Name': 'tag-key', 'Values': [ "Backup", TAG ]},
{'Name': 'status', 'Values': [ "in-use" ] },
]
).get(
'Volumes', []
)
print "Found {0} volumes that need backing up".format(len(volumes))
for v in volumes:
vol_id = v['VolumeId']
print(vol_id)
vol_name = None
snap = None
vol_tags = v.get('Tags', None)
try:
retention_days = [
int(t.get('Value')) for t in vol_tags
if t['Key'] == 'Retention'][0]
except IndexError:
retention_days = 7
delete_date = datetime.date.today() + datetime.timedelta(days=retention_days)
delete_fmt = delete_date.strftime('%Y-%m-%d')
print "Will delete volume: {0} on {1}".format(vol_id, delete_fmt)
if vol_tags is not None:
for tag in vol_tags:
try:
if tag['Key'] == 'Name':
vol_name = tag.get('Value')
print(vol_name)
snap = ec.create_snapshot(
VolumeId=vol_id,
)
ec2.Snapshot(id=snap['SnapshotId']).create_tags(
Tags=[
{'Key': 'DeleteOn', 'Value': delete_fmt},
{'Key': 'Name', 'Value': vol_name},
]
)
break
except:
print "No Tag key 'Name' found."
print "Retaining snapshot %s of volume %s aka %s for %d days" % (
snap['SnapshotId'],
vol_id,
vol_name,
retention_days,
)
Upvotes: 0
Reputation: 327
I've found these guys solutions to be the best to date:
https://blog.powerupcloud.com/2016/02/15/automate-ebs-snapshots-using-lambda-function/
There are examples of descriptions in their code base being added to snaps.
Upvotes: 0
Reputation: 376
I had the same problem, but in my case I needed to copy all the tags from the EC2 instance to the snapshots, take a look to my code it might help you or at leas guide you:
Doing it this way, you just need to make sure that the instance has the "Name" tag so it can be copied to the snapshot, I also needed this because of the CostCenter tag
Upvotes: 1
Reputation: 52375
boto3.resource
is an abstraction for low level boto3.client
You are mixing both. If you are using client.describe_instances
, then use client.describe_volumes
.
If you want to use resource.volumes
, then use resource.instances
. I prefer resource.instances
because of its powerful filter and abstraction. If you use resources and want to access the underlying client for some reason, you can get the low level client using meta
.
ec2 = boto3.resource('ec2')
client = ec2.meta.client
Avoid dealing with reservations etc., Use resource.instances
. There are plenty of examples if you google for it. Few lines of code and very readable.
Upvotes: 1