Módulos que outros times usam
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.
Módulo é uma interface
Seção intitulada “Módulo é uma interface”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.
Versionamento e compatibilidade
Seção intitulada “Versionamento e compatibilidade”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.
Composição, não herança
Seção intitulada “Composição, não herança”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 + alarmesAninhar 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.
Valores padrão que não sabotam
Seção intitulada “Valores padrão que não sabotam”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.
Teste e documentação geradas
Seção intitulada “Teste e documentação geradas”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:
terraform-docs markdown table . > README.md # rode no CI e falhe se houver diferençatflint --recursive && terraform validateNo 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.