Prácticas recomendadas para el estilo y la estructura generales

En este documento, se proporcionan recomendaciones básicas de estilo y estructura para tus opciones de configuración de Terraform. Estas recomendaciones se aplican a los módulos reutilizables de Terraform y a las configuraciones raíz.

Esta guía no es una introducción a Terraform. Para obtener una introducción al uso de Terraform con Google Cloud, consulta Comienza a usar Terraform.

Sigue una estructura de módulo estándar

  • Los módulos de Terraform deben seguir la estructura de módulo estándar.
  • Inicia cada módulo con un archivo main.tf, en el que los recursos se ubican de forma predeterminada.
  • En cada módulo, incluye un archivo README.md en formato Markdown. En el archivo README.md, incluye la documentación básica sobre el módulo.
  • Coloca los ejemplos en una carpeta examples/, con un subdirectorio separado para cada ejemplo. Para cada ejemplo, incluye un archivo README.md detallado.
  • Crea grupos lógicos de recursos con sus propios archivos y nombres descriptivos, como network.tf, instances.tf o loadbalancer.tf.
    • Evita darle a cada recurso su propio archivo. Agrupa los recursos según su propósito compartido. Por ejemplo, combina google_dns_managed_zone y google_dns_record_set en dns.tf.
  • En el directorio raíz del módulo, incluye solo Terraform (*.tf) y los archivos de metadatos del repositorio (como README.md y CHANGELOG.md).
  • Coloca cualquier documentación adicional en un subdirectorio docs/.

Adopta una convención de nombres

  • Asigna nombres a todos los objetos de configuración con guiones bajos para delimitar varias palabras. Esta práctica garantiza la coherencia con la convención de nombres para los tipos de recursos, los tipos de fuente de datos y otros valores predefinidos. Esta convención no se aplica a los argumentos del nombre.

    Recomendado:

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

    No se recomienda:

    resource "google_compute_instance" "web-server" {
      name = "web-server"
    }
    
  • A fin de simplificar las referencias a un recurso que es el único de su tipo (por ejemplo, un solo balanceador de cargas para un módulo completo), nombra el recurso main.

    • Se necesita trabajo mental adicional para recordar some_google_resource.my_special_resource.id en comparación con some_google_resource.main.id.
  • Para diferenciar los recursos del mismo tipo entre sí (por ejemplo, primary y secondary), proporciona nombres de recursos significativos.

  • Haz que los nombres de recursos sean singulares.

  • En el nombre del recurso, no repitas el tipo de recurso. Por ejemplo:

    Recomendado:

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

    No se recomienda:

    resource "google_compute_global_address" "main_global_address" { … }
    

Usa las variables con cuidado

  • Declara todas las variables en variables.tf.
  • Proporciona nombres descriptivos de variables que sean relevantes para su uso o propósito:
    • Las entradas, las variables locales y los resultados que representan valores numéricos (como los tamaños de disco o de RAM) deben nombrarse con unidades (como ram_size_gb). Las APIs de Google Cloud no tienen unidades estándar, por lo que nombrar variables con unidades hace que la unidad de entrada esperada sea clara para los encargados de mantener la configuración.
    • Para las unidades de almacenamiento, usa prefijos de unidades binarias (potencias de 1,024): kibi, mebi y gibi. Para todas las demás unidades de medida, usa prefijos de unidades decimales (potencias de 1,000): kilo, mega y giga. Este uso coincide con el de Google Cloud.
    • Para simplificar la lógica condicional, asigna nombres positivos a las variables booleanas (por ejemplo, enable_external_access).
  • Las variables deben tener descripciones. Las descripciones se incluyen automáticamente en la documentación generada automáticamente de un módulo publicado. Las descripciones agregan contexto adicional para los desarrolladores nuevos que los nombres descriptivos no pueden proporcionar.
  • Proporciona tipos definidos de variables.
  • Cuando corresponda, proporciona valores predeterminados:
    • Para las variables que tienen valores independientes del entorno (como el tamaño del disco), proporciona valores predeterminados.
    • Para las variables que tienen valores específicos del entorno (como project_id), no proporciones valores predeterminados. De este modo, el módulo que realiza la llamada debe proporcionar valores significativos.
  • Usa valores predeterminados vacíos para las variables (como strings o listas vacías) solo cuando dejar la variable vacía es una preferencia válida que las API subyacentes no rechazan.
  • Sé prudente cuando uses las variables. Solo parametriza los valores que deben variar para cada instancia o entorno. Cuando decidas si debes exponer una variable, asegúrate de tener un caso de uso concreto para cambiar esa variable. Si solo hay una pequeña probabilidad de que se necesite una variable, no la expongas.
    • La adición de una variable con un valor predeterminado es compatible con versiones anteriores.
    • La eliminación de una variable es incompatible con versiones anteriores.
    • En los casos en que un literal se vuelve a usar en varios lugares, puedes usar un valor local sin exponerlo como una variable.

