Expansão de disco automática EC2 (Lambda+CloudWatch+SNS)
Tags: aws, python, lambda, tutorial
Published: 2023-07-03 16:31:13

1- Crie uma política no IAM com as  permissões.

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:ListMetrics",
                "ec2:DescribeTags"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameter"
            ],
            "Resource": "arn:aws:ssm:::parameter/AmazonCloudWatch-*"
        }
    ]
}

```
2- Crie uma função (role) para EC2 e adicione a politica criada na etapa 1.

3- Instale o agente do CloudWatch na instância. (nesse exemplo estou utilizando em uma maquina linux ubuntu).

`wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i -E ./amazon-cloudwatch-agent.deb`

Para coletar as métricas necessárias rode os comandos:

```
cd /opt/aws/amazon-cloudwatch-agent/bin 
wget https://raw.githubusercontent.com/railandermarques/cloudwatchmonitoring/master/config.json 
cd /opt/aws/amazon-cloudwatch-agent/etc/ 
cp -av ../bin/config.json amazon-cloudwatch-agent.json 
mkdir -p /usr/share/collectd/ 
touch /usr/share/collectd/types.db 
sudo sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/bin/config.json -s 
systemctl enable amazon-cloudwatch-agent 
systemctl status amazon-cloudwatch-agent
```

4-Crie um tópico SNS com nome de sua escolha.

5- Acesse o CloudWatch no console AWS > All Alarms > Create Alarm > Select metric, e pesquise por CWAgent, verifique o que possui métricas de disco. 

* Selecione a métrica “disk_used_percent” com o path da partição que o disco utiliza. (Nesse caso estou monitorando para expandir o disco root.)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdpokeuf84aak5b4gebd.png)

* Defina o período no qual você deseja receber alertas e indique a porcentagem igual ou maior na qual você deseja que o disco seja expandido. Em seguida, clique em "Next".

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1u19cxucyr7l2d3xvwam.png)

6 - Na aba "Notificação", selecione "Em alarme", "Selecionar um tópico SNS existente" e escolha o tópico que foi criado na etapa 4. Em seguida, clique em "Next".


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zm0w00so425d002ktxg8.png)

7- Defina um nome para o alarme, clique em "Next" e, em seguida, em "Create alarm".

8- Este exemplo envia alertas para um grupo do Discord e para que isso funcione, é necessário adicionar a camada "requests" ao Lambda. Siga as etapas abaixo:

* Caso não sabia criar as dependências do request baixe o arquivo - https://github.com/henbsantos/requests-lambda/blob/main/requests.zip

* Acesse o AWS Lambda, vá ate a aba Layers > Create Layer.

* Defina um nome > Faça o upload do Zip Baixado > Compatible runtimes selecione Python 3.9 > Create.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebikcyi533g5aa5u4sri.png)

9- Crie uma função Lambda com um nome de sua escolha, selecione Python 3.9 e atribua uma função que permita o acesso a todos os EBS e instâncias EC2 (neste exemplo, foi utilizado o acesso de Administrador, mas é possível ajustar isso para uma permissão mais restrita).

10- Dentro da função Lambda, na aba "Layers", clique em "Add a Layer" e selecione a camada criada na etapa 8.

11- Cole o código python no lambda_function.py

Observação: esse código esta expandindo o disco root,  partição /dev/xvda 1, tipo ext4. Caso o disco tenha uma configuração diferente é necessario alterar os comandos da linha 50. 
Linha 41 - alterar ID do volume que será expandido.
Linha 48 - alterar o ID da instância.
Linha 55 - Alterar endereço do webhook do discord (colocar o do chat que ira receber os alertas da finalização de expansão).
Linha 56 - Editar mensagem a ser enviada para o alerta do Discord.

```
import boto3
import time
import json
import requests


def expandir_volume_ebs(volume_id):
    ec2_client = boto3.client('ec2')

    response = ec2_client.describe_volumes(VolumeIds=[volume_id])
    current_size = response['Volumes'][0]['Size']
    new_size = current_size + 1

    response = ec2_client.modify_volume(
        VolumeId=volume_id,
        Size=new_size
    )

    return response

def executar_comandos_shell(instance_id, comandos):
    ssm_client = boto3.client('ssm')

    response = ssm_client.send_command(
        InstanceIds=[instance_id],
        DocumentName="AWS-RunShellScript",
        Parameters={'commands': comandos}
    )

    return response

def enviar_mensagem_discord(webhook_url, message):
    data = {
        'content': message
    }
    response = requests.post(webhook_url, json=data)
    if response.status_code != 204:
        print('Falha ao enviar mensagem para o Discord:', response.text)

def lambda_handler(event, context):
    volume_id = "vol-0d2dfdd723adb27bd"

    response = expandir_volume_ebs(volume_id)
    print("Volume expandido:", response)

    time.sleep(180)

    instance_id = "i-0ac8a7a16a50b9f06"

    comandos = ['sudo su', 'growpart /dev/xvda 1', 'resize2fs /dev/xvda1']

    response = executar_comandos_shell(instance_id, comandos)
    print("Comandos executados:", response)

    webhook_url = 'https://discord.com/api/webhooks/1123651003187728464/OYQ_--_Am'
    message = 'O disco root /dev/xvda 1 da instância i-0ac8a7a16a50b9f06 foi expandido!'
    enviar_mensagem_discord(webhook_url, message)

    return {
        'statusCode': 200,
        'body': json.dumps('Lambda executada !')
    }
```

12 -Após o lambda ser executado você deve receber um alerta no chat do discord do webhook. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ggaf3htgs7oheibvu86b.png)