As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Melhores práticas para organização e estrutura de base de código
A estrutura e a organização adequadas da base de código são essenciais à medida que o uso do Terraform cresce em grandes equipes e empresas. Uma base de código bem arquitetada permite a colaboração em grande escala e, ao mesmo tempo, aprimora a capacidade de manutenção.
Esta seção fornece recomendações sobre modularidade, convenções de nomenclatura, documentação e padrões de codificação do Terraform que oferecem suporte à qualidade e consistência.
A orientação inclui dividir a configuração em módulos reutilizáveis por ambiente e componentes, estabelecer convenções de nomenclatura usando prefixos e sufixos, documentar módulos e explicar claramente as entradas e saídas e aplicar regras de formatação consistentes usando verificações de estilo automatizadas.
As melhores práticas adicionais abrangem a organização lógica de módulos e recursos em uma hierarquia estruturada, a catalogação de módulos públicos e privados na documentação e a abstração de detalhes de implementação desnecessários em módulos para simplificar o uso.
Ao implementar diretrizes de estrutura de base de código sobre modularidade, documentação, padrões e organização lógica, você pode oferecer suporte à ampla colaboração entre equipes e, ao mesmo tempo, manter o Terraform sustentável à medida que o uso se espalha pela organização. Ao aplicar convenções e padrões, você pode evitar a complexidade de uma base de código fragmentada.
Melhores práticas:
Implemente uma estrutura de repositório padrão
Recomendamos que você implemente o seguinte layout de repositório. A padronização dessas práticas de consistência em todos os módulos melhora a capacidade de descoberta, a transparência, a organização e a confiabilidade, ao mesmo tempo que permite a reutilização em muitas configurações do Terraform.
-
Módulo ou diretório raiz: esse deve ser o ponto de entrada principal para os módulos raiz
e reutilizáveis do Terraform e espera-se que seja exclusivo. Se você tiver uma arquitetura mais complexa, poderá usar módulos aninhados para criar abstrações leves. Isso ajuda você a descrever a infraestrutura em termos de sua arquitetura, em vez de diretamente, em termos de objetos físicos. -
README: O módulo raiz e todos os módulos aninhados devem ter arquivos README. Esse arquivo deve ser nomeado
README.md
. Ele deve conter uma descrição do módulo e para que ele deve ser usado. Se você quiser incluir um exemplo de uso desse módulo com outros recursos, coloque-o em umexamples
diretório. Considere incluir um diagrama que descreva os recursos de infraestrutura que o módulo pode criar e seus relacionamentos. Use terraform-docspara gerar automaticamente entradas ou saídas do módulo. -
main.tf: Este é o ponto de entrada principal. Para um módulo simples, todos os recursos podem ser criados nesse arquivo. Para um módulo complexo, a criação de recursos pode estar espalhada por vários arquivos, mas todas as chamadas de módulo aninhadas devem estar no
main.tf
arquivo. -
variables.tf e outputs.tf: esses arquivos contêm as declarações de variáveis e saídas. Todas as variáveis e resultados devem ter descrições de uma ou duas frases que expliquem seu propósito. Essas descrições são usadas para documentação. Para obter mais informações, consulte a HashiCorp documentação para configuração de variáveis e configuração
de saída . -
Todas as variáveis devem ter um tipo definido.
-
A declaração da variável também pode incluir um argumento padrão. Se a declaração incluir um argumento padrão, a variável será considerada opcional e o valor padrão será usado se você não definir um valor ao chamar o módulo ou executar o Terraform. O argumento padrão requer um valor literal e não pode referenciar outros objetos na configuração. Para tornar uma variável obrigatória, omita um padrão na declaração da variável e considere se a configuração
nullable = false
faz sentido. -
Para variáveis que têm valores independentes do ambiente (como
disk_size
), forneça valores padrão. -
Para variáveis que tenham valores específicos do ambiente (como
project_id
), não forneça valores padrão. Nesse caso, o módulo de chamada deve fornecer valores significativos. -
Use padrões vazios para variáveis como cadeias de caracteres ou listas vazias somente quando deixar a variável vazia for uma preferência válida que as APIs subjacentes não rejeitam.
-
Seja criterioso no uso de variáveis. Parametrize valores somente se eles precisarem variar para cada instância ou ambiente. Ao decidir se deseja expor uma variável, certifique-se de ter um caso de uso concreto para alterar essa variável. Se houver apenas uma pequena chance de que uma variável seja necessária, não a exponha.
-
Adicionar uma variável com um valor padrão é compatível com versões anteriores.
-
A remoção de uma variável é incompatível com versões anteriores.
-
Nos casos em que um literal é reutilizado em vários lugares, você deve usar um valor local sem expô-lo como uma variável.
-
-
Não passe saídas diretamente por meio de variáveis de entrada, pois isso impede que elas sejam adicionadas adequadamente ao gráfico de dependências. Para garantir que dependências implícitas
sejam criadas, certifique-se de que as saídas façam referência aos atributos dos recursos. Em vez de referenciar diretamente uma variável de entrada para uma instância, passe o atributo.
-
-
locals.tf: esse arquivo contém valores locais que atribuem um nome a uma expressão, portanto, um nome pode ser usado várias vezes em um módulo em vez de repetir a expressão. Os valores locais são como as variáveis locais temporárias de uma função. As expressões em valores locais não se limitam a constantes literais; elas também podem fazer referência a outros valores no módulo, incluindo variáveis, atributos de recursos ou outros valores locais, para combiná-los.
-
providers.tf: Esse arquivo contém o bloco terraform e os blocos provider
. provider
os blocos devem ser declarados somente nos módulos raiz pelos consumidores dos módulos.Se você estiver usando o HCP Terraform, adicione também um bloco de nuvem
vazio. O cloud
bloco deve ser configurado inteiramente por meio de variáveis de ambientee credenciais de variáveis de ambiente como parte de um pipeline de CI/CD. -
versions.tf: Esse arquivo contém o bloco required_providers.
Todos os módulos do Terraform devem declarar quais provedores são necessários para que o Terraform possa instalar e usar esses provedores. -
data.tf: para uma configuração simples, coloque as fontes de dados
ao lado dos recursos que fazem referência a elas. Por exemplo, se você estiver buscando uma imagem para ser usada na execução de uma instância, coloque-a ao lado da instância em vez de coletar recursos de dados em seu próprio arquivo. Se o número de fontes de dados ficar muito grande, considere movê-las para um data.tf
arquivo dedicado. -
arquivos.tfvars: para módulos raiz, você pode fornecer variáveis não confidenciais usando um arquivo.
.tfvars
Para consistência, nomeie os arquivos variáveisterraform.tfvars
. Coloque valores comuns na raiz do repositório e valores específicos do ambiente na pasta.envs/
-
Módulos aninhados: os módulos aninhados devem existir no
modules/
subdiretório. Qualquer módulo aninhado que tenha umREADME.md
é considerado utilizável por um usuário externo. Se aREADME.md
não existir, o módulo será considerado somente para uso interno. Módulos aninhados devem ser usados para dividir o comportamento complexo em vários módulos pequenos que os usuários podem escolher cuidadosamente.Se o módulo raiz incluir chamadas para módulos aninhados, essas chamadas devem usar caminhos relativos, por exemplo,
./modules/sample-module
para que o Terraform os considere parte do mesmo repositório ou pacote, em vez de baixá-los novamente separadamente.Se um repositório ou pacote contiver vários módulos aninhados, o ideal é que eles possam ser compostos pelo chamador, em vez de chamarem diretamente uns aos outros e criarem uma árvore de módulos profundamente aninhada.
-
Exemplos: exemplos de uso de um módulo reutilizável devem existir no
examples/
subdiretório na raiz do repositório. Para cada exemplo, você pode adicionar um README para explicar o objetivo e o uso do exemplo. Exemplos de submódulos também devem ser colocados noexamples/
diretório raiz.Como os exemplos geralmente são copiados em outros repositórios para personalização, os blocos de módulos devem ter sua origem definida para o endereço que um chamador externo usaria, não para um caminho relativo.
-
Arquivos com nome de serviço: os usuários geralmente desejam separar os recursos do Terraform por serviço em vários arquivos. Essa prática deve ser desencorajada tanto quanto possível, e os recursos devem ser definidos em
main.tf
seu lugar. No entanto, se uma coleção de recursos (por exemplo, funções e políticas do IAM) exceder 150 linhas, é razoável dividi-la em seus próprios arquivos, comoiam.tf
. Caso contrário, todo o código do recurso deverá ser definido nomain.tf
. -
Scripts personalizados: use scripts somente quando necessário. O Terraform não contabiliza nem gerencia o estado dos recursos criados por meio de scripts. Use scripts personalizados somente quando os recursos do Terraform não forem compatíveis com o comportamento desejado. Coloque scripts personalizados chamados pelo Terraform em um
scripts/
diretório. -
Scripts auxiliares: organize scripts auxiliares que não são chamados pelo Terraform em um diretório.
helpers/
Documente scripts auxiliares noREADME.md
arquivo com explicações e exemplos de invocações. Se os scripts auxiliares aceitarem argumentos, forneça verificação e--help
saída de argumentos. -
Arquivos estáticos: arquivos estáticos aos quais o Terraform faz referência, mas não executa (como scripts de inicialização carregados em instâncias do EC2) devem ser organizados em um
files/
diretório. Coloque documentos longos em arquivos externos, separados do HCL. Referencie-os com a função file (). -
Modelos: Para arquivos que a função templatefile do Terraform lê, use a extensão de arquivo
. .tftpl
Os modelos devem ser colocados em umtemplates/
diretório.
Estrutura do módulo raiz
O Terraform sempre é executado no contexto de um único módulo raiz. Uma configuração completa do Terraform consiste em um módulo raiz e na árvore de módulos secundários (que inclui os módulos que são chamados pelo módulo raiz, quaisquer módulos chamados por esses módulos e assim por diante).
Exemplo básico do layout do módulo raiz do Terraform:
. ├── data.tf ├── envs │ ├── dev │ │ └── terraform.tfvars │ ├── prod │ │ └── terraform.tfvars │ └── test │ └── terraform.tfvars ├── locals.tf ├── main.tf ├── outputs.tf ├── providers.tf ├── README.md ├── terraform.tfvars ├── variables.tf └── versions.tf
Estrutura de módulo reutilizável
Os módulos reutilizáveis seguem os mesmos conceitos dos módulos raiz. Para definir um módulo, crie um novo diretório para ele e coloque os .tf
arquivos nele, assim como você definiria um módulo raiz. O Terraform pode carregar módulos a partir de caminhos relativos locais ou de repositórios remotos. Se você espera que um módulo seja reutilizado por várias configurações, coloque-o em seu próprio repositório de controle de versão. É importante manter a árvore de módulos relativamente plana para facilitar a reutilização dos módulos em diferentes combinações.
Exemplo básico de layout de módulo reutilizável do Terraform:
. ├── data.tf ├── examples │ ├── multi-az-new-vpc │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── README.md │ │ ├── terraform.tfvars │ │ ├── variables.tf │ │ ├── versions.tf │ │ └── vpc.tf │ └── single-az-existing-vpc │ │ ├── data.tf │ │ ├── locals.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── README.md │ │ ├── terraform.tfvars │ │ ├── variables.tf │ │ └── versions.tf ├── iam.tf ├── locals.tf ├── main.tf ├── outputs.tf ├── README.md ├── variables.tf └── versions.tf
Estrutura para modularidade
Em princípio, você pode combinar quaisquer recursos e outras construções em um módulo, mas o uso excessivo de módulos aninhados e reutilizáveis pode tornar sua configuração geral do Terraform mais difícil de entender e manter, portanto, use esses módulos com moderação.
Quando fizer sentido, divida sua configuração em módulos reutilizáveis que elevam o nível de abstração descrevendo um novo conceito em sua arquitetura que é construído a partir de tipos de recursos.
Ao modularizar sua infraestrutura em definições reutilizáveis, busque conjuntos lógicos de recursos em vez de componentes individuais ou coleções excessivamente complexas.
Não envolva recursos únicos
Você não deve criar módulos que envolvam outros tipos de recursos únicos. Se você tiver problemas para encontrar um nome para seu módulo que seja diferente do nome do tipo de recurso principal dentro dele, seu módulo provavelmente não está criando uma nova abstração ― está adicionando complexidade desnecessária. Em vez disso, use o tipo de recurso diretamente no módulo de chamada.
Encapsular relacionamentos lógicos
Agrupe conjuntos de recursos relacionados, como bases de rede, camadas de dados, controles de segurança e aplicativos. Um módulo reutilizável deve encapsular partes da infraestrutura que funcionem juntas para habilitar um recurso.
Mantenha a herança estável
Ao agrupar módulos em subdiretórios, evite aprofundar mais de um ou dois níveis. Estruturas de herança profundamente aninhadas complicam as configurações e a solução de problemas. Os módulos devem se basear em outros módulos, não construir túneis por meio deles.
Ao focar os módulos em agrupamentos lógicos de recursos que representam padrões de arquitetura, as equipes podem configurar rapidamente bases de infraestrutura confiáveis. Equilibre a abstração sem excesso de engenharia ou simplificação.
Recursos de referência em resultados
Para cada recurso definido em um módulo reutilizável, inclua pelo menos uma saída que faça referência ao recurso. Variáveis e saídas permitem inferir dependências entre módulos e recursos. Sem nenhuma saída, os usuários não podem solicitar adequadamente seu módulo em relação às configurações do Terraform.
Módulos bem estruturados que fornecem consistência de ambiente, agrupamentos orientados por propósitos e referências de recursos exportados permitem a colaboração em grande escala com o Terraform em toda a organização. As equipes podem montar a infraestrutura a partir de blocos de construção reutilizáveis.
Não configure provedores
Embora os módulos compartilhados herdem os provedores dos módulos de chamada, os módulos não devem definir as configurações do provedor sozinhos. Evite especificar blocos de configuração do provedor nos módulos. Essa configuração só deve ser declarada uma vez globalmente.
Declare os fornecedores necessários
Embora as configurações do provedor sejam compartilhadas entre os módulos, os módulos compartilhados também devem declarar seus próprios requisitos de provedor
Ao declarar os requisitos de versão e evitar a configuração codificada do provedor, os módulos oferecem portabilidade e reutilização em todas as configurações do Terraform usando provedores compartilhados.
Para módulos compartilhados, defina as versões mínimas necessárias do provedor em um bloco required_providersversions.tf
Para declarar que um módulo requer uma versão específica do AWS provedor, use um required_providers
bloco dentro de um terraform
bloco:
terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = ">= 4.0.0" } } }
Se um módulo compartilhado suportar somente uma versão específica do AWS provedor, use o operador de restrição pessimista (~>
), que permite que somente o componente da versão mais à direita seja incrementado:
terraform { required_version = ">= 1.0.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } } }
Neste exemplo, ~> 4.0
permite a instalação de 4.57.1
e4.67.0
, mas não5.0.0
. Para obter mais informações, consulte Sintaxe de restrição de versão
Siga as convenções de nomenclatura
Nomes claros e descritivos simplificam sua compreensão das relações entre os recursos no módulo e a finalidade dos valores de configuração. A consistência com as diretrizes de estilo melhora a legibilidade tanto para os usuários quanto para os mantenedores do módulo.
Siga as diretrizes para nomeação de recursos
-
Use snake_case (onde os termos em minúsculas são separados por sublinhados) para que todos os nomes de recursos correspondam aos padrões de estilo do Terraform. Essa prática garante a consistência com a convenção de nomenclatura para tipos de recursos, tipos de fonte de dados e outros valores predefinidos. Essa convenção não se aplica aos argumentos de nomes
. -
Para simplificar as referências a um recurso que é o único desse tipo (por exemplo, um único balanceador de carga para um módulo inteiro), nomeie o recurso
main
outhis
para maior clareza. -
Use nomes significativos que descrevam a finalidade e o contexto do recurso e que ajudem a diferenciar recursos semelhantes (por exemplo, para o banco de dados principal e
primary
read_replica
para uma réplica de leitura do banco de dados). -
Use nomes singulares, não plurais.
-
Não repita o tipo de recurso no nome do recurso.
Siga as diretrizes para nomenclatura de variáveis
-
Adicione unidades aos nomes das entradas, variáveis locais e saídas que representem valores numéricos, como tamanho do disco ou tamanho da RAM (por exemplo,
ram_size_gb
para o tamanho da RAM em gigabytes). Essa prática deixa clara a unidade de entrada esperada para os mantenedores da configuração. -
Use unidades binárias, como MiB e GiB, para tamanhos de armazenamento, e unidades decimais, como MB ou GB, para outras métricas.
-
Dê nomes positivos às variáveis booleanas, como
enable_external_access
.
Use recursos de anexo
Alguns recursos têm pseudo-recursos incorporados como atributos. Sempre que possível, você deve evitar usar esses atributos de recursos incorporados e, em vez disso, usar o recurso exclusivo para anexar esse pseudo-recurso. Esses relacionamentos de recursos podem causar cause-and-effect problemas exclusivos para cada recurso.
Usando um atributo incorporado (evite esse padrão):
resource "aws_security_group" "allow_tls" { ... ingress { description = "TLS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.main.cidr_block] ipv6_cidr_blocks = [aws_vpc.main.ipv6_cidr_block] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } }
Usando recursos de anexo (preferencial):
resource "aws_security_group" "allow_tls" { ... } resource "aws_security_group_rule" "example" { type = "ingress" description = "TLS from VPC" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [aws_vpc.main.cidr_block] ipv6_cidr_blocks = [aws_vpc.main.ipv6_cidr_block] security_group_id = aws_security_group.allow_tls.id }
Use tags padrão
Atribua tags a todos os recursos que podem aceitar tags. O Terraform AWS Provider tem uma fonte de dados aws_default_tags
Considere adicionar as tags necessárias a todos os recursos criados por um módulo do Terraform. Aqui está uma lista de possíveis tags para anexar:
-
Nome: Nome do recurso legível por humanos
-
AppId: o ID do aplicativo que usa o recurso
-
AppRole: A função técnica do recurso; por exemplo, “servidor web” ou “banco de dados”
-
AppPurpose: a finalidade comercial do recurso; por exemplo, “interface de usuário de front-end” ou “processador de pagamento”
-
Ambiente: o ambiente de software, como desenvolvimento, teste ou produção
-
Projeto: Os projetos que usam o recurso
-
CostCenter: Quem cobrar pelo uso de recursos
Atenda aos requisitos de registro do Terraform
Um repositório de módulos deve atender a todos os requisitos a seguir para que possa ser publicado em um registro do Terraform.
Você deve sempre seguir esses requisitos, mesmo que não esteja planejando publicar o módulo em um registro a curto prazo. Ao fazer isso, você pode publicar o módulo em um registro posteriormente sem precisar alterar a configuração e a estrutura do repositório.
-
Nome do repositório: Para um repositório de módulos, use o nome de três partes
terraform-aws-<NAME>
, que<NAME>
reflete o tipo de infraestrutura que o módulo gerencia. O<NAME>
segmento pode conter hífens adicionais (por exemplo,terraform-aws-iam-terraform-roles
). -
Estrutura padrão do módulo: O módulo deve seguir a estrutura padrão do repositório. Isso permite que o registro inspecione seu módulo e gere documentação, acompanhe o uso de recursos e muito mais.
-
Depois de criar o repositório Git, copie os arquivos do módulo para a raiz do repositório. Recomendamos que você coloque cada módulo destinado a ser reutilizável na raiz de seu próprio repositório, mas você também pode referenciar módulos de subdiretórios.
-
Se você estiver usando o HCP Terraform, publique os módulos que devem ser compartilhados no registro da sua organização. O registro gerencia os downloads e controla o acesso com os tokens da API HCP Terraform, para que os consumidores não precisem acessar o repositório de origem do módulo, mesmo quando executam o Terraform na linha de comando.
-
-
Localização e permissões: o repositório deve estar em um dos seus provedores de sistema de controle de versão (VCS)
configurado, e a conta de usuário do HCP Terraform VCS deve ter acesso de administrador ao repositório. O registro precisa de acesso de administrador para criar os webhooks para importar novas versões do módulo. -
tags x.y.z para lançamentos: pelo menos uma tag de lançamento deve estar presente para que você publique um módulo. O registro usa tags de lançamento para identificar as versões do módulo. Os nomes das tags de lançamento devem usar controle de versão semântico
, que você pode opcionalmente prefixar com um v
(por exemplo, e).v1.1.0
1.1.0
O registro ignora as tags que não se parecem com números de versão. Para obter mais informações sobre a publicação de módulos, consulte a documentação do Terraform.
Para obter mais informações, consulte Preparando um repositório de módulos na documentação
Use fontes de módulos recomendadas
O Terraform usa o source
argumento em um bloco de módulo para encontrar e baixar o código-fonte de um módulo filho.
Recomendamos que você use caminhos locais para módulos estreitamente relacionados que tenham o objetivo principal de eliminar elementos de código repetidos e usar um registro de módulo nativo do Terraform ou um provedor de VCS para módulos que devem ser compartilhados por várias configurações.
Os exemplos a seguir ilustram os tipos de fonte
Registro
Registro do Terraform:
module "lambda" { source = "github.com/terraform-aws-modules/terraform-aws-lambda.git?ref=e78cdf1f82944897ca6e30d6489f43cf24539374" #--> v4.18.0 ... }
Ao fixar hashes de confirmação, você pode evitar o desvio de registros públicos que são vulneráveis a ataques na cadeia de suprimentos.
Terraform do HCP:
module "eks_karpenter" { source = "app.terraform.io/my-org/eks/aws" version = "1.1.0" ... enable_karpenter = true }
Terraform Enterprise:
module "eks_karpenter" { source = "terraform.mydomain.com/my-org/eks/aws" version = "1.1.0" ... enable_karpenter = true }
Provedores de VCS
Os provedores de VCS apoiam o ref
argumento de selecionar uma revisão específica, conforme mostrado nos exemplos a seguir.
GitHub (HTTPS):
module "eks_karpenter" { source = "github.com/my-org/terraform-aws-eks.git?ref=v1.1.0" ... enable_karpenter = true }
Repositório Git genérico (HTTPS):
module "eks_karpenter" { source = "git::https://example.com/terraform-aws-eks.git?ref=v1.1.0" ... enable_karpenter = true }
Repositório Git genérico (SSH):
Atenção
Você precisa configurar as credenciais para acessar repositórios privados.
module "eks_karpenter" { source = "git::ssh://username@example.com/terraform-aws-eks.git?ref=v1.1.0" ... enable_karpenter = true }
Siga os padrões de codificação
Aplique regras e estilos consistentes de formatação do Terraform em todos os arquivos de configuração. Aplique padrões usando verificações de estilo automatizadas em pipelines de CI/CD. Quando você incorpora as melhores práticas de codificação nos fluxos de trabalho da equipe, as configurações permanecem legíveis, sustentáveis e colaborativas à medida que o uso se espalha amplamente pela organização.
Siga as diretrizes de estilo
-
Formate todos os arquivos (
.tf
arquivos) do Terraform com o comando terraform fmtpara corresponder HashiCorp aos padrões de estilo. -
Use o comando terraform validate
para verificar a sintaxe e a estrutura da sua configuração. -
Analise estaticamente a qualidade do código usando o TFlint
. Esse linter verifica as melhores práticas do Terraform, além da formatação, e falha nas compilações quando encontra erros.
Configurar ganchos de pré-confirmação
Configure ganchos de pré-confirmação do lado do cliente que executamterraform fmt
, tflint
checkov
, e outras digitalizações de código e verificações de estilo antes de permitir confirmações. Essa prática ajuda você a validar a conformidade com os padrões mais cedo nos fluxos de trabalho do desenvolvedor.
Use estruturas de pré-confirmação, como pré-confirmação
Mover as verificações de estilo e qualidade para ganchos locais de pré-confirmação fornece feedback rápido aos desenvolvedores antes que as mudanças sejam introduzidas. Os padrões se tornam parte do fluxo de trabalho de codificação.