Railander Marques
Posted on January 16, 2024
Este tutorial oferece uma solução para ligar ou desligar uma instância EC2 na AWS através do envio de emails para um endereço específico. O processo é desencadeado por meio de eventos do Amazon S3, onde os emails são armazenados. O fluxo geral é explicado conforme os seguintes passos:
Configuração do SES: Criação de um subdomínio no Amazon Simple Email Service (SES) para receber os emails. Isso é necessário para garantir a correta leitura dos cabeçalhos dos emails enviados para o domínio principal.
Configuração da Regra no SES: Definição de uma regra no SES para direcionar emails específicos para um bucket no Amazon S3.
Configuração da Lambda: Implementação de uma função Lambda em Python 3.10 para processar os eventos do S3. A Lambda verifica o remetente autorizado no email, executa a ação desejada (ligar ou desligar a instância EC2), e notifica o remetente sobre a conclusão da ação.
Configuração do S3: Adição de uma trigger no bucket S3 configurada no SES para acionar a Lambda quando novos emails são recebidos.
O código Python na Lambda verifica o remetente autorizado, lê o conteúdo do email, identifica a ação desejada (ligar ou desligar) e executa a operação na instância EC2 associada. O remetente é notificado sobre o status da ação via email.
O tutorial fornece observações importantes, como a necessidade de adicionar permissões adequadas na Lambda, ajustar as tags da instância desejada e verificar os emails autorizados no código. Além disso, são apresentadas dicas úteis, como a configuração de ciclos de vida no S3 e no CloudWatch Logs.
O resultado final é uma solução eficiente para controlar instâncias EC2 na AWS por meio do envio de emails autorizados, proporcionando automação e praticidade.
Explicação básica do uso:
Você envia um email para aws@sandbox.dominio.com.br com EXATAMENTE no corpo de texto os dizeres: (não importa o assunto do email, somente o corpo do email)
Ligar VM SRV_LNX_BI
Desligar VM SRV_LNX_BI
Dai a instancia vai ligar ou desligar, se seu email estiver autorizado na lambda e no SES.
Workflow Diagram:
Passo 1: Configurar um subdomínio no SES (Ex.: sandbox.dominio.com.br) com o MailFrom habilitado. Neste caso vamos usar um subdomínio no SES porque, ao enviar um email para o domínio principal, o email não é direcionado diretamente para o SES devido às configurações MX estarem associadas a outro servidor de email (Google, Microsoft, Zimbra), com essa configuração, o Amazon SES não consegue ler corretamente o cabeçalho (header) do email e, consequentemente, não consegue enviá-lo para o Amazon S3 da maneira desejada.
Passo 2: Na pagina do SES, Vá em “Email receiving” > Você vera uma regra de “INBOUND MAIL”, clique nela > Create rule > adicione um nome como no print abaixo > Next.
Passo 2.1: Em "Recipient conditions”, insira um email que você recebera as ações solicitando o desligamento ou ligamento das instâncias (ex.: aws@sandbox.dominio.com.br), e configure-o no Workmail depois, com o subdomínio validado, clique em Next.
Passo 2.2: Clique em “add new action” > “Deliver to Amazon S3 Bucket” > Adicione um bucket para armazenar os emails recebidos, para posteriormente a Lambda ler este conteúdo automaticamente, Next e depois Create Rule.
Com isso, você fará o SES publicar os emails recebidos no bucket S3.
Passo 3: Agora precisamos configurar o código na lambda e uma trigger de eventos, que ao receber algo no S3, ira filtrar o conteúdo, e recebendo as mensagens definidas para ligar ou desligar a instancia, executar as ações pre-definidas.
Para lambda, vamos configurar um runtime python 3.10, adicionar as permissões para EC2 (Para ligar e desligar as instancias) Bucket S3 (Para ler o bucket e analisar as mensagens publicadas pelo SES), CloudWatch Logs (Para logs) e SES (Para envio de notificações).
Segue o código:
import json
import boto3
import re
import email
from botocore.exceptions import NoCredentialsError
s3 = boto3.client('s3')
ec2 = boto3.client('ec2')
ses = boto3.client('ses')
authorized_senders = [
'railander.marques@dominio.com.br',
'email-teste@dominio.edu.br'
]
def lambda_handler(event, context):
print(json.dumps(event)) # Imprimir o evento
################### Verificar se o evento é do S3
if 'Records' not in event or not event['Records'][0].get('s3'):
return {'statusCode': 400, 'body': json.dumps('Event is not from S3')}
################### Obter o nome do bucket e do arquivo do evento
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
################### Baixar o arquivo do S3
try:
s3.download_file(bucket, key, '/tmp/email')
print('File downloaded successfully') # Adicionado print
except NoCredentialsError:
return {'statusCode': 400, 'body': json.dumps('Credentials not available')}
################### Ler o conteúdo do email
with open('/tmp/email', 'r', encoding='utf-8') as f:
email_content = f.read()
print(f'Email content: {email_content}') # Adicionado print
################### Encontrar o remetente do email
sender_match = re.search(r'From: .*<(.+)>', email_content)
################### Se o remetente for autorizado, executar a ação
sender_email = sender_match.group(1)
if sender_email not in authorized_senders:
################### Enviar email de notificação para o remetente não autorizado
send_response_email([sender_email, 'infraestrutura@dominio.com.br'], sender_email, 'Requisição de ação não autorizada', f'''
O remetente {sender_email} não está autorizado para executar essa ação.
Caso você acredite que isso seja um erro ou tenha alguma dúvida, por favor entre em contato com nossa equipe de suporte.
Atenciosamente,
Equipe de Infraestrutura
''')
return {'statusCode': 401, 'body': json.dumps('Unauthorized sender')}
else:
################### Se o remetente for autorizado, executar a ação
sender_email = sender_match.group(1)
################### Localizar a ação de ligar ou desligar a instância no conteúdo do email
body_match = re.search(r'Ligar VM SRV_LNX_BI|Desligar VM SRV_LNX_BI', email_content)
if body_match:
body = body_match.group(0)
if 'Ligar VM SRV_LNX_BI' in body:
instance_id = get_instance_id()
print(f'Starting instance {instance_id}...')
ec2.start_instances(InstanceIds=[instance_id])
send_response_email([sender_email, 'infraestrutura@dominio.com.br'], sender_email, 'Ação de Instância - Ligamento', f'A instância associada à tag "SRV_LNX_BI" está sendo ligada conforme sua solicitação.Remetente Original: {sender_email}Caso você tenha mais perguntas ou precise de assistência adicional, sinta-se à vontade para entrar em contato.Atenciosamente,Equipe de Infraestrutura')
elif 'Desligar VM SRV_LNX_BI' in body:
instance_id = get_instance_id()
print(f'Stopping instance {instance_id}...')
ec2.stop_instances(InstanceIds=[instance_id])
send_response_email([sender_email, 'infraestrutura@dominio.com.br'], sender_email, 'Ação de Instância - Desligamento', f'A instância associada à tag "SRV_LNX_BI" foi desligada conforme sua solicitação.Remetente Original: {sender_email}Caso você tenha mais perguntas ou precise de assistência adicional, sinta-se à vontade para entrar em contato.Atenciosamente,Equipe de Infraestrutura')
else:
print('Remetente não encontrado no email.')
return {'statusCode': 200, 'body': json.dumps('Request was successful')}
def send_response_email(to_addresses, from_address, subject, body):
response = ses.send_email(
Source='infraestrutura@dominio.com.br',
Destination={
'ToAddresses': to_addresses
},
Message={
'Subject': {
'Data': subject
},
'Body': {
'Text': {
'Data': body
}
}
}
)
return response
def get_instance_id():
################### Lógica para obter o ID da instância associada à tag "SRV_LNX_BI"
response = ec2.describe_instances(
Filters=[
{
'Name': 'tag:Name',
'Values': ['SRV_LNX_BI']
}
]
)
for reservation in response['Reservations']:
for instance in reservation['Instances']:
return instance['InstanceId']
Observações:
- Adicionar uma trigger para o bucket configurado no SES, você consegue fazer isso dentro do painel da lambda, em "add trigger”, escolher trigger para o s3 e “All object create events”, nada mais a alterar e ok.
- Tempo de execução da lambda, 3 minutos.
- Não esquecer de adicionar as permissões na lambda, para ec2, cloudwatch, ses e s3.
- Alterar no código a tag name da Instancia desejada.
- Add o email que vai receber uma copia das ações ex.: infraestrutura@dominio.com.br. (Verifica-los no SES)
- Crie um workmail com um email usando o domínio delegado que foi verificado no SES. (Ex do código: aws@sandbox.dominio.com.br) ele que irá receber as solicitações por email.
- Adicionar os emails autorizados no código lambda. (Toda a descrição do código no final da pagina)
- Apenas os emails que foram verificados no SES e adicionados à Lambda receberão e terão a automação funcionando.
- Faça o teste.
Resultado:
Explicação:
O código é um Lambda Function que é executado quando um novo arquivo é enviado para um bucket do S3. O arquivo é um email com uma solicitação para ligar ou desligar uma instância EC2.
O código funciona da seguinte forma:
- O Lambda Function recebe o evento do S3 como entrada.
- O código verifica se o evento é do S3.
- O código obtém o nome do bucket e do arquivo do evento.
- O código baixa o arquivo do S3 para um arquivo temporário.
- O código lê o conteúdo do email.
- O código encontra o remetente do email.
- O código verifica se o remetente é autorizado.
- Se o remetente for autorizado, o código executa a ação solicitada.
- O código envia um email de notificação ao remetente.
- O código retorna um código de status HTTP 200.
Aqui estão alguns detalhes adicionais sobre o código:
- A função
get_instance_id()
é usada para obter o ID da instância associada à tag "SRV_LNX_BI". - A função
send_response_email()
é usada para enviar um email de notificação ao remetente.
A mensagem que o usuário envia no email está contida na variável email_content
. Essa variável é definida na linha abaixo:
with open('/tmp/email', 'r', encoding='utf-8') as f:
email_content = f.read()
O código procura os remetentes autorizados na lista authorized_senders
. Essa lista está definida na linha abaixo:
authorized_senders = [
'railander.marques@dominio.com.br',
'email-teste@dominio.edu.br'
]
Dica:
- Não esquecer de configurar um ciclo de vida no S3, para os emails recebidos e tambem não esquecer de configurar um ciclo de vida no CloudWatch logs.
- É possível também fazer a configuração com um domínio free do AWS Workmail.
Posted on January 16, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.