Blog

How to create backup of ec2 instances using AWS lambda function

Lambda function for auto AMI Creation (AWS EC2)

Creating backup for resources that are running in production environment is most important task. Suppose, You have 100s of EC2 instances running in your production account. It is important to take backup of all required instances, becuase you may not know what can go down when. If you have enough backup, you can restore it and get your system back up and running. 

In this article, we will see how to create AMI of AWS EC2 instances using lambda function which can later be automted using AWS cloudwatch rules. 

AMI means Amazon Machine Image, when you create AMI of EC2 instance, it comes with OS of EC2 instance, all the packages are installed as it is, all the files are stored as it is. You can just launch that AMI and same kind of configuration and code will be up and running. 

Let’s get started  

There are multiple possible ways to create AMI,
1. We can run aws CLI command which will create AMI, and keep it as cronjob to run everyday, now that creates dependency upon the server, if server is down, AMI won’t be generated.

2. We can create Lambda function for this, as lambda costs us for the time it was being executed, in this case it’d be called once per day only, so cost-effective solution as well.

Let’s see Lambda function for the same

var AWS = require('aws-sdk');
var DateToday = new Date();
var date = DateToday.toISOString();
var timestamp = date.substr(0, 10);
console.log('Today: ' , timestamp);

var DatePrevious = new Date();
DatePrevious.setDate(DatePrevious.getDate() - 30)
var dateprev = DatePrevious.toISOString();
var timestamp_prev = dateprev.substr(0, 10);
console.log('<br>30 days ago was: ' , timestamp_prev);


var INSTANCE_SETTINGS = [
	{
		id: 'i-xxxxxxxxxxxxxxx',
		name: '$servername',
		region: '$regioname'
	}
	
];

exports.handler = function (event, context) {	
	for (var index = 0; index < INSTANCE_SETTINGS.length; index++) {
		let currentIndex = index;
		var ec2 = new AWS.EC2({
			apiVersion: '2016-11-15',
			region: INSTANCE_SETTINGS[index].region,
		});

       var ImageNameString = "auto-ami-" + (INSTANCE_SETTINGS[index].name) + "-" + timestamp;
		var params = {
			Description: "Created from Lambda",
			InstanceId: INSTANCE_SETTINGS[index].id,
			Name: ImageNameString,
			NoReboot: true,
		};
		ec2.createImage(params, function (err, data0) {
			if (err) console.log(err, err.stack);
			else console.log(data0);
		});

        var ImageNameToDelete = "auto-ami-" + (INSTANCE_SETTINGS[index].name) + "-" + timestamp_prev;
        console.log("Image name to delete : ",ImageNameToDelete)
        let params_todelete = {}
        params_todelete.Filters = [...PARAMS_TO_DELETE.Filters]
        params_todelete.Owners = ['self'];
        params_todelete.Filters[0].Values = [ImageNameToDelete];


		ec2.describeImages(params_todelete, function (err, data) {
			if (err) console.log(err, err.stack);
			else {
                if(data.Images.length > 0) {
                    var newec2 = new AWS.EC2({
						apiVersion: '2016-11-15',
						region: INSTANCE_SETTINGS[currentIndex].region,
					});
    
                    newec2.deregisterImage({ ImageId : data.Images[0].ImageId }, function(err, data) {
                        if (err) console.log(err, err.stack); // an error occurred
                        else     console.log("SUCCESS");           // successful response
                    });
                }
            }
        });
	}
};

const PARAMS_TO_DELETE = {
    Filters: [
        {
            Name: 'name',
            Values: [],
        }
    ],
    Owners: ['self']
};

Above function will create AMI and delete AI that is older than 30 days and created by this code, as deletion is dependent upon name here.

Above code can run on NodeJS version 12 or below
EC2 full access will be required.

If you want to create AMI for more than 1 EC2 instance, look at the script below. Instances can be in any AWS region.

var AWS = require('aws-sdk');
var DateToday = new Date();
var date = DateToday.toISOString();
var timestamp = date.substr(0, 10);
console.log('Today: ' , timestamp);

var DatePrevious = new Date();
DatePrevious.setDate(DatePrevious.getDate() - 30)
var dateprev = DatePrevious.toISOString();
var timestamp_prev = dateprev.substr(0, 10);
console.log('<br>30 days ago was: ' , timestamp_prev);


var INSTANCE_SETTINGS = [
    {
       id: 'i-xxxxxxxxxxxxxxx',
       name: '$servername',
       region: '$regioname'
    },
    {
       id: 'i-xxxxxxxxxxxxxx2',
       name: '$servername2',
       region: '$regioname2'
    },
    {
       id: 'i-xxxxxxxxxxxxxx3',
       name: '$servername3',
       region: '$regioname3'
    }

];

exports.handler = function (event, context) {	
	for (var index = 0; index < INSTANCE_SETTINGS.length; index++) {
		let currentIndex = index;
		var ec2 = new AWS.EC2({
			apiVersion: '2016-11-15',
			region: INSTANCE_SETTINGS[index].region,
		});

       var ImageNameString = "auto-ami-" + (INSTANCE_SETTINGS[index].name) + "-" + timestamp;
		var params = {
			Description: "Created from Lambda",
			InstanceId: INSTANCE_SETTINGS[index].id,
			Name: ImageNameString,
			NoReboot: true,
		};
		ec2.createImage(params, function (err, data0) {
			if (err) console.log(err, err.stack);
			else console.log(data0);
		});

        var ImageNameToDelete = "auto-ami-" + (INSTANCE_SETTINGS[index].name) + "-" + timestamp_prev;
        console.log("Image name to delete : ",ImageNameToDelete)
        let params_todelete = {}
        params_todelete.Filters = [...PARAMS_TO_DELETE.Filters]
        params_todelete.Owners = ['self'];
        params_todelete.Filters[0].Values = [ImageNameToDelete];


		ec2.describeImages(params_todelete, function (err, data) {
			if (err) console.log(err, err.stack);
			else {
                if(data.Images.length > 0) {
                    var newec2 = new AWS.EC2({
						apiVersion: '2016-11-15',
						region: INSTANCE_SETTINGS[currentIndex].region,
					});
    
                    newec2.deregisterImage({ ImageId : data.Images[0].ImageId }, function(err, data) {
                        if (err) console.log(err, err.stack); // an error occurred
                        else     console.log("SUCCESS");           // successful response
                    });
                }
            }
        });
	}
};

const PARAMS_TO_DELETE = {
    Filters: [
        {
            Name: 'name',
            Values: [],
        }
    ],
    Owners: ['self']
};

Now, to automate creation of AMI as backup purpose, you need to run cronjob, which will call this lambda function and create AMI whenever required. for that we can use AWS Cloudwatch.

Let’s see how to do that

We can create a trigger from cloudwatch which will run everyday once and call this lambda function for execution

30 12 * * ? * 
// which means it will run at 12:30:00 GMT daily.

you can check logs in cloudwatch as well.

Drafted On,
22nd January 2022
DevOps @identicalCloud.com

Updated On,
17th May 2021
DevOps @ identicalCloud.com

Leave a Comment