Serverless - Automate AMI creation / deletion using AWS Lambda

Earlier we have blogged on EBS volumes Snapshot. If you have multiple EBS volumes in the instance, its recommended to have AMI instead of EBS snapshot. During disaster or instance failure, the instance recovery will be faster with AMIs in comparison to EBS snapshots. So, in this post, we are going to show how you can automate the backups (AMIs/Snaphots) through AWS Lambda.

Tag the Instances

Go to EC2 Console.

Go to Tags tab. Click Add/Edit Tags. Add a tag with key “Backup” and value “yes”.

Click Save. Similarly, apply the same tag to the instances for which you want to create AMIs.

Lambda Function to Create AMI

Create a new Lambda Function:

Paste the following inline code:

var aws = require('aws-sdk');  
aws.config.region = 'ap-south-1';  
var ec2 = new aws.EC2();  
var now = new Date();  
date = now.toISOString().substring(0, 10)  
hours = now.getHours()  
minutes = now.getMinutes()  
exports.handler = function(event, context) {  
    var instanceparams = {
        Filters: [{
            Name: 'tag:Backup',
            Values: [
                'yes'
            ]
        }]
    }
    ec2.describeInstances(instanceparams, function(err, data) {
        if (err) console.log(err, err.stack);
        else {
            for (var i in data.Reservations) {
                for (var j in data.Reservations[i].Instances) {
                    instanceid = data.Reservations[i].Instances[j].InstanceId;
                    nametag = data.Reservations[i].Instances[j].Tags
                    for (var k in data.Reservations[i].Instances[j].Tags) {
                        if (data.Reservations[i].Instances[j].Tags[k].Key == 'Name') {
                            name = data.Reservations[i].Instances[j].Tags[k].Value;
                        }
                    }
                    console.log("Creating AMIs of the Instance: ", name);
                    var imageparams = {
                        InstanceId: instanceid,
                        Name: name + "_" + date + "_" + hours + "-" + minutes,
                        NoReboot: true
                    }
                    ec2.createImage(imageparams, function(err, data) {
                        if (err) console.log(err, err.stack);
                        else {
                            image = data.ImageId;
                            console.log(image);
                            var tagparams = {
                                Resources: [image],
                                Tags: [{
                                    Key: 'DeleteOn',
                                    Value: 'yes'
                                }]
                            };
                            ec2.createTags(tagparams, function(err, data) {
                                if (err) console.log(err, err.stack);
                                else console.log("Tags added to the created AMIs");
                            });
                        }
                    });
                }
            }
        }
    });
}

In this script, we have used the three EC2 APIs:

  • describeInstances() which will describe the filtered Instances based on the key tag “Backup” and the value “yes”.
  • createImage() which will create the AMIs of the filtered instances.
  • createTags() which will assign the tags with key “DeleteOn” and the value “yes” to the created Images.

Once the function is created, go to “Triggers” tab and schedule the time at which function should execute. Click Add Trigger.

Select CloudWatch Events - Schedule . Give a name to the rule and add a description.

Select Cron. Update the cron job according to UTC. To execute the function at midnight, update the cron as follows:

cron(30 18 ? * MON-FRI *)  

Submit.

Lambda Function to Delete AMI / Snapshots

Similarly, for deletion of the AMIs and Snapshots, Create a lambda function with the following inline code and schedule it:

var aws = require('aws-sdk');  
aws.config.region = 'ap-south-1';  
var ec2 = new aws.EC2();  
var d = new Date();  
var x = 7;  /* ------Retention Days------- */  
d.setDate(d.getDate() - x);  
reqdate = d.toISOString().substring(0, 10);  
exports.handler = function(event, context) {  
ec2.describeImages({  
    Owners: [
        'self'
    ],
    Filters: [{
        Name: 'tag:DeleteOn',
        Values: [
            'yes'
        ]
    }]

}, function(err, data) {
    if (err) console.log(err, err.stack);
    else {
        for (var j in data.Images) {
            imagename = data.Images[j].Name
            imageid = data.Images[j].ImageId

            if (imagename.indexOf(reqdate) > -1) {
                console.log("image that is going to be deregistered: ", imagename);
                console.log("image id: ", imageid);

                var deregisterparams = {
                    ImageId: imageid
                };
                ec2.deregisterImage(deregisterparams, function(err, data01) {
                    if (err) console.log(err, err.stack); // an error occurred
                    else {
                        console.log("Image Deregistered");

                    }
                });
            }
        }
        setTimeout(function() {
            for (var j in data.Images) {
                imagename = data.Images[j].Name
                if (imagename.indexOf(reqdate) > -1) {
                    for (var k in data.Images[j].BlockDeviceMappings) {
                        snap = data.Images[j].BlockDeviceMappings[k].Ebs.SnapshotId;
                        console.log(snap);
                        var snapparams = {
                            SnapshotId: snap
                        };
                        ec2.deleteSnapshot(snapparams, function(err, data) {
                            if (err) console.log(err, err.stack); // an error occurred
                            else console.log("Snapshot Deleted"); // successful response
                        });
                    }
                }
            }
        }, 10000);
    }
});
}

In this Script, the following EC2 APIs are being used:

  • describeImages() which will describe the filtered Images based on the key tag “DeleteOn” and the value “yes”.
  • deregisterImage() which will deregister the filtered images.
  • deleteSnapshot() which will delete the Snapshots associated with the filtered Images.

Hope this will reduce your manual efforts while taking backups. Happy Automation..!! :)

Priyanka Sharma

Priyanka is Senior Cloud and DevOps Engineer. She can churn out CloudFormation templates at a moment's notice and play with Chef/Ansible. Dancing, music, badminton and word games are her hobbies

comments powered by Disqus