queirozsc
8/30/2017 - 4:28 PM

Scheduling Automated AMI Backups of Your EC2 Instances https://www.aaronmedacco.com/blog/post/2017/07/03/scheduling-automated-ami-backups-of

aws events put-targets --rule ami-backup-event-rule --targets "Id"="1","Arn"="ARN"
aws lambda add-permission --function-name ami-backup-function --statement-id LambdaPermission --action "lambda:InvokeFunction" --principal events.amazonaws.com --source-arn ARN
aws events put-rule --name ami-backup-event-rule --schedule-expression "rate(1 day)"
aws lambda create-function --function-name ami-backup-function --runtime nodejs6.10 --handler index.handler --role ARN --zip-file fileb://index.zip --timeout 30
var AWS = require("aws-sdk");
var ec2 = new AWS.EC2();
 
var numBackupsToRetain = 2; // The Number Of AMI Backups You Wish To Retain For Each EC2 Instance.
var instancesToBackupTagName = "BackupAMI"; // Tag Key Attached To Instances You Want AMI Backups Of. Tag Value Should Be Set To "Yes".
var imageBackupTagName = "ScheduledAMIBackup"; // Tag Key Attached To AMIs Created By This Process. This Process Will Set Tag Value To "True".
var imageBackupInstanceIdentifierTagName = "ScheduledAMIInstanceId"; // Tag Key Attached To AMIs Created By This Process. This Process Will Set Tag Value To The Instance ID.
var deleteSnaphots = true; // True if you want to delete snapshots during cleanup. False if you want to only delete AMI, and leave snapshots intact.
 
exports.handler = function(event, context) {
    var describeInstancesParams = {
        DryRun: false,
        Filters: [{
            Name: "tag:" + instancesToBackupTagName,
            Values: ["Yes"]
        }]
    };
    ec2.describeInstances(describeInstancesParams, function(err, data) {
        if (err) {
            console.log("Failure retrieving instances.");
            console.log(err, err.stack); 
        }
        else {
            for (var i = 0; i < data.Reservations.length; i++) {
                for (var j = 0; j < data.Reservations[i].Instances.length; j++) {
                    var instanceId = data.Reservations[i].Instances[j].InstanceId;
                    createImage(instanceId);
                }
            }
        }
    });
    cleanupOldBackups();
};
 
var createImage = function(instanceId) {
    console.log("Found Instance: " + instanceId);
    var createImageParams = {
        InstanceId: instanceId,
        Name: "AMI Scheduled Backup I(" + instanceId + ") T(" + new Date().getTime() + ")",
        Description: "AMI Scheduled Backup for Instance (" + instanceId + ")",
        NoReboot: true,
        DryRun: false
    };
    ec2.createImage(createImageParams, function(err, data) {
        if (err) {
            console.log("Failure creating image request for Instance: " + instanceId);
            console.log(err, err.stack);
        }
        else {
            var imageId = data.ImageId;
            console.log("Success creating image request for Instance: " + instanceId + ". Image: " + imageId);
            var createTagsParams = {
                Resources: [imageId],
                Tags: [{
                    Key: "Name",
                    Value: "AMI Backup I(" + instanceId + ")"
                },
                {
                    Key: imageBackupTagName,
                    Value: "True"
                },
                {
                    Key: imageBackupInstanceIdentifierTagName,
                    Value: instanceId
                }]
            };
            ec2.createTags(createTagsParams, function(err, data) {
                if (err) {
                    console.log("Failure tagging Image: " + imageId);
                    console.log(err, err.stack);
                }
                else {
                    console.log("Success tagging Image: " + imageId);
                }
            });
        }
    });
};
 
var cleanupOldBackups = function() {
    var describeImagesParams = {
        DryRun: false,
        Filters: [{
            Name: "tag:" + imageBackupTagName,
            Values: ["True"]
        }]
    };
    ec2.describeImages(describeImagesParams, function(err, data) {
        if (err) {
            console.log("Failure retrieving images for deletion.");
            console.log(err, err.stack); 
        }
        else {
            var images = data.Images;
            var instanceDictionary = {};
            var instances = [];
            for (var i = 0; i < images.length; i++) {
                var currentImage = images[i];
                for (var j = 0; j < currentImage.Tags.length; j++) {
                    var currentTag = currentImage.Tags[j];
                    if (currentTag.Key === imageBackupInstanceIdentifierTagName) {
                        var instanceId = currentTag.Value;
                        if (instanceDictionary[instanceId] === null || instanceDictionary[instanceId] === undefined) {
                            instanceDictionary[instanceId] = [];
                            instances.push(instanceId);
                        }
                        instanceDictionary[instanceId].push({
                            ImageId: currentImage.ImageId,
                            CreationDate: currentImage.CreationDate,
                            BlockDeviceMappings: currentImage.BlockDeviceMappings
                        });
                        break;
                    }
                }
            }
            for (var t = 0; t < instances.length; t++) {
                var imageInstanceId = instances[t];
                var instanceImages = instanceDictionary[imageInstanceId];
                if (instanceImages.length > numBackupsToRetain) {
                    instanceImages.sort(function (a, b) {
                       return new Date(b.CreationDate) - new Date(a.CreationDate); 
                    });
                    for (var k = numBackupsToRetain; k < instanceImages.length; k++) {
                        var imageId = instanceImages[k].ImageId;
                        var creationDate = instanceImages[k].CreationDate;
                        var blockDeviceMappings = instanceImages[k].BlockDeviceMappings;
                        deregisterImage(imageId, creationDate, blockDeviceMappings);
                    }   
                }
                else {
                    console.log("AMI Backup Cleanup not required for Instance: " + imageInstanceId + ". Not enough backups in window yet.");
                }
            }
        }
    });
};
 
var deregisterImage = function(imageId, creationDate, blockDeviceMappings) {
    console.log("Found Image: " + imageId + ". Creation Date: " + creationDate);
    var deregisterImageParams = {
        DryRun: false,
        ImageId: imageId
    };
    console.log("Deregistering Image: " + imageId + ". Creation Date: " + creationDate);
    ec2.deregisterImage(deregisterImageParams, function(err, data) {
       if (err) {
           console.log("Failure deregistering image.");
           console.log(err, err.stack);
       } 
       else {
           console.log("Success deregistering image.");
           if (deleteSnaphots) {
                for (var p = 0; p < blockDeviceMappings.length; p++) {
                   var snapshotId = blockDeviceMappings[p].Ebs.SnapshotId;
                   if (snapshotId) {
                       deleteSnapshot(snapshotId);
                   }
               }    
           }
       }
    });
};
 
var deleteSnapshot = function(snapshotId) {
    var deleteSnapshotParams = {
        DryRun: false,
        SnapshotId: snapshotId
    };
    ec2.deleteSnapshot(deleteSnapshotParams, function(err, data) {
        if (err) {
            console.log("Failure deleting snapshot. Snapshot: " + snapshotId + ".");
            console.log(err, err.stack);
        }
        else {
            console.log("Success deleting snapshot. Snapshot: " + snapshotId + ".");
        }
    })
};
aws iam attach-role-policy --policy-arn ARN --role-name ami-backup-role
aws iam create-role --role-name ami-backup-role --assume-role-policy-document file://role-trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
aws iam create-policy --policy-name ami-backup-policy --policy-document file://iam-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1499061014000",
            "Effect": "Allow",
            "Action": [
                "ec2:CreateImage",
                "ec2:CreateTags",
                "ec2:DeleteSnapshot",
                "ec2:DeregisterImage",
                "ec2:DescribeImages",
                "ec2:DescribeInstances"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}