IaC além do Terraform – testando infraestrutura como código

작성자

카테고리:

← 피드로
DEV Community · Rafael Dutra · 2026-09-05 개발(SW)

1. Código de infraestrutura também quebra

Nos dois artigos anteriores desta série, vimos o OpenTofu como alternativa para provisionar infraestrutura e o Ansible para configurá-la depois de criada. Mas há uma pergunta que fica no ar em qualquer um desses fluxos: como saber, antes de rodar apply em produção, que um módulo Terraform não vai abrir uma porta que não deveria, destruir um recurso por engano, ou simplesmente ter um erro de sintaxe? Testar infraestrutura como código é tão importante quanto testar qualquer outro software — só que, diferente de uma função pura, os “efeitos colaterais” de um teste malfeito aqui podem ser uma conta de nuvem inesperada ou um serviço em produção fora do ar.

Este artigo fecha a série cobrindo três camadas complementares de teste: análise estática com tflint, verificação de segurança e compliance com checkov, e testes de integração de verdade com Terratest.

2. As camadas de teste em IaC

Vale pensar nessas ferramentas como camadas que rodam em momentos diferentes do ciclo de vida do código, da mais rápida/barata para a mais lenta/cara:

  1. Lint e análise estática (tflint): roda em segundos, sem precisar de credenciais de nuvem nem de rodar terraform plan. Pega erros de sintaxe, más práticas e problemas específicos de cada provider.
  2. Análise de segurança e compliance (checkov): também estática, mas focada em identificar configurações inseguras (bucket público, criptografia desabilitada, security group aberto para 0.0.0.0/0) comparando o código contra um catálogo de políticas.
  3. Testes de integração (Terratest): a camada mais próxima da realidade — de fato roda terraform apply num ambiente isolado, valida o resultado, e depois roda terraform destroy. Mais lento e mais caro (usa recursos reais de nuvem), mas é o único jeito de garantir que o módulo realmente funciona de ponta a ponta.

Um pipeline de CI/CD maduro roda as três, nessa ordem, falhando rápido nas camadas mais baratas antes de chegar nas mais caras.

3. tflint na prática

O tflint foca em problemas que o terraform validate não pega, porque validate só garante que a sintaxe HCL e os tipos estão corretos — não que o uso de um recurso específico faz sentido para aquele provider.

# Instalação (Linux/macOS via script oficial)
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

# Rodar no diretório do módulo
tflint --init   # baixa o plugin do provider (ex.: AWS)
tflint

Enter fullscreen mode Exit fullscreen mode

Exemplo de configuração habilitando o plugin da AWS, que traz regras específicas (tipos de instância inválidos, uso de AMIs deprecated, etc.):

