AWS: Snapshots automáticos de EC2 e AMI com Lambda e CloudWatch


Olá, abaixo apresento 3 scripts em Python 2.7 para inserir no Lambda e executar via agendamento no Cloudwatch com o objetivo de gerar Snapshots das EC2 e também criar AMIs destas maquinas, excluindo após um período de retenção.
Com isto, você terá snapshots e AMI dos servidores e os mais antigos serão excluídos.





No IAM (Identi Access Management) você deverá criar 1 role, aqui eu a chamei de "AWS_backup" e ela possui 2 polices.
Uma para o EC2 (snapshot da instancia) e outra para o AMI.
Segue o código:


Policy 'ec2-snapshot':

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:*"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": "ec2:Describe*",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateSnapshot",
                "ec2:DeleteSnapshot",
                "ec2:CreateTags",
                "ec2:ModifySnapshotAttribute",
                "ec2:ResetSnapshotAttribute"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}




Policy 'AMI-backup':


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1476467851000",
            "Effect": "Allow",
            "Action": [
                "ec2:CreateImage",
                "ec2:DeregisterImage",
                "ec2:DescribeImageAttribute",
                "ec2:DescribeImages"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}





Após isto, vamos criar o script responsável por criar e tagear os snapshots (script no lambda com python 2.7), nomeie-e com o nome que desejar, lembre-se da descrição também (importante!), você usará o nome dele posteriormente para agendar a execução.

No seu script, você precisará trocar o nome da região (eu utilizo a 'us-east-1').
Outra coisa que deverá mudar é a "Key-tag".
Para determinar quais servidores (EC2) terão seus snapshots e AMI criados automaticamente, eu criei uma tag chamada "Backup" e para todas as maquinas que eu quero o snapshot, coloquei a tag como "True" (como na imagem abaixo):



Script:




import boto3  
import collections  
import datetime

ec = boto3.client('ec2', region_name='us-east-1')

#begins lambda function
def lambda_handler(event, context):  
    reservations = ec.describe_instances(
        Filters=[
            {'Name': 'tag-key', 'Values': ['Backup', 'True']},
        ]
    ).get(
        'Reservations', []
    )

    instances = sum(
        [
            [i for i in r['Instances']]
            for r in reservations
        ], [])

    print "Number of the Instances : %d" % len(instances)

    to_tag = collections.defaultdict(list)

    for instance in instances:
        try:
            retention_days = [
                int(t.get('Value')) for t in instance['Tags']
                if t['Key'] == 'Retention'][0]
        except IndexError:
            # Please give your retention period day
            retention_days = 5

        for dev in instance['BlockDeviceMappings']:
            if dev.get('Ebs', None) is None:
                continue
            vol_id = dev['Ebs']['VolumeId']
            for name in instance['Tags']:
                # To store the instance tag value
                Instancename= name['Value']
                # To store the instance key value
                key= name['Key']
                # Below the code is create Snapshot name as instance Name 
                if key == 'Name' :
                    ins_name = Instancename
                    print "Found EBS volume %s on instance %s" % (
                    vol_id, instance['InstanceId'])

            #To get all the instance tags deatils
            for name in instance['Tags']:
                # To store the instance tag value
                Instancename= name['Value']
                # To store the instance key value
                key= name['Key']
                # Below the code is create Snapshot name as instance Name 
                if key == 'Name' :
                    snap = ec.create_snapshot(
                    VolumeId=vol_id,
                    Description=Instancename,
                    )
                    print "snap %s" %snap

            to_tag[retention_days].append(snap['SnapshotId'])

            print "Retaining snapshot %s of volume %s from instance %s for %d days" % (
                snap['SnapshotId'],
                vol_id,
                instance['InstanceId'],
                retention_days,

            )
            for retention_days in to_tag.keys():
                delete_date = datetime.date.today() + datetime.timedelta(days=retention_days)
                snap = snap['Description'] + str('_')
                # Here to get current date 
                snapshot = snap + str(datetime.date.today())   
                # to mention the current date formet
                delete_fmt = delete_date.strftime('%Y-%m-%d')
                print "Will delete %d snapshots on %s" % (len(to_tag[retention_days]), delete_fmt)
                # below code is create the name and current date as instance name
                ec.create_tags(
                Resources=to_tag[retention_days],
                Tags=[
                {'Key': 'DeleteOn', 'Value': delete_fmt},
                {'Key': 'Name', 'Value': snapshot },
                ]
                ) 
        to_tag.clear()













