Práticas recomendadas para o estilo e a estrutura gerais

Este documento fornece recomendações básicas de estilo e estrutura para as suas configurações do Terraform. Estas recomendações aplicam-se a módulos Terraform reutilizáveis e a configurações raiz.

Este guia não é uma introdução ao Terraform. Para uma introdução à utilização do Terraform com o Google Cloud, consulte o artigo Começar a usar o Terraform.

Siga uma estrutura de módulo padrão

  • Os módulos do Terraform têm de seguir a estrutura de módulos padrão.
  • Comece cada módulo com um ficheiro main.tf, onde os recursos estão localizados por predefinição.
  • Em cada módulo, inclua um ficheiro README.md no formato Markdown. No ficheiro README.md, inclua documentação básica sobre o módulo.
  • Coloque os exemplos numa pasta examples/, com um subdiretório separado para cada exemplo. Para cada exemplo, inclua um ficheiro README.md detalhado.
  • Crie agrupamentos lógicos de recursos com os seus próprios ficheiros e nomes descritivos, como network.tf, instances.tf ou loadbalancer.tf.
    • Evite dar a cada recurso o seu próprio ficheiro. Agrupe os recursos de acordo com o respetivo objetivo partilhado. Por exemplo, combine google_dns_managed_zone e google_dns_record_set em dns.tf.
  • No diretório raiz do módulo, inclua apenas ficheiros de metadados do Terraform (*.tf) e do repositório (como README.md e CHANGELOG.md).
  • Coloque qualquer documentação adicional numa subdiretoria docs/.

Adote uma convenção de nomenclatura

  • Dê nomes a todos os objetos de configuração usando carateres de sublinhado para delimitar várias palavras. Esta prática garante a consistência com a convenção de nomenclatura para tipos de recursos, tipos de origens de dados e outros valores predefinidos. Esta convenção não se aplica a argumentos de nome.

    Recomendado:

    resource "google_compute_instance" "web_server" {
      name = "web-server"
    }
    

    Não recomendado:

    resource "google_compute_instance" "web-server" {
      name = "web-server"
    }
    
  • Para simplificar as referências a um recurso que é o único do seu tipo (por exemplo, um único equilibrador de carga para um módulo inteiro), atribua o nome main ao recurso.

    • É preciso um esforço mental adicional para se lembrar de some_google_resource.my_special_resource.id em comparação com some_google_resource.main.id.
  • Para distinguir os recursos do mesmo tipo entre si (por exemplo, primary e secondary), indique nomes de recursos significativos.

  • Torne os nomes de recursos singulares.

  • No nome do recurso, não repita o tipo de recurso. Por exemplo:

    Recomendado:

    resource "google_compute_global_address" "main" { ... }
    

    Não recomendado:

    resource "google_compute_global_address" "main_global_address" { … }
    

Use variáveis com cuidado

  • Declare todas as variáveis em variables.tf.
  • Atribua às variáveis nomes descritivos relevantes para a respetiva utilização ou finalidade:
    • As entradas, as variáveis locais e as saídas que representam valores numéricos, como os tamanhos do disco ou o tamanho da RAM, têm de ter nomes com unidades (como ram_size_gb). Google Cloud As APIs não têm unidades padrão, pelo que a atribuição de nomes às variáveis com unidades torna a unidade de entrada esperada clara para os responsáveis pela manutenção da configuração.
    • Para unidades de armazenamento, use prefixos de unidades binárias (potências de 1024): kibi, mebi, gibi. Para todas as outras unidades de medida, use prefixos de unidades decimais (potências de 1000): kilo, mega e giga. Esta utilização corresponde à utilização no âmbito do Google Cloud.
    • Para simplificar a lógica condicional, atribua nomes positivos às variáveis booleanas, por exemplo, enable_external_access.
  • As variáveis têm de ter descrições. As descrições são incluídas automaticamente na documentação gerada automaticamente de um módulo publicado. As descrições adicionam contexto adicional para novos programadores que os nomes descritivos não podem fornecer.
  • Atribua tipos definidos às variáveis.
  • Quando adequado, forneça valores predefinidos:
    • Para variáveis que tenham valores independentes do ambiente (como o tamanho do disco), forneça valores predefinidos.
    • Para variáveis que tenham valores específicos do ambiente (como project_id), não forneça valores predefinidos. Desta forma, o módulo de chamadas tem de fornecer valores significativos.
  • Use predefinições vazias para variáveis (como strings ou listas vazias) apenas quando deixar a variável vazia for uma preferência válida que as APIs subjacentes não rejeitam.
  • Seja criterioso na utilização de variáveis. Apenas parametrize valores que tenham de variar para cada instância ou ambiente. Quando decidir se deve expor uma variável, certifique-se de que tem um exemplo de utilização concreto para alterar essa variável. Se existir apenas uma pequena probabilidade de ser necessária uma variável, não a exponha.
    • A adição de uma variável com um valor predefinido é retrocompatível.
    • A remoção de uma variável é incompatível com versões anteriores.
    • Nos casos em que um literal é reutilizado em vários locais, pode usar um valor local sem o expor como uma variável.

