AMI Backup — Instancias EC2 por TAGs

AWS User Group São Paulo
awsugsp
Published in
5 min readApr 10, 2018

Olá pessoa, enfim depois de um bom tempo tentando escrever esse artigo, eis que eu consigo sentar e escrever sobre rotinas de operações de instâncias EC2.

Este artigo é o primeiro de outros nessa linha e vou abordar um tema, que na teoria é bem simples, e que tem várias formas de fazer a mesma coisa. Porém, da forma que criei essa rotina, ainda não tinha visto. Enfim, vamos começar.

A ideia dessa rotina é basicamente fazer backup das instancias EC2 em qualquer região da sua conta AWS a partir de TAGs;

Para isso, precisaremos criar uma função Lambda utilizando Node.js 6.10 com o seguinte código:

let AWS = require("aws-sdk");// Tag of numbers of AMI backups to retain for each EC2 Instance.
let numBackupsToRetainTagName = "BackupRetainDays";
// Tag name to insert on EC2 Inatance
let instancesToBackupTagName = "Backup";
// Tag values to insert on TAG instancesToBackupTagName
let instancesToBackupTagValue = "yes"
// Tag Key Attached To AMIs Created By This Process.
let imageBackupInstanceIdentifierTagName = "ScheduledInstanceId";
const describeIntancesRegion = (ec2, region) => {
let describeInstancesParams = {
DryRun: false,
Filters: [{
Name: "tag:" + instancesToBackupTagName,
Values: [instancesToBackupTagValue]
}]
};
ec2.describeInstances(describeInstancesParams, function(err, data) {
if (err) {
console.log("Failure retrieving instances.");
console.log(err, err.stack);
}
else {
console.log("Instancias para criar imagem Region (" + region + ") - " + data.Reservations.length);
for (let i = 0; i < data.Reservations.length; i++) {
for (let j = 0; j < data.Reservations[i].Instances.length; j++) {
let instance = data.Reservations[i].Instances[j] ;
let tags = instance.Tags ;
let instanceId = instance.InstanceId;
let numBackupsToRetain = 2;
for(let w = 0; w < tags.length; w++) {
let tag = tags[w] ;
if (tag.Key == numBackupsToRetainTagName) {
numBackupsToRetain = tag.Value;
}
}
createImage(ec2, instanceId, region, numBackupsToRetain);
cleanupOldBackups(ec2, instanceId, region, numBackupsToRetain);
}
}
}
});
};
const createImage = (ec2, instanceId,region,numBackupsToRetain) => {
let createImageParams = {
InstanceId: instanceId,
Name: "AMI Snapshot I(" + instanceId + ") T(" + new Date().getTime() + ")",
Description: "AMI Snapshot (" + instanceId + ")",
NoReboot: true,
DryRun: false
};

ec2.createImage(createImageParams, function(err, data) {
if (err) {
console.log("Failure creating image request for Instance: " + region + " - " + instanceId);
console.log(err, err.stack);
}
else {
let imageId = data.ImageId;
console.log("Success creating image request for Instance: " + region + " - " + instanceId + ". Image: " + imageId);
let createTagsParams = {
Resources: [imageId],
Tags: [{
Key: "Name",
Value: "AMI AutoSnapshot"
},
{
Key: imageBackupInstanceIdentifierTagName,
Value: instanceId
},
{
Key: numBackupsToRetainTagName,
Value: numBackupsToRetain
}
]
};
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);
}
});
}
});
};
const cleanupOldBackups = (ec2,instanceId,region, numBackupsToRetain) => {
let describeImagesParams = {
DryRun: false,
Filters: [{
Name: "tag:" + imageBackupInstanceIdentifierTagName,
Values: [instanceId]
}]
};
ec2.describeImages(describeImagesParams, function(err, data) {
if (err) {
console.log("Failure retrieving images for deletion.");
console.log("Error describe instance: " + err, err.stack);
}
else {
let images = data.Images;
console.log("Images: " + images.length + " of " + numBackupsToRetain + " to ratain");
let instanceDictionary = [];
for ( let i = 0; i < images.length; i++ ) {
let currentImage = images[i];
instanceDictionary.push({
ImageId: currentImage.ImageId,
CreationDate: currentImage.CreationDate,
BlockDeviceMappings: currentImage.BlockDeviceMappings
});
}
if (images.length > numBackupsToRetain) {
images.sort(function (a, b) {
return new Date(b.CreationDate) - new Date(a.CreationDate);
});
for (let k = numBackupsToRetain; k < images.length; k++) {
let imageId = images[k].ImageId;
let creationDate = images[k].CreationDate;
let blockDeviceMappings = images[k].BlockDeviceMappings;
console.log("Deregister imageId:" + imageId );
deregisterImage(ec2, imageId, creationDate, blockDeviceMappings);
}
}
else {
console.log("AMI Backup Cleanup not required for Instance: " + instanceId + ". Not enough backups in window yet.");
}
}
});
};
const deregisterImage = (ec2, imageId, creationDate, blockDeviceMappings) => {
let 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: " + imageId + ". Creation Date: " + creationDate);
console.log(err, err.stack);
}
else {
console.log("Success deregistering Image: " + imageId + ". Creation Date: " + creationDate);
for (let p = 0; p < blockDeviceMappings.length; p++) {
let snapshotId = blockDeviceMappings[p].Ebs.SnapshotId;
if (snapshotId) {
deleteSnapshot(ec2,snapshotId);
}
}
}
});
};
const deleteSnapshot = (ec2, snapshotId) => {
let 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 + ".");
}
});
};
exports.handler = (event, context) => {
let ec2 = new AWS.EC2();
let describeRegionsParams = {
DryRun: false
};
ec2.describeRegions(describeRegionsParams, function(err, data) {
if (err) {
console.log("describe instances error " + err, err.stack);
}
else {
console.log("Describe instances sucesso!");
for(let i=0; i < data.Regions.length; i++) {
let region = data.Regions[i] ;
AWS.config.update({region: region.RegionName });
let ec2 = new AWS.EC2();
describeIntancesRegion(ec2, region.RegionName) ;
}
}}
);
};