Expón los resultados

  • Organiza todos los resultados en un archivo outputs.tf.
  • Proporciona descripciones significativas para todos los resultados.
  • Documenta las descripciones de salida en el archivo README.md. Genera descripciones automáticas en la confirmación con herramientas como terraform-docs.
  • Genera todos los valores útiles a los que los módulos raíz podrían necesitar hacer referencia o que podrían necesitar compartir. En especial para los módulos de código abierto o de uso intensivo, expón todos los resultados que tengan potencial para su consumo.
  • No pases los resultados directamente a través de variables de entrada, ya que esto evita que se agreguen de forma correcta al grafo de dependencia. Para garantizar que se creen dependencias implícitas, asegúrate de que se generen atributos de referencia de los recursos. En lugar de hacer referencia a una variable de entrada para una instancia directamente, pasa el atributo como se muestra a continuación:

    Recomendado:

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

    No se recomienda:

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

Usa fuentes de datos

  • Coloca fuentes de datos junto a los recursos que hacen referencia a estas. Por ejemplo, si recuperas una imagen que se usará para iniciar una instancia, colócala junto con la instancia en lugar de recopilar recursos de datos en su propio archivo.
  • Si la cantidad de fuentes de datos es grande, considera moverlas a un archivo data.tf dedicado.
  • Para recuperar datos relacionados con el entorno actual, usa la interpolación de variables o recursos.

Limita el uso de secuencias de comandos personalizadas

  • Usa las secuencias de comandos solo cuando sea necesario. El estado de los recursos creados a través de secuencias de comandos no se tiene en cuenta ni se administra en Terraform.
    • De ser posible, evita las secuencias de comandos personalizadas. Úsalas solo cuando los recursos de Terraform no admitan el comportamiento deseado.
    • Cualquier secuencia de comandos personalizada que se use debe tener un motivo documentado con claridad para un plan existente y, idealmente, un plan de baja.
  • Terraform puede llamar a las secuencias de comandos personalizadas mediante aprovisionadores, incluido el aprovisionador local-exec.
  • Coloca las secuencias de comandos personalizadas que llama Terraform en un directorio scripts/.

Incluye secuencias de comandos auxiliares en un directorio separado

  • Organiza las secuencias de comandos auxiliares a las que Terraform no llama en un directorio helpers/.
  • Documenta las secuencias de comandos auxiliares en el archivo README.md con explicaciones y, además, invocaciones de ejemplo.
  • Si las secuencias de comandos auxiliares aceptan argumentos, proporciona la verificación de argumentos y el resultado --help.

Coloca los archivos estáticos en un directorio separado

  • Los archivos estáticos a los que Terraform hace referencia, pero que no ejecuta (como las secuencias de comandos de inicio cargados en instancias de Compute Engine), deben estar organizados en un directorio files/.
  • Coloca los documentos HereDocs largos en archivos externos, separados de su HCL. Haz referencia a ellos con la función file().
  • Para los archivos que se leen con la función templatefile de Terraform, usa la extensión de archivo .tftpl.
    • Las plantillas se deben colocar en un directorio templates/.

Protege los recursos con estado

Para los recursos con estado, como las bases de datos, asegúrate de que la protección contra la eliminación esté habilitada. Por ejemplo:

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

  lifecycle {
    prevent_destroy = true
  }
}

Usa el formato integrado

Todos los archivos de Terraform deben cumplir con los estándares de terraform fmt.

Limita la complejidad de las expresiones

  • Limita la complejidad de cualquier expresión interpolada individual. Si se necesitan muchas funciones en una sola expresión, considera dividirla en varias expresiones mediante valores locales.
  • Nunca tengas más de una operación ternaria en una sola línea. En su lugar, usa varios valores locales para compilar la lógica.

Usa count para valores condicionales

Para crear una instancia de un recurso de forma condicional, usa el metaargumento count. Por ejemplo:

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
  ...
}

Ten cuidado cuando uses variables especificadas por el usuario a fin de establecer la variable count para los recursos. Si se proporciona un atributo de recurso para una variable de este tipo (como project_id) y ese recurso aún no existe, Terraform no puede generar un plan. En su lugar, Terraform informa el error value of count cannot be computed. En esos casos, usa una variable enable_x independiente para calcular la lógica condicional.

Usa for_each para recursos iterados

Si deseas crear varias copias de un recurso según un recurso de entrada, usa el metaargumento for_each.

Publica módulos en un registro

¿Qué sigue?