Exponha resultados

  • Organize todas as saídas num ficheiro outputs.tf.
  • Forneça descrições significativas para todos os resultados.
  • Documentar as descrições de saída no ficheiro README.md. Gere automaticamente descrições no commit com ferramentas como o terraform-docs.
  • Produzir todos os valores úteis que os módulos raiz possam precisar de consultar ou partilhar. Especialmente para módulos de código aberto ou muito usados, exponha todas as saídas que tenham potencial de consumo.
  • Não transmita resultados diretamente através de variáveis de entrada, porque isso impede que sejam adicionados corretamente ao gráfico de dependências. Para garantir que são criadas dependências implícitas, certifique-se de que as saídas fazem referência a atributos de recursos. Em vez de fazer referência diretamente a uma variável de entrada para uma instância, transmita o atributo da seguinte forma:

    Recomendado:

    output "name" {
      description = "Name of instance"
      value       = google_compute_instance.main.name
    }
    

    Não recomendado:

    output "name" {
      description = "Name of instance"
      value       = var.name
    }
    

Use origens de dados

  • Coloque as origens de dados junto aos recursos que lhes fazem referência. Por exemplo, se estiver a obter uma imagem a usar no lançamento de uma instância, coloque-a junto à instância em vez de recolher recursos de dados no respetivo ficheiro.
  • Se o número de origens de dados aumentar, pondere movê-las para um ficheiro data.tf dedicado.
  • Para obter dados relativos ao ambiente atual, use a interpolação de variáveis ou recursos.

Limite a utilização de scripts personalizados

  • Use scripts apenas quando necessário. O estado dos recursos criados através de scripts não é contabilizado nem gerido pelo Terraform.
    • Se possível, evite scripts personalizados. Use-as apenas quando os recursos do Terraform não suportarem o comportamento pretendido.
    • Todos os scripts personalizados usados têm de ter um motivo claramente documentado para a sua existência e, idealmente, um plano de descontinuação.
  • O Terraform pode chamar scripts personalizados através de aprovisionadores, incluindo o aprovisionador local-exec.
  • Coloque scripts personalizados chamados pelo Terraform num diretório scripts/.

Inclua scripts auxiliares num diretório separado

  • Organize os scripts auxiliares que não são chamados pelo Terraform num diretório helpers/.
  • Documente os scripts auxiliares no ficheiro README.md com explicações e invocações de exemplo.
  • Se os scripts auxiliares aceitarem argumentos, forneça verificação de argumentos e --help saída.

Coloque ficheiros estáticos num diretório separado

  • Os ficheiros estáticos aos quais o Terraform faz referência, mas que não executa (como scripts de inicialização carregados em instâncias do Compute Engine), têm de ser organizados num diretório files/.
  • Coloque HereDocs longos em ficheiros externos, separados do respetivo HCL. Faça referência a eles com a função file().
  • Para ficheiros lidos através da função Terraform templatefile, use a extensão de ficheiro .tftpl.
    • Os modelos têm de ser colocados num diretório templates/.

Proteja recursos com estado

Para recursos com estado, como bases de dados, certifique-se de que a proteção contra eliminação está ativada. Por exemplo:

resource "google_sql_database_instance" "main" {
  name = "primary-instance"
  settings {
    tier = "D0"
  }

  lifecycle {
    prevent_destroy = true
  }
}

Use a formatação integrada

Todos os ficheiros Terraform têm de estar em conformidade com as normas da terraform fmt.

Limite a complexidade das expressões

  • Limite a complexidade de quaisquer expressões interpoladas individuais. Se precisar de muitas funções numa única expressão, considere dividi-la em várias expressões usando valores locais.
  • Nunca tenha mais do que uma operação ternária numa única linha. Em alternativa, use vários valores locais para criar a lógica.

Use count para valores condicionais

Para instanciar um recurso condicionalmente, use o meta-argumento count. Por exemplo:

variable "readers" {
  description = "..."
  type        = list
  default     = []
}

resource "resource_type" "reference_name" {
  // Do not create this resource if the list of readers is empty.
  count = length(var.readers) == 0 ? 0 : 1
  ...
}

Use com moderação as variáveis especificadas pelo utilizador para definir a variável count para recursos. Se for fornecido um atributo de recurso para essa variável (como project_id) e esse recurso ainda não existir, o Terraform não pode gerar um plano. Em alternativa, o Terraform comunica o erro value of count cannot be computed. Nestes casos, use uma variável enable_x separada para calcular a lógica condicional.

Use for_each para recursos iterados

Se quiser criar várias cópias de um recurso com base num recurso de entrada, use o meta-argumento for_each.

Publique módulos num registo

O que se segue?