Simple AWS Lambda Function to Snapshot All Attached Instance Volumes

I realize that this has been done before but, this is my first venture into creating a AWS Lambda function and I'm not a Python guy but with a little reading:

https://github.com/boto/boto3
http://boto3.readthedocs.io/en/latest
http://docs.aws.amazon.com/lambda/latest/dg/python-programming-model.html

I came up with a useful function that can automate snapshots of all instance attached volumes (I'm not sure exactly what the execution scope is yet Single Region or all Regions). I chose to store my Python script in S3. I'm not going to go through all of the AWS Console  steps since it changes often, instead I'm just going to focus on the things you need to setup.
  1. Create an S3 Bucket.
  2. Upload Lambda Python handler script to S3.
    • Zip your script. 
      • Name: snapshot-all-attached-volumes.zip
      • Be sure its located at the root of your zip file. Upload the zip and not the Python file.
    • Make sure you take note of the uploaded file path, you'll need it later.
  3. Create a new Policy in IAM
    • Name: lambda_s3_exec_role_snapshot_all_attached_volumes
    • Policy Document: see below
  4. Create a new S3 execution role for the Lambda function.
    • Choose a Lambda policy. 
    • Name: lambda_s3_exec_role_snapshot_all_attached_volumes
    • Choose the policy named lambda_s3_exec_role_snapshot_all_attached_volumes
  5. Create a new Lambda function.
    • Name: snapshot_all_attached_volumes
    • Runtime: Python x.x
    • Code Entry Type: Upload a file from Amazon S3
    • S3 Link URL: Use the upload file path from above.
    • Handler: snapshot-all-attached-volumes.snapshot_all_attached_volumes_handler
    • Role: select the role named lambda_s3_exec_role_snapshot_all_attached_volumes
    • Accept the defaults for everything else
    *** WARNING: This creates snapshots but does nothing to clean them up. You will pay for any snapshots created by this script. You should also consider implementing a method of deleting aging snapshots that meets your organizational retention needs. ***

    Lambda Function Python Script:
    There are lots of things you could do to improve this script like adding in a tag based bypass for volumes you don't want to snapshot.

    import boto3
    
    def snapshot_all_attached_volumes_handler(event, context):
     print("Begining automated snapshot of all attached volumes.")
     ec2 = boto3.resource('ec2')
    
     for instance in ec2.instances.all():
      instId = instance.id
      for mapping in list(instance.block_device_mappings):
       attachment = mapping['DeviceName']
       volId = mapping['Ebs']['VolumeId']
       description = "Automated Snapshot of [{0}] attached as [{1}] to [{2}].".format(volId, attachment, instId)
       snapshot = ec2.create_snapshot(VolumeId=volId, Description=description)
       print(description)
     return "Finished automated snapshot of all attached volumes."
    

    Required Policy Document for Lambda Role:
    Be sure to insert your bucket name in the placeholder <***BUCKET NAME***>.
    https://awspolicygen.s3.amazonaws.com/policygen.html can help if you want to build your own.

     {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ],
                "Resource": "arn:aws:logs:*:*:*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "s3:GetObject",
                    "s3:PutObject"
                ],
                "Resource": [
                    "arn:aws:s3:::<***BUCKET NAME***>/*"
                ]
            },
            {
                "Effect": "Allow",
                "Action": [
                    "ec2:CreateSnapshot",
                    "ec2:DescribeInstances"
                ],
                "Resource": [
                   "*"
                ]
            }
        ]
    } 
    

    NOTICE: All thoughts/statements in this article are mine alone and do not represent those of Amazon or Amazon Web services. All referenced AWS services and service names are the property of AWS. Although I have made every effort to ensure that the information in this article was correct at writing, I do not assume and hereby disclaim any liability to any party for any loss, damage, or disruption caused by errors or omissions, whether such errors or omissions result from negligence, accident, or any other cause.