Lembre-se configurar o tempo de TimeOut ao máximo, uma vez que essa função varrerá todas as instancias de todas as AZs desta conta, com isso, dependendo da quantidade de instancias, o tempo de execução poderá ser grande.

Neste função foram criadas as seguintes variáveis.

Nesta função, foram configuradas duas variáveis a com os valores da TAGs a serem configuradas nas instancias EC2:

numBackupsToRetainTagName = "BackupRetainDays";
instancesToBackupTagName = "Backup";

Neste caso, será necessário criar a TAG BackupRetainDays com a quantidade de versões a serem retidas e a variável Backup com o valor yes nas instancias que entrarão nessa rotina.

Para essa função, é necessário criar também uma role cedendo permissão ao Lambda, Trust Relationship lambda.amazonaws.com com a seguinte policy:

IAM Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:DescribeImages",
"ec2:DeregisterImage",
"ec2:DescribeInstances",
"ec2:DeleteSnapshot",
"ec2:CreateTags",
"logs:*",
"ec2:DescribeRegions",
"ec2:CreateImage"
],
"Resource": "*"
}
]
}

Após a função devidamente criada e com as permissões acima, precisamos configurar o CloudWatch Events conforme o print abaixo:

Pronto, ja temos a função configurada, com as permissões corretas e programada para ser executada todos os dias 0h.

Originally published at medium.com by Wembley Carvalho.
Thanks to
Marcelo Palladino (hide).

--

--

AWS User Group São Paulo
awsugsp

Comunidade para discussões, palestras e networking de Tecnologias AWS no Brasil e no mundo.