Agora, vamos criar o script responsável por apagar os snapshots antigos.
Novamente, use o Python 2.7 como runtime, note que a tag para deletar é diferente (ele irá criar a tag e inserir como valor a data em que deverá ser deletado) e atente-se ao region_name:


import boto3  
import re  
import datetime

#Please mention your region name
#below line code is call cross region
ec = boto3.client('ec2', region_name='us-east-1')  
iam = boto3.client('iam')

#begins lambda function
def lambda_handler(event, context):  
    account_ids = list()
    try:         
        iam.get_user()
    except Exception as e:
        # use the exception message to get the account ID the function executes under
        account_ids.append(re.search(r'(arn:aws:sts::)([0-9]+)', str(e)).groups()[1])

    delete_on = datetime.date.today().strftime('%Y-%m-%d')
    filters = [
        {'Name': 'tag-key', 'Values': ['DeleteOn']},
        {'Name': 'tag-value', 'Values': [delete_on]},
    ]
    snapshot_response = ec.describe_snapshots(OwnerIds=account_ids, Filters=filters)

    for snap in snapshot_response['Snapshots']:
        print "Deleting snapshot %s" % snap['SnapshotId']
        ec.delete_snapshot(SnapshotId=snap['SnapshotId'])








Nosso terceiro e ultimo script será responsável por criar e remover as images das instâncias (AMI), perceba que o tempo de retenção meu é de 5 dias:


import boto3
import collections
import datetime

ec = boto3.client('ec2')

def lambda_handler(event, context):
    
    reservations = ec.describe_instances(
        Filters=[
            {'Name': 'tag-key', 'Values': ['backup', 'Backup']},
        ]
    ).get(
        'Reservations', []
    )

    instances = sum(
        [
            [i for i in r['Instances']]
            for r in reservations
        ], [])

    print "Found %d instances that need backing up" % len(instances)

    to_tag = collections.defaultdict(list)

    for instance in instances:
        try:
            retention_days = [
                int(t.get('Value')) for t in instance['Tags']
                if t['Key'] == 'Retention'][0]
        except IndexError:
            retention_days = 5
        finally:   
            create_time = datetime.datetime.now()
            create_fmt = create_time.strftime('%Y-%m-%d.%H.%M.%S')
            AMIid = ec.create_image(InstanceId=instance['InstanceId'], Name="Lambda - " + instance['InstanceId']  + " From " + create_fmt, Description="Lambda created AMI of instance " + instance['InstanceId'], NoReboot=True, DryRun=False)
            to_tag[retention_days].append(AMIid['ImageId'])
            print "Retaining AMI %s of instance %s for %d days" % (
                AMIid['ImageId'],
                instance['InstanceId'],
                retention_days,
            )

    for retention_days in to_tag.keys():
        delete_date = datetime.date.today() + datetime.timedelta(days=retention_days)
        delete_fmt = delete_date.strftime('%m-%d-%Y')
        print "Will delete %d AMIs on %s" % (len(to_tag[retention_days]), delete_fmt)

        ec.create_tags(
            Resources=to_tag[retention_days],
            Tags=[
                {'Key': 'DeleteOn', 'Value': delete_fmt},
            ]
        )







Por fim,
O último passo e criar o agendamento para execução disto dentro do Cloudwatch, basta seguir o Wizard e apontar para os scripts criados em Python no Lambda.


Mais vistos no mês:

TuxMath - Tux, do Comando da Matemática. Ensino e diversão a crianças.

As melhores distribuições Linux para 2017

OPNsense - Firewall Open Source

Teste de Performance de Rede com Iperf

DHCP - Guia Completo

SSD no linux

Aula #14 - Os sistemas de arquivos ext2/ext3/ext4

Ophcrack: Descubra todas as senhas do Windows

F-Droid: Loja de app Open Source para Android