Eventos orientados com Amazon EventBridge
Tags: aws, eventdriven, python, terraform
Published: 2023-02-26 23:01:23

## Sobre arquiteturas orientadas a eventos

As arquiteturas orientadas a eventos (Event-Driven) são caracterizadas por serviços que se comunicam de forma assíncrona e desacoplado através de eventos.

Os serviços transmitirão eventos (Produtores) que serão consumidos e reagidos por outros serviços (Consumidores).

Uma característica que marca uma Arquitetura orientadas a eventos é que: Produtores e Consumidores estão completamente dissociados, um produtor não deve saber ou se importar com quem está consumindo seus eventos.

![fluxo de uma arquitetura orientada a eventos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8zblbucxrr1dtmtdyko.png)
_Descrição da imagem: Representação o fluxo de uma arquitetura orientada a eventos, do lado esquerdo três retângulos representam os produtores, no meio um retângulo maior representa o roteador e a direita doisretângulos representam os consumidores_


## O que seria o Amazon EventBridge?

> O [Amazon EventBridge](https://docs.aws.amazon.com/pt_br/eventbridge/latest/userguide/eb-what-is.html) é um serviço que oferece acesso em tempo real a alterações de dados em serviços da AWS, em suas aplicações e em aplicações de software como serviço (SaaS) sem precisar escrever código. Para começar, você pode escolher uma origem de eventos no console do EventBridge. Em seguida, pode selecionar um destino entre os serviços da AWS, incluindo o AWS Lambda, o Amazon Simple Notification Service (SNS) e o Amazon Kinesis Data Firehose. O EventBridge entregará automaticamente os eventos quase em tempo real.

Em resumo você pode receber, filtrar, transformar, rotear (dos Produtores) e entregar esses eventos a Consumidores.

## Trabalhando com o Amazon EventBridge

Para exemplificar o uso do Amazon EventBridge vamos utilizar um caso passei a pouco tempo:

* Toda vez que uma instancia de banco de dados, do [AWS RDS](https://docs.aws.amazon.com/pt_br/AmazonRDS/latest/UserGuide/Welcome.html), fosse criada queríamos que uma [AWS Lambda](https://docs.aws.amazon.com/pt_br/lambda/latest/dg/welcome.html) foi executada para adicionar uma determinada tag a instancia.

Utilizaremos então a seguinte arquitetura:

![arquitetura de serviços utilizados](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zni56gp7j5zta4ymrppq.png)
_Descrição da imagem: Diagrama representando os serviços utilizados, numerados de um a três_

1. Como **Produtor** de eventos utilizaremos o _CloudTrail_. 
2. Como **Roteador**, que irá tratar os eventos, utilizaremos o _EventBridge_
3. Como **Consumidor** desses eventos utilizaremos a _AWS Lambda_

_O repositório do Github [rafaelonline/eventbridge-lambda](https://github.com/rafaelonline/eventbridge-lambda) possui um exemplo de configuração infraestrutura usando Terraform e o script Python utilizado._

### Passo 1

_Pre-Requisito: É preciso que a conta possua um Trail configurado no CloudTrail, saiba como configurar em [Criar uma trilha para a sua conta da AWS](https://docs.aws.amazon.com/pt_br/awscloudtrail/latest/userguide/cloudtrail-create-a-trail-using-the-console-first-time.html)_

O [CloudTrail](https://docs.aws.amazon.com/pt_br/awscloudtrail/latest/userguide/cloudtrail-user-guide.html) gera eventos no formato JSON e possui integração nativa com o EventBridge. Ele será nosso Produtor de eventos.

> O CloudTrail proporciona visibilidade sobre as atividades de usuários por meio do registro das ações executadas na sua conta. O CloudTrail registra informações importantes sobre cada ação, como quem fez a solicitação, quais serviços foram usados, quais ações foram executadas, quais os parâmetros da ação e quais elementos da resposta foram retornados pelo serviço da AWS

### Passo 2

Agora devemos configurar uma regra no [EventBridge Rules](https://docs.aws.amazon.com/pt_br/eventbridge/latest/userguide/eb-get-started.html) para monitorar os eventos desejados. 

No nosso caso o [padrão de evento(event pattern)](https://docs.aws.amazon.com/pt_br/eventbridge/latest/userguide/eb-event-patterns.html) será a própria AWS tendo como origem o Cloudtrail e nele os eventos relacionados ao `rds.amazonaws.com` com nome `CreateDBInstance`.

Exemplo no console da AWS:

![Exemplo no console da AWS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/laq42cmw1rwuuppr58cv.png)
_Descrição da imagem: Recorte contendo exemplo de configuração da regra no console da AWS_

Exemplo de código Terraform para criação da regra:

```
###### EVENTBRIDGE RULE - CREATED RDS INSTANCE ######
resource "aws_cloudwatch_event_rule" "rds_event_rule" {
  name          = "rule-rds-created"
  description   = "Triggers Lambda when new RDS instance are created"
  is_enabled    = true
  event_pattern = <<EOF
    {
    "source": ["aws.rds"],
    "detail-type": ["AWS API Call via CloudTrail"],
    "detail": {
        "eventSource" : ["rds.amazonaws.com"],
        "eventName": ["CreateDBInstance"]
    }
    }
  EOF
}
```

### Passo 3

Agora precisamos encaminhar nosso evento para o Consumidor (Alvo), que será uma Lambda.

> Um [Alvo](https://docs.aws.amazon.com/pt_br/eventbridge/latest/userguide/eb-targets.html) é um recurso ou endpoint que EventBridge envia um evento, quando esse evento corresponde ao padrão definido em uma regra. Essa regra processa os dados do evento e envia as informações pertinentes ao Alvo

Podemos encaminhar esse mesmo evento para até cinco Alvos. 
Por exemplo (não abordado nesse tutorial), no caso que tive nós enviamos um e-mail através do AWS SNS informando que uma instancia RDS foi criada, além de executar a Lambda que adiciona a tag.

Inclusive, podemos enviar o evento originalmente como foi gerado, enviar somente uma parte do evento, fazer alguma edição no evento antes de enviar e enviar um json de evento fixo.

Exemplo no console da AWS:

![Exemplo no console da AWS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/50ipnofirkiqwik4fz35.png)
_Descrição da imagem: Recorte contendo exemplo de configuração do alvo no console da AWS_

Exemplo de código Terraform para criação do Target:
```
###### EVENTBRIDGE TARGET - CREATED RDS INSTANCE ######
resource "aws_cloudwatch_event_target" "lambda_rule_rds" {
  depends_on = [aws_lambda_function.autotag]
  rule       = aws_cloudwatch_event_rule.rds_event_rule.name
  target_id  = "SendToLambda"
  arn        = aws_lambda_function.autotag.arn
}
```

### Passo 4

Chegamos ao passo final que é termos o nosso Consumidor, para  nosso cenário criamos um script Python e uma Lambda para adicionar a Tag a instancia de RDS criada.

Script Python que cria a Tag
```
"""Add tags on RDS and Aurora."""
import logging
import os
import boto3
from botocore.exceptions import ClientError

# Config Logging.
log = logging.getLogger()
log.setLevel(logging.INFO)

def lambda_handler(event, context):
    """Add tags on RDS and Aurora"""

    # Define the tags to add to the RDS instance
    tag_key = os.environ.get('TAG_KEY')
    tag_value = os.environ.get('TAG_VALUE')
    tags = [
        {
            'Key': tag_key,
            'Value': tag_value
        }
    ]

    # Connect to RDS service
    rds = boto3.client('rds')
    event_name = event.get("detail").get("eventName")

    if event_name == "CreateDBCluster":
        aurora_arn = event.get("detail").get("responseElements").get("dBClusterArn")
    # Add tags to the Regional Cluster
        try:
            rds.add_tags_to_resource(
                ResourceName=aurora_arn,
                Tags=tags
            )
            log.info('Tag adicionda com sucesso ao Cluster Aurora: %s', aurora_arn)
        except ClientError as error:
            log.exception(error)

    else:
    # Add tags to the RDS instance
        rds_arn = event.get("detail").get("responseElements").get("dBInstanceArn")
        try:
            rds.add_tags_to_resource(
                ResourceName=rds_arn,
                Tags=tags
            )
            log.info('Tag adicionda com sucesso ao RDS: %s', rds_arn)
        except ClientError as error:
            log.exception(error)        
```

Exemplo de código Terraform para criação Lambda:
 
```
###### GENERATE PACKAGE LAMBDA ###### 
data "archive_file" "lambda_autotag" {
  type        = "zip"
  source_dir  = "${path.module}/code/src"
  output_path = "${path.module}/code/lambda_package.zip"
}

###### LAMBDA FUNCTION ######
resource "aws_lambda_function" "autotag" {
  function_name    = var.autotag_function_name
  role             = aws_iam_role.lambda_exec_role.arn
  filename         = data.archive_file.lambda_autotag.output_path
  source_code_hash = data.archive_file.lambda_autotag.output_base64sha256
  description      = var.autotag_description
  publish          = true

  runtime       = "python3.8"
  handler       = "main.lambda_handler"
  timeout       = 300
  memory_size   = 128
  architectures = ["arm64"]

  environment {
    variables = {
      TAG_KEY   = var.lambda_tag_key
      TAG_VALUE = var.lambda_tag_value
    }
  }
}
```
Obs.: É necessário ter uma IAM Role que permita adição de tags na instância RDS

## Conclusão

Através do Amazon EventBridge conseguimos que um evento de origem aciona-se outros eventos e executa-se uma automação. No nosso exemplo adicionamos uma tag, mas poderia ser qualquer outro processamento ou ação. 

## Saiba Mais

[Event-Driven Architectures vs. Event-Based Compute in Serverless Applications](https://www.alexdebrie.com/posts/event-driven-vs-event-based/)
[O que é a Amazon EventBridge?](https://docs.aws.amazon.com/pt_br/eventbridge/latest/userguide/eb-what-is.html)