Descobrindo os custos da sua infraestrutura utilizando Infracost
Tags: infracost, terraform, gitops, github
Published: 2022-07-07 03:50:28

# Introdução
Um dos maiores problemas na jornada de uma empresa quando decide migrar seu ambiente e aplicações para a Cloud é: **Quanto vai custar?** 💸

Temos algumas formas de calcular e ter uma previsão dos gastos em nuvem utilizado as calculadoras dos próprios providers, porém, na minha visão a forma mais interessante de ter uma previsão de custos é utilizando o [infracost.io](https://www.infracost.io/).

# O que é o Infracost? 💰
O [Infracost](https://www.infracost.io/) é uma ferramenta utilizada junto com o seu [Terraform](https://www.terraform.io/), ou seja, você terá uma previsão de quanto a sua infraestrutura vai custar assim que executar o `terraform plan` no seu projeto.

Alguns benefícios ao utilizar o Infracost:

* **Maior conscientização sobre custos:** Os engenheiros recebem antecipadamente o custo estimado da infraestrutura, antes que quaisquer recursos sejam lançados ou alterados. Eles podem fazer escolhas econômicas enquanto mantêm a velocidade de entrega de software.
* **Visibilidade de custo para equipes de plataforma:** O impacto de custo de módulos compartilhados que são usados em muitas equipes pode ser avaliado pelas equipes de plataforma.
* **Retrabalho de infraestrutura reduzido:** O Infracost integrado a sua pipeline CI/CD fornece as informações necessárias para tomar as decisões corretas. Os orçamentos não são quebrados e menos retrabalho é necessário para consertar a infraestrutura para alinhar com os orçamentos após o lançamento.
* **Orçamentos e custos alinhados:** Se as mudanças na infraestrutura ou o lançamento de um novo produto quebrarem os orçamentos, a comunicação pode acontecer antecipadamente, não após a chegada da fatura.

# Mão na massa 💻
Neste laboratório vamos utilizar o projeto que criamos no artigo: [**Pipeline IaC com Terraform e GitHub Actions.**](https://dev.to/santospedroh/pipeline-iac-com-terraform-e-github-actions-k3p) Se você não acompanhou o post anterior faça o clone do projeto: `git clone https://github.com/santospedroh/iac-pipeline.git`.

Vamos alterar o *workflow* do **GitHub Action** para adicionar os recursos do Infracost, assim vamos poder analisar quando o `terraform plan` for executado quais os custos da nossa infraestrutura.

##Instalação e configuração do CLI 🛠
O Infracost tem uma [documentação](https://www.infracost.io/docs/) bem completa e simples com [Get started](https://www.infracost.io/docs/) super legal. No meu caso estou utilizando um MacOS e vou fazer a instalação utilizando o gerenciador de pacotes [Homebrew](https://brew.sh/index_pt-br).

```bash
➜  iac-pipeline git:(main) brew install infracost
```

Após a instalação você precisa criar uma API KEY para o Infracost gerar os custos estimados da infraestrutura, será necessário executar o comando `infracost register` e informa seu nome e e-mail para gerar a API KEY.

```bash
➜  iac-pipeline git:(main) infracost register
Please enter your name and email address to get an API key.
See our FAQ (https://www.infracost.io/docs/faq) for more details.
Name: Pedro Santos
Email: santos.pedroh@gmail.com

Thanks Pedro Santos! Your API key is: <SUA_API_KEY_VAI_APARECER_AQUI>
This was saved to /Users/santospedroh/.config/infracost/credentials.yml

Follow https://infracost.io/docs to use Infracost.
```

Agora já podemos executar o Infracost e verificar a estimativa de custos do projeto Terraform utilizando o comando `infracost breakdown --path .` para gerar a estimativa de custos direto no terminal.

```bash
➜  iac-pipeline git:(main) ✗ infracost breakdown --path .
Evaluating Terraform directory at .
  ✔ Downloading Terraform modules 
  ✔ Evaluating Terraform directory 
  ✔ Retrieving cloud prices to calculate costs 

Project: santospedroh/iac-pipeline/.

 Name                                                 Monthly Qty  Unit   Monthly Cost 
                                                                                       
 module.ec2_instance.aws_instance.this[0]                                              
 ├─ Instance usage (Linux/UNIX, on-demand, t3.micro)          730  hours         $7.59 
 └─ root_block_device                                                                  
    └─ Storage (general purpose SSD, gp2)                       8  GB            $0.80 
                                                                                       
 OVERALL TOTAL                                                                   $8.39 
──────────────────────────────────
10 cloud resources were detected:
∙ 1 was estimated, it includes usage-based costs, see https://infracost.io/usage-file
∙ 9 were free, rerun with --show-skipped to see details
```

Também podemos alterar o formato o qual as informações de custos são exibidas. Para gerar um arquivo html por exemplo, utilize o comando `infracost breakdown --path . --format html > infracost.html` um arquivo html vai ser criado. 

Mais detalhes sobre os formatos pode ser verificados na [documentação](https://www.infracost.io/docs/features/cli_commands/).

```bash
➜  iac-pipeline git:(main) infracost breakdown --path . --format html > infracost.html
Evaluating Terraform directory at .
  ✔ Downloading Terraform modules 
  ✔ Evaluating Terraform directory 
  ✔ Retrieving cloud prices to calculate costs

➜  iac-pipeline git:(main) ✗ ls -ltr *.html
-rw-r--r--  1 santospedroh  staff  16269  6 Jul 16:09 infracost.html
```

Arquivo HTML com a previsão de custos do projeto.

![Infracost html](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7un54c6jtb8a8jsypr53.png) Podemos ver que o custo da infraestrutura atual utilizando uma instância t3.micro é de $8.39

##Integração com o GitHub Action 🤖
Agora chegou o momento de integrarmos o Infracost com o nosso workflow no [GitHub Action](https://github.com/features/actions) para recebemos os valores estimados direto nas _Pull Request's_ que forem criadas. O primeiro passo para a integração é criar uma secret da sua API KEY do Infracost no repositório do projeto, assim como fizemos no [artigo anterior](https://dev.to/santospedroh/pipeline-iac-com-terraform-e-github-actions-k3p).

![Infracost secret](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lch9rs6fqhf2pojlt59h.png)
Vamos utilizar o [Infracost Actions oficial](https://github.com/marketplace/actions/infracost-actions) como referência para editar o nosso workflow [**plan.yml**](https://raw.githubusercontent.com/santospedroh/iac-pipeline/infracost/.github/workflows/plan.yml) que já temos em nosso projeto.

```yaml
name: "Plan"

on:
  pull_request:
      
env:
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

jobs:
  terraform:
    name: "Terraform"
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1
        with:
          terraform_version: 0.15.5

      - name: Terraform Init
        id: init
        run: terraform init
      
      - name: Terraform Validate
        id: validate
        run: terraform validate -no-color

       # Aqui vamos gerar o terraform plan em arquivo para passar para o infracost
      - name: Terraform Plan
        id: plan
        run: terraform plan -out tfplan.binary

      - name: Terraform show
        id: show
        run: terraform show -json tfplan.binary > plan.json
        working-directory: ${{ env.working-directory }}

      - uses: actions/github-script@v6
        if: github.event_name == 'pull_request'
        env:
          PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
            #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
            #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
            #### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
            <details><summary>Show Plan</summary>
            \`\`\`\n
            ${process.env.PLAN}
            \`\`\`
            </details>
            *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            })
      - name: Terraform Plan Status
        if: steps.plan.outcome == 'failure'
        run: exit 1

      # Chamar a action do infracost passando a nossa secret
      - name: Setup Infracost
        uses: infracost/actions/setup@v1
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}
    
      # Passar o arquivo plan.json para o infracost gerar os custos
      - name: Generate Infracost JSON
        run: infracost breakdown --path plan.json --format json --out-file /tmp/infracost.json
    
      - name: Infracost Actions
        uses: infracost/actions/comment@v1
        with:
          path: /tmp/infracost.json
          behavior: update
```
## Enviando um Pull Request para alteração da infraestrutura 📈
Agora vamos simular um aumento da capacidade computacional com 2 instâncias EC2, atualmente estamos utilizando apenas uma instância do tipo t3.micro que tem 2 vCPU e 1GB de memória RAM e vamos para 2 instâncias do tipo m5.large que tem 2 vCPU e 4GB de memória RAM.

Vamos criar uma nova branch no projeto com o nome `infracost` para fazer as alterações e enviar o _Pull Request_.

```bash
➜  iac-pipeline git:(main) git checkout -b infracost
Switched to a new branch 'infracost'
➜  iac-pipeline git:(infracost)
```

Atualizar o arquivo [**main.ft**](https://raw.githubusercontent.com/santospedroh/iac-pipeline/infracost/main.tf) com as novas instâncias do tipo m5.large

```hcl
module "ec2_instance" {
  source = "terraform-aws-modules/ec2-instance/aws"

  for_each = toset(["server-01", "server-02"]) # Duas instâncias ec2

  name                   = "snake-game-${each.key}"
  ami                    = "ami-0cff7528ff583bf9a"
  instance_type          = "m5.large" # Tipo das instâncias m5.large
  vpc_security_group_ids = [aws_security_group.game_snake_sg.id]
  subnet_id              = module.vpc.public_subnets[0]
  user_data              = file("userdata.sh")
  tags = {
    Name = "snake-game-ec2"
    Terraform = "true"
    Environment = "prod"
    Team = "gamer-development"
    Application = "snake-game"
    Language = "javascript"
  }
}
```

Vamos fazer o *commit* e o *push* e criar o [_Pull Request_](https://github.com/santospedroh/iac-pipeline/pull/8) no _[GitHub](https://github.com/santospedroh/iac-pipeline/tree/infracost)_

```bash
➜  iac-pipeline git:(infracost)  git add .
➜  iac-pipeline git:(infracost)  git commit -m "Infra com 2 instancias do tipo m5.large"
[infracost 1d900e7] Infra com 2 instancias do tipo m5.large
 4 files changed, 28 insertions(+), 10 deletions(-)
 create mode 100644 .terraform.lock.hcl
➜  iac-pipeline git:(infracost) git push origin infracost                              
Enumerating objects: 14, done.
Counting objects: 100% (14/14), done.
Delta compression using up to 12 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (8/8), 1.59 KiB | 1.59 MiB/s, done.
Total 8 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
To github.com:santospedroh/iac-pipeline.git
   8e2c8e8..1d900e7  infracost -> infracost
```

Após a execução do _workflow_ plan podemos ver nos comentários do _Pull Request_ a estimativa de custo do Infracost.

![Infracost PR](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jqcehjydq7rps70ezj8y.png) O valor da nova infra ficou estimado em $142

# Conclusão
Utilizando o Infracost dificilmente você vai ter surpresas ao receber a fatura do seu _cloud provider_, podemos saber a estimativa de custo de cada projeto [Terraform](https://www.terraform.io/), podemos avaliar e discutir os valores em cada _Pull Request_ enviada podendo aprovar ou não o _Merge_ para a branch principal do projeto antes de qualquer mudança ser aplicada. 

Todos esses benefícios ajudam bastante no dia a dia de um time que utiliza as práticas DevOps e FinOps.

Espero que tenha gostado desse hands-on, aproveitem bem o [Infracost](https://www.infracost.io/), até a próxima! ✌🏼