Pular para o conteúdo

Módulos que outros times usam

Avançado18 min de leituraiac

Um módulo usado só pelo autor é uma pasta. Um módulo usado por outros times é um produto: tem interface, versão, documentação, compatibilidade e alguém responsável por não quebrar quem depende dele. Confundir os dois é a origem do módulo com quarenta variáveis que ninguém entende e que todo mundo copia em vez de usar.

Comece pelo que o consumidor precisa dizer, não pelos recursos que você quer criar. Se a chamada não cabe em poucas linhas legíveis, a abstração está errada.

module "servico_web" {
source = "git::https://github.com/empresa/tf-modulos.git//servico-web?ref=v2.3.0"
nome = "loja"
ambiente = "prod"
imagem = "registry.exemplo.com/loja@sha256:abc..."
dominio = "loja.exemplo.com"
replicas = 4
}

Três sinais de interface saudável:

  • Poucas entradas obrigatórias, todas sem valor padrão razoável possível.
  • Nomes do domínio do consumidor (replicas, dominio), não do provider (desired_count, alb_listener_rule_priority).
  • Saídas úteis: o que o consumidor precisa para conectar outras coisas (ARN, endpoint, nome do Secret), e nada além.

Se o módulo expõe uma variável para cada atributo de cada recurso, ele não abstrai nada — só adiciona uma camada de indireção sobre o provider.

Módulo compartilhado precisa de tag semântica e de contrato de compatibilidade:

  • patch: correção que não muda a interface.
  • minor: variável nova com padrão, saída nova.
  • major: variável removida ou renomeada, mudança que destrói e recria recurso, requisito novo de versão de provider.
module "servico_web" {
source = "app.terraform.io/empresa/servico-web/aws"
version = "~> 2.3" # aceita 2.x, nunca sobe para 3.0 sozinho
}

Nunca aponte para main: quem consome passa a ter a infraestrutura alterada por um merge que ele não viu. Publique um CHANGELOG.md com a instrução de migração de cada major — quem vai executar terraform plan amanhã precisa saber se aquele -/+ é esperado.

Prefira módulos pequenos que se combinam a um módulo grande que tenta tudo:

modulos/
rede-basica/ # VPC, subnets, rotas
servico-web/ # composição: task/deployment + service + ingress + DNS
banco-postgres/ # instância + parâmetros + backup + alarmes

Aninhar mais de dois níveis costuma ser sinal de problema: o consumidor perde a rastreabilidade do que é criado e o plano fica ilegível. Módulo “raiz” que só passa variáveis adiante é camada sem valor — remova.

Evite também o count/for_each no módulo inteiro como forma de ligar e desligar partes: um módulo com dez condicionais tem dez comportamentos e nenhum é testado.

Padrão bom é o seguro, mesmo quando não é o mais barato nem o mais conveniente:

variable "acesso_publico" {
type = bool
default = false # o padrão precisa ser o seguro; abrir é decisão explícita
}
variable "retencao_backup_dias" {
type = number
default = 7
validation {
condition = var.retencao_backup_dias >= 7
error_message = "Retenção mínima de 7 dias por política interna."
}
}
variable "tags" {
type = map(string)
default = {}
}

Padrões que já causaram incidente: bucket público, banco sem deletion_protection, sem criptografia, sem log de auditoria, com senha gerada e devolvida em output não sensível. A regra: o caminho preguiçoso precisa ser o caminho seguro, porque é o que 90% das pessoas vão seguir.

Módulo compartilhado sem teste quebra o ambiente de outra pessoa. O mínimo viável:

# tests/basico.tftest.hcl — nativo, roda com `terraform test`
run "cria_com_padroes_seguros" {
command = plan
variables { nome = "teste", ambiente = "dev" }
assert {
condition = aws_s3_bucket_public_access_block.este.block_public_acls == true
error_message = "O bucket não pode permitir ACL pública por padrão."
}
}

E a documentação sai do próprio código, para não envelhecer:

Janela do terminal
terraform-docs markdown table . > README.md # rode no CI e falhe se houver diferença
tflint --recursive && terraform validate

No pipeline do repositório de módulos: fmt, validate, tflint, terraform test, verificação do README gerado e publicação da tag. Assim a promessa de estabilidade tem alguma coisa por trás.

Próximo passo: para configurar sistemas que já existem, em vez de provisioná-los, veja Ansible. O passo a passo de extração está em Módulo reutilizável.