Meu primeiro projeto com AWS Cloudformation - Automatizando a criação da arquitetura
Tags: aws, node, cloud, cloudformation
Published: 2022-06-26 17:53:47

[0. Objetivo do projeto](#objetivo-do-projeto)
[1. Desenho da arquitetura](#desenho-da-arquitetura)
[2. Código e explicação](#código-e-explicação)
[3. Conclusão](#conclusão)


### Objetivo do projeto
A motivação por trás desse projeto foi me capacitar em AWS, principalmente nos serviços Cloudformation, S3, Lambda, VPC, RDS e IG e nos conhecimentos de políticas de acesso para crescer profissionalmente. O objetivo do projeto é ser um registrador de arquivos excluídos, sempre que um arquivo for deletado manualmente, ou por meio de políticas de um bucket, o S3 deverá alertar um Lambda para realizar um registro no RDS (MySQL). Pode ser uma boa solução para quem pretende sincronizar arquivos de um bucket com seu conteúdo em um banco RDS.
Todo o projeto pode ser encontrado nesse [repositorio.](https://github.com/walteralleyz/myf_proj_cloudformation)

### Desenho da arquitetura

![Arquitetura Simples](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qp3ktfigk0c06oht5zl.png)
A arquitetura é autoexplicativa: Vamos precisar de um S3 para enviar notificações à um Lambda que vai se comunicar com um RDS. Mas nada é tão simples, já que a comunicação entre esses serviços depende de rede e permissões. Por isso vamos precisar de uma VPC, duas subnets (que serão associadas ao RDS e ao Lambda) e uma Role com permissão do evento do bucket para o Lambda.

### Código e explicação
Inicialmente vamos criar uma VPC para proteger o acesso ao nosso RDS:

```
SimpleVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
        - Key: stack
          Value: des
```
Começamos definindo o tipo de recurso com a palavra-chave "Type" e associamos ao valor "AWS::EC2::VPC", pois a VPC se encontra dentro do grupo EC2. Também precisamos definir o CidrBlock que é o conjunto de IP disponível dentro da VPC.
Agora precisamos criar duas subnets, associadas a nossa VPC e um grupo de subnets que será usado pelo RDS.

```
SimpleSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: SimpleVPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: "us-east-1a"
      Tags:
        - Key: stack
          Value: des

  SimpleSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId:
        Ref: SimpleVPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: "us-east-1b"
      Tags:
        - Key: stack
          Value: des

  SimpleDBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      SubnetIds:
        - Ref: SimpleSubnet1
        - Ref: SimpleSubnet2
      DBSubnetGroupDescription: Subnet para o RDS
```
Podemos criar o nosso RDS com uma máquina micro, que será suficiente para o nosso projeto pessoal, 4 GB de storage (mais do que o suficiente), com uma engine MySQL, e utilizando do nosso grupo de subnets.

```
SQLDemoDbInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      DBName: !Ref pDbName
      MasterUsername: !Ref pDbUser
      MasterUserPassword: !Ref pDbPass
      Engine: MySQL
      DBInstanceClass: db.t2.micro
      StorageType: gp2
      PubliclyAccessible: False
      DBSubnetGroupName:
        Ref: SimpleDBSubnetGroup
      AllocatedStorage: "4"
      DBInstanceIdentifier: !Join ["-", [ "SQLDemoDbInstance", !Ref "AWS::Region" ]]
      AvailabilityZone: !Select [1, !GetAZs ""]
```

Agora, vamos criar nosso bucket com uma política de notificação. Nesse caso, eu quero que meu bucket notifique o Lambda sempre que um objeto for removido, independente de seu estado anterior (versionado, taggeado, etc).
É importante definir que o bucket terá uma dependência com a criação do Lambda e apontar a propriedade "Function" para o Arn do nosso Lambda, usando a função !GetAtt.
Perceba que eu estou definindo um filtro no S3 para notificar somente arquivos terminados em ".txt".

```
MyBucket:
    Type: AWS::S3::Bucket
    DependsOn: LambdaS3Permissao
    Properties:
      BucketName: !Ref BucketName
      NotificationConfiguration:
        LambdaConfigurations:
          - Event: s3:ObjectRemoved:*
            Function: !GetAtt LambdaS3Processamento.Arn
            Filter:
              S3Key:
                Rules:
                  - Name: suffix
                    Value: .txt
```
Por fim, vamos criar a Lambda com um código simples que pode ser atualizado depois no console. Você também poderia apontar o seu código para um arquivo zip dentro de um bucket S3, mas vamos deixar mais simples para o nosso caso.
O nosso Lambda terá a simples tarefa de receber o evento de notificação, extrair o nome do arquivo e salvar na base RDS com um timestamp associado. É importante lembrar de utilizar as mesmas subnets e o security group da nossa VPC.

```
LambdaS3Processamento:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.execute
      Role: !GetAtt LambdaS3Role.Arn
      Runtime: nodejs12.x
      MemorySize: 128
      Timeout: 45
      VpcConfig:
        SecurityGroupIds:
          - sg-0c094b8e5f85ae4b3
        SubnetIds:
          - !Ref SimpleSubnet1
          - !Ref SimpleSubnet2
      Code:
        ZipFile: >
          exports.execute = (event, context) => {
            console.log(event);
          }
```
Você pode, facilmente, criar a sua infraestrutura por meio do console AWS, no serviço cloudformation, importando o template que a gente acabou de criar.

Depois de criado com sucesso, precisamos alterar o código da nossa Lambda com o nosso projeto do github. Para isso, precisamos baixar o projeto e compactar no formato zip. No serviço de Lambda do console AWS, escolha upload e o formato zip. Depois de atualizar o código, podemos testar enviando um arquivo ao nosso bucket e apagando logo em seguida.

### Conclusão

O AWS Cloudformation nos permitiu construir uma estrutura simples, de fácil deploy utilizando apenas um arquivo. Sempre que for necessário replicar a mesma estrutura, podemos usar o mesmo arquivo alterando poucos parâmetros.