Continuous Deployment para websites com AWS Amplify e Serverless Framework
Tags: ptbr, serverless, aws, awsamplify
Published: 2020-09-10 00:09:28

É mais fácil do que nunca criar um site e apresentá-lo ao mundo, especialmente para pessoas não técnicas com a ascensão dos construtores de sites online. Para sites mais avançados, engenheiros de software e desenvolvedores da web também foram mimados com ferramentas recentes, como Zeit, Netlify, GitHub Pages e o mais recente AWS Amplify Console.

[O AWS Amplify Console](https://aws.amazon.com/amplify/console/) é um serviço da AWS lançado em novembro de 2018 que fornece “Hospedagem para aplicativos da web fullstack serverless com implantação contínua (continuous deployment)”. Apesar do que o nome indica, o AWS Amplify “Console” inclui suporte para CloudFormation, permitindo que você configure os recursos usando Infraestrutura como Código (IAC).

Antes do Amplify Console, tive que escrever mais de 100 linhas de um complexo CloudFormation para configurar um site estático com [S3](https://aws.amazon.com/s3/) , [CloudFront](https://aws.amazon.com/cloudfront/) e [Route53](https://aws.amazon.com/route53/) . Não há dúvida de que, nos bastidores, esses são os blocos de construção fundamentais que o Amplify Console está usando, mas ele não só fornece uma abstração simples sobre esses serviços da AWS, como inclui muitos outros excelentes recursos, como implantação contínua, previews de pull request, testes com Cypress e mais.

Vamos dar uma olhada no que é necessário para criar um site básico com o AWS Amplify usando o Serverless Framework:

> Embora este exemplo use o Serverless Framework, na verdade ele está usando apenas recursos nativos do CloudFormation. Eu uso o Serverless Framework aqui principalmente porque prefiro definir meus parâmetros / variáveis ​​usando sua seção `custom` ao invés dos parâmetros mais detalhados do CloudFormation. Fique atento para o uso de um plug-in do Serverless Framework que simplifica isso ainda mais.

```yaml
service: my-website
provider:
  name: aws
  region: us-east-1
  variableSyntax: "\\${{([ ~:a-zA-Z0-9._@\\'\",\\-\\/\\(\\)]+?)}}"

plugins:
  - serverless-dotenv-plugin

custom:
  # 👇 Modifique esses valores
  repository: https://github.com/USER/REPO
  domainName: example.com
  domainEnabled: true
  # 👆 Mude para `false` se você quiser usar esse template sem um domínio customizado
  branch: master
  amplifyStage: PRODUCTION
  accessToken: ${{env:GITHUB_PERSONAL_ACCESS_TOKEN}}

resources:
  Conditions:
    UseDomainName:
      !Equals
        - ${{self:custom.domainEnabled}}
        - true
  
  Resources:
    AmplifyApp:
      Type: "AWS::Amplify::App"
      Properties:
        Name: ${{self:service}}
        Repository: ${{self:custom.repository}}
        AccessToken: 
        # 👇 Você provavelmente terá que modificar o BuildSpec, por exemplo o `baseDirectory`, que é conhecido também como `dist` ou `build`
        BuildSpec: |-
          version: 0.1
          frontend:
            phases:
              preBuild:
                commands:
                  - npm ci
              build:
                commands:
                  - npm run build
            artifacts:
              baseDirectory: public
              files:
                - '**/*'
            cache:
              paths:
                - node_modules/**/*

    AmplifyBranch:
      Type: AWS::Amplify::Branch
      Properties:
        AppId: !GetAtt AmplifyApp.AppId
        BranchName: ${{self:custom.branch}}
        EnableAutoBuild: true
        Stage: ${{self:custom.amplifyStage}}

    AmplifyDomain:
      Type: AWS::Amplify::Domain
      Condition: UseDomainName
      Properties:
        DomainName: ${{self:custom.domainName}}
        AppId: !GetAtt AmplifyApp.AppId
        SubDomainSettings:
          - Prefix: ${{self:custom.branch}}
            BranchName: !GetAtt AmplifyBranch.BranchName

  Outputs:
    AmplifyAppId:
      Value: !Ref AmplifyApp

    DefaultDomain:
      Value: !Sub ${{self:custom.branch}}.${AmplifyApp.DefaultDomain}

    BranchUrl:
      Condition: UseDomainName
      Value: !Sub ${AmplifyBranch.BranchName}.${AmplifyDomain.DomainName}
```

## Executando nosso Exemplo

Antes de deployar essa aplicação, você precisará criar um token de acesso pessoal GitHub e armazená-lo em um arquivo `.env`, como `GITHUB_PERSONAL_ACCESS_TOKEN=seu-token-aqui`.

> **Importante:** Seu token de acesso será exibido em texto simples em logs e CloudFormation. Verifique a seção **🔒 Segredos e Segurança** abaixo para saber a maneira correta de fazer isso.

Depois fazer o deploy desse `.yaml`, um novo aplicativo Amplify é criado em sua conta, que é configurado para construir e implantar automaticamente quaisquer mudanças em seu branch `master`. O Amplify não iniciará a primeira construção até que você coloque modifique seu branch ou acione manualmente uma construção. Você pode fazer isso simplesmente fazendo login no console da AWS e navegando até seu aplicativo no AWS Amplify ou executando `aws amplify start-deployment --app-id=AMPLIFY_APP_ID --branch-name=master`.

Se estiver usando um domínio personalizado que está registrado no Route53, você deve estar pronto para começar! O AWS Amplify Console cuida de tudo para você. Caso contrário, você precisará executar algumas etapas adicionais de DNS para aprovar o certificado SSL e apontar seu domínio para o endpoint criado. O console do Amplify Console (vê por que esse nome é difícil?) fornece ótimas instruções sobre como fazer isso. Lembre-se de que o DNS leva tempo para se propagar, então não espere que funcione imediatamente. Enquanto isso, você pode usar o domínio fornecido pelo Amplify que definimos no `Outputs.DefaultDomain`do modelo CloudFormation acima.

## 🔒 Segredos e segurança

Um leitor atento pode reconhecer o uso do `${{env:GITHUB_PERSONAL_ACCESS_TOKEN}}` no template yaml. É importante notar que **esta não é uma maneira segura de usar segredos em templates do Serverless Framework** pois eles aparecerão nos logs e no template gerado do CloudFormation como texto simples.

A maneira segura é usar o [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) . Basta armazenar seu segredo por meio do console do AWS Secrets Manager ou executando:

```shell
aws secretsmanager \
	create-secret --name AmplifyGithub \
	--secret-string '[{"accessToken":"YOUR_ACCESS_TOKEN"}]' \
	--profile=SEU_CLI_PROFILE \
	--region=SUA_REGION
```

E substitua `AccessToken`no modelo Serverless Framework por:

```yaml
AccessToken: '{{resolve:secretsmanager:AmplifyGithub:SecretString:accessToken}}'
```

## Implantação Contínua

Embora o AWS Amplify faça um ótimo trabalho ao configurar a implantação contínua básica, ele deixa muito a desejar para aplicativos mais complexos. Não há pipelines de vários estágios, bloqueadores baseados em alarme/tempo e sem reversões automáticas (ou mesmo reversão manual para esse assunto). Eu apostaria que a integração com [AWS CodePipeline](https://aws.amazon.com/codepipeline/) já está no roteiro.

Por enquanto, você mesmo deve ser capaz de configurar o AWS CodePipeline (ou o pipeline de CI / CD de sua escolha), embora eu não tenha tentado isso ainda - talvez um artigo para outro dia. Você pode acionar builds manualmente com o [start-job](https://docs.aws.amazon.com/cli/latest/reference/amplify/start-job.html) se seu ambiente de build já tiver o AWS CLI configurado. Como alternativa, o AWS Amplify Console permite que você crie Webhooks para acionar builds, embora atualmente não haja suporte para CloudFormation documentado. Lembre-se de desligar o `EnableAutoBuild` seu `Amplify::Branch` para desabilitar a Implementação Contínua integrada.

Rollbacks ainda podem ser um desafio, mas talvez você possa usar o `start-job --commit-id` parâmetro. Se você tentar isso (ou qualquer outro método de CD avançado com o Console do Amplify), me conta como foi!

## Simplificando ainda mais - um plugin de Serverless Framework

[A Wizeline](https://wizeline.com/) desenvolveu um [plug-in de Serverless Framework](https://github.com/wizeline/serverless-amplify-plugin/) que reduz o exemplo acima a:

```yaml
plugins:
  - @wizeline/serverless-amplify-plugin

custom:
  amplify:
    repository: https://github.com/USER/REPO
```

Muito bacana né?

# Créditos

- [Continuous Deployment for websites with AWS Amplify and Serverless Framework](https://www.halfstack.software/continuous-deployment-for-websites-with-aws-amplify-and-serverless-framework/), escrito originalmente por [Brett Andrews](https://twitter.com/AWSbrett).