# .tflint.hcl
plugin "aws" {
  enabled = true
  version = "0.31.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

rule "terraform_deprecated_interpolation" {
  enabled = true
}

rule "terraform_unused_declarations" {
  enabled = true
}

Enter fullscreen mode Exit fullscreen mode

Com isso, tflint sinaliza coisas como variáveis declaradas e nunca usadas, ou um instance_type que não existe na AWS — erros que só apareceriam em produção, na hora do apply, sem essa checagem prévia.

4. checkov na prática

O checkov (da Bridgecrew/Prisma Cloud) analisa o código Terraform contra centenas de políticas de segurança e compliance prontas (CIS Benchmarks, PCI-DSS, HIPAA, entre outras), sem precisar aplicar nada:

pip install checkov

checkov -d ./terraform

Enter fullscreen mode Exit fullscreen mode

Exemplo de um recurso com problema de segurança óbvio:

resource "aws_s3_bucket" "data" {
  bucket = "minha-empresa-dados"
}

resource "aws_s3_bucket_public_access_block" "data" {
  bucket                  = aws_s3_bucket.data.id
  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

Enter fullscreen mode Exit fullscreen mode

Rodar checkov contra esse arquivo reporta uma falha correspondente à política CKV_AWS_53 (“Ensure S3 bucket has block public ACLs enabled”), com o número da linha e uma explicação do risco. É possível suprimir uma regra pontualmente quando a exceção é intencional e documentada:

resource "aws_s3_bucket_public_access_block" "data" {
  # checkov:skip=CKV_AWS_53:bucket de assets públicos do site, intencional
  bucket                  = aws_s3_bucket.data.id
  block_public_acls       = false
  block_public_policy     = false
  ignore_public_acls      = false
  restrict_public_buckets = false
}

Enter fullscreen mode Exit fullscreen mode

Isso documenta a exceção diretamente no código, em vez de silenciar o alerta de forma invisível em algum lugar do pipeline.

5. Terratest na prática

O Terratest é uma biblioteca Go (mantida pela Gruntwork) para escrever testes de integração reais contra código Terraform. O padrão típico de um teste é: apply → validar → destroy, sempre em um ambiente descartável.

// test/webserver_test.go
package test

import (
    "testing"
    "time"

    "github.com/gruntwork-io/terratest/modules/http-helper"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestWebServerModule(t *testing.T) {
    terraformOptions := &terraform.Options{
        TerraformDir: "../examples/webserver",
        Vars: map[string]interface{}{
            "environment": "test",
        },
    }

    // Garante que 'terraform destroy' roda no final,
    // mesmo se o teste falhar no meio do caminho.
    defer terraform.Destroy(t, terraformOptions)

    terraform.InitAndApply(t, terraformOptions)

    publicIP := terraform.Output(t, terraformOptions, "public_ip")
    url := "http://" + publicIP

    http_helper.HttpGetWithRetry(
        t,
        url,
        nil,
        200,
        "Welcome",
        30,          // tentativas
        5*time.Second, // intervalo entre tentativas
    )

    assert.NotEmpty(t, publicIP, "o output public_ip não deveria estar vazio")
}

Enter fullscreen mode Exit fullscreen mode

Rodando:

cd test
go test -v -timeout 30m

Enter fullscreen mode Exit fullscreen mode

O defer terraform.Destroy(...) logo após criar as opções é o detalhe mais importante desse padrão: garante que o destroy roda mesmo se uma das validações (HttpGetWithRetry, assert) falhar no meio do teste, evitando deixar recursos reais de nuvem órfãos e gerando custo desnecessário.

6. Juntando tudo num pipeline de CI/CD

# .github/workflows/terraform-test.yml
name: Terraform Test Pipeline

on:
  pull_request:
    paths:
      - "terraform/**"

jobs:
  static-checks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: tflint
        uses: terraform-linters/setup-tflint@v4
      - run: tflint --init && tflint --chdir=terraform

      - name: checkov
        uses: bridgecrewio/checkov-action@master
        with:
          directory: terraform

  integration-tests:
    needs: static-checks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: "1.22"
      - name: Terratest
        working-directory: test
        run: go test -v -timeout 30m

Enter fullscreen mode Exit fullscreen mode

O job integration-tests só roda depois que static-checks passa — falhar rápido nas checagens estáticas (segundos) evita gastar tempo e dinheiro rodando apply/destroy reais (minutos) para um código que já tinha um problema óbvio de lint ou segurança.

7. Evitando drift em produção

Testes automatizados evitam que código com problema chegue à produção, mas não evitam drift — divergência entre o que o state do Terraform diz que existe e o que de fato existe na nuvem (alguém mudou algo manual pelo console, por exemplo). Para isso, a prática recomendada é rodar terraform plan (ou tofu plan) periodicamente contra o ambiente de produção, fora do fluxo de deploy, e alertar se houver diferença:

# Rodado por um cron/job agendado, não por um deploy
terraform plan -detailed-exitcode
# exit code 0 = sem mudanças, 1 = erro, 2 = há diferenças (drift)

Enter fullscreen mode Exit fullscreen mode

O -detailed-exitcode é o que torna isso automatizável: um pipeline pode checar o código de saída e abrir um alerta apenas quando o valor for 2, sem precisar fazer parsing do output textual do plano.

8. Conclusão da série

Fechamos esta série de três artigos indo além do Terraform puro: o OpenTofu como alternativa de provisionamento nascida de uma crise de licenciamento, o Ansible cobrindo a configuração do que já foi provisionado, e agora as três camadas de teste — tflint, checkov e Terratest — que dão confiança de que o código de infraestrutura faz exatamente o que deveria antes de chegar em produção. Nenhuma dessas ferramentas substitui o Terraform; todas ampliam o que é possível fazer em volta dele, e é essa combinação — provisionar, configurar e testar — que sustenta um pipeline de IaC realmente confiável.

Imagem de capa: Logo oficial do Terraform — Wikimedia Commons

Referências:

  1. tflint — GitHub
  2. checkov Documentation
  3. Terratest Documentation

원문에서 계속 ↗