Cómo mejorar la flexibilidad usando variables, dependencias y condicionales de Terraform

Introducción

El Lenguaje de Configuración de Hashicorp (HCL), que utiliza Terraform, ofrece muchas estructuras y capacidades útiles que están presentes en otros lenguajes de programación. El uso de bucles en el código de infraestructura puede reducir considerablemente la duplicidad de código y aumentar la legibilidad, permitiendo así un mejor refactorizado futuro y una mayor flexibilidad. HCL también proporciona algunas estructuras de datos comunes, como listas y mapas (también llamados arreglos y diccionarios respectivamente en otros lenguajes), así como condicionales para el branching del camino de ejecución.

Único en Terraform es la capacidad de especificar manualmente las dependencias de los recursos. Aunque el gráfico de ejecución que construye al ejecutar tu código ya contiene las conexiones detectadas (que son correctas en la mayoría de los casos), puede encontrarse en la necesidad de forzar una relación de dependencia que Terraform no pudo detectar.

En este artículo, revisaremos las estructuras de datos que proporciona HCL, sus características de bucle para recursos (la clave count, for_each y for), las condicionales para manejar valores conocidos y desconocidos, y las relaciones de dependencia entre recursos.

Prerrequisitos

  • Un Token de Acceso Personal de DigitalOcean, que puede crear a través del panel de control de DigitalOcean. Puedes encontrar instrucciones en los documentos de productos de DigitalOcean, Cómo Crear un Token de Acceso Personal.
  • Terraform instalado en tu máquina local y un proyecto establecido con el proveedor de DigitalOcean. Completa Step 1 y Step 2 del tutorial Cómo Usar Terraform con DigitalOcean, y asegúrate de nombrar la carpeta del proyecto terraform-flexibilidad, en lugar de loadbalance. Durante Step 2, no necesitas incluir la variable pvt_key y la recurrencia de claves SSH cuando configures el proveedor.

Nota: Este tutorial ha sido probado específicamente con Terraform 1.0.2.

Tipos de datos en HCL

Antes de aprender más sobre ciclos y otras características de HCL que hacen que tu código sea más flexible, primero revisaremos los tipos de datos disponibles y su uso.

El lenguaje de configuración de Hashicorp soporta tipos primitivos y complejos. Los tipos primitivos son cadenas de caracteres, números y valores booleanes, que son los tipos básicos que no se derivan de otros. Las tipos complejos, en cambio, agrupan varios valores en uno solo. Los dos tipos de valores complejos son tipos estructurales y coleccionistas. Los tipos estructurales permiten agrupar valores de diferentes tipos juntos. Un ejemplo principal es la definición de recursos que usas para especificar qué va a parecer tu infraestructura. En comparación con los tipos estructurales, las colecciones también agrupan valores, pero solo de un mismo tipo. Los tres tipos disponibles de valores de colección en HCL que nos interesan son listas, mapas y conjuntos.

Los listas son similares a los arreglos en otras lenguajes de programación. Contienen un número conocido de elementos de el mismo tipo, que se acceden mediante notación de arreglo ([]) utilizando su índice entero, comenzando desde 0. Aquí hay un ejemplo de declaración de una variable lista que contiene los nombres de Dropletas que despliegues en los pasos siguientes:

Listas

Para el tipo, se especificó que es una lista cuyo elemento tiene el mismo tipo de cadena, y luego se proporcionó su valor predeterminado. En HCL, los valores enumerados entre paréntesis significan una lista.

variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third"]
}

En HCL, los valores enumerados entre corchetes significan una lista.

Mapas

Las mapas son colecciones de pares clave-valor donde cada valor se accede usando su clave de tipo string. Hay dos maneras de especificar mapas dentro de corchetes: usando comillas o signos iguales (=). En ambos casos, el valor debe estar encomendado con comillas. Cuando se usan las comillas, la clave también debe estar encomendada.

La siguiente definición de mapa que contiene nombres de Droplet para diferentes entornos está escrita usando el signo igual:

variable "droplet_env_names" {
  type = map(string)

  default = {
    development = "dev-droplet"
    staging = "staging-droplet"
    production = "prod-droplet"
  }
}

Si la clave comienza con un número, debes usar la sintaxis de colón:

variable "droplet_env_names" {
  type = map(string)

  default = {
    "1-development": "dev-droplet"
    "2-staging": "staging-droplet"
    "3-production": "prod-droplet"
  }
}

Conjuntos

Los conjuntos no admiten ordenamiento, lo que significa que la travesía de los conjuntos no garantiza una ordenación idéntica cada vez y que sus elementos no pueden ser accedidos de manera objetivo. Contienen elementos únicos repetidos exactamente una vez y especificar el mismo elemento varias veces resultará en que solo sea presente una instancia en el conjunto.

Declarar un conjunto es similar a declarar una lista, la única diferencia es el tipo de variable:

variable "droplet_names" {
  type    = set(string)
  default = ["first", "second", "third", "fourth"]
}

Ahora que has aprendido sobre los tipos de estructuras de datos que HCL ofrece y revisado la sintaxis de listas, mapas y conjuntos, que utilizaremos a lo largo de este tutorial, pasarás a probar algunos métodos flexibles de despliegue de múltiples instancias del mismo recurso en Terraform.

Configurando el número de recursos usando la clave «count»

En esta sección, crearás múltiples instancias del mismo recurso utilizando la clave «count». La clave «count» está disponible en todos los recursos y especifica cuántas instancias deberían ser creadas.

Veremos cómo funciona escribiendo una instancia de Droplet que almacenaremos en un archivo llamado droplets.tf en el directorio del proyecto que creaste como parte de los requisitos previos. Cree y abre para editarlo ejecutando:

  1. nano droplets.tf

Agregue las siguientes líneas:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = 3
  image  = "ubuntu-20-04-x64"
  name   = "web"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Este código define un recurso Droplet llamado test_droplet, con Ubuntu 20.04, 1GB de RAM y un núcleo de CPU.

Noté que el valor de count es establecido en 3, lo que significa que Terraform intentará crear tres instancias del mismo recurso. Cuando lo haya hecho, guarde y cierre el archivo.

Puedes planificar el proyecto para ver qué acciones Terraform haría ejecutando:

  1. terraform plan -var "do_token=${DO_PAT}"

La salida será similar a esta:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] será creado + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } # digitalocean_droplet.test_droplet[1] será creado + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } # digitalocean_droplet.test_droplet[2] será creado + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } Plan: 3 to add, 0 to change, 0 to destroy. ...

La salida detallada que crearía Terraform tres instancias de test_droplet, todas con el mismo nombre web. Aunque es posible, no es preferido, por lo que modifiquemos la definición de Droplet para que cada instancia tenga un nombre único. Abre droplets.tf para editar:

  1. nano droplets.tf

Modifica la línea destacada:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = 3
  image  = "ubuntu-20-04-x64"
  name   = "web.${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Guarda y cierra el archivo.

El objeto count proporciona el parámetro index, el cual contiene el índice actual de iteración, comenzando desde 0. El índice actual se sustituye en el nombre del Droplet usando interpolación de cadenas, lo que permite construir una cadena dinámica al sustituir variables. Puedes planificar el proyecto de nuevo para ver los cambios:

  1. terraform plan -var "do_token=${DO_PAT}"

La salida será similar a esta:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] será creado + resource "digitalocean_droplet" "test_droplet" { ... name = "web.0" ... } # digitalocean_droplet.test_droplet[1] será creado + resource "digitalocean_droplet" "test_droplet" { ... name = "web.1" ... } # digitalocean_droplet.test_droplet[2] será creado + resource "digitalocean_droplet" "test_droplet" { ... name = "web.2" ... } Plan: 3 to add, 0 to change, 0 to destroy. ...

Esta vez, las tres instancias de test_droplet tendrán su índice en sus nombres, lo que les hará más fáciles de rastrear.

Ahora sabes cómo crear varias instancias de un recurso utilizando la clave count, así como obtener y usar el índice de una instancia durante la provisión. Siguiente, aprenderás cómo obtener el nombre del Droplet de una lista.

Obtener nombres de gotas de una lista

En situaciones en las que se necesitan múltiples instancias del mismo recurso con nombres personalizados, puedes obtenerlos dinámicamente de una variable de lista que definas. Durante el resto del tutorial, verás varios métodos para automatizar la implementación de gotas desde una lista de nombres, promoviendo flexibilidad y facilidad de uso.

Primero deberás definir una lista conteniendo los nombres de las gotas. Abre un archivo llamado variables.tf y lo abre para editarlo:

  1. nano variables.tf

Agrega las siguientes líneas:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

Guarda y cierra el archivo. Este código define una lista llamada droplet_names, que contiene cadenas como first, second, third, y fourth.

Abra droplets.tf para editar:

  1. nano droplets.tf

Modifica las líneas destacadas:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = length(var.droplet_names)
  image  = "ubuntu-20-04-x64"
  name   =  var.droplet_names[count.index]
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Para mejorar la flexibilidad, en lugar de especificar manualmente un número constante de elementos, pasas la longitud de la lista droplet_names al parámetro count, el cual siempre devolverá el número de elementos en la lista. Para el nombre, obtén el elemento de la lista ubicado en count.index, utilizando notación de corchetes de arreglo. Guarda y cierra el archivo cuando haya terminado.

Intenta planificar el proyecto nuevamente. Recibirás salida similar a esta:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] se creará + resource "digitalocean_droplet" "test_droplet" { ... + name = "first" ... } # digitalocean_droplet.test_droplet[1] se creará + resource "digitalocean_droplet" "test_droplet" { ... + name = "second" ... } # digitalocean_droplet.test_droplet[2] se creará + resource "digitalocean_droplet" "test_droplet" { ... + name = "third" ... } # digitalocean_droplet.test_droplet[3] se creará + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... Plan: 4 to add, 0 to change, 0 to destroy. ...

Como resultado de estas modificaciones, se desplegarían cuatro Droplet, nombrados sucesivamente después de los elementos de la lista droplet_names.

Ha aprendido sobre count, sus características y sintaxis, y ha utilizado junto con una lista para modificar las instancias de los recursos. Ahora verá sus desventajas y cómo superarlas.

Comprender las Desventajas de count

Ahora que sabe cómo se utiliza count, vamos a examinar sus desventajas al modificar la lista con la que se utiliza.

Pruebe a desplegar los Droplet en la nube:

  1. terraform apply -var "do_token=${DO_PAT}"

Escriba yes cuando se le pida. El final de su salida será similar a esto:

Output
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Ahora vamos a crear una instancia de Droplet más ampliando la lista droplet_names. Abra variables.tf para editar:

  1. nano variables.tf

Añada un nuevo elemento al inicio de la lista:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["zero", "first", "second", "third", "fourth"]
}

Cuando haya terminado, guarde y cierre el archivo.

El proyecto se planifica de la siguiente manera:

  1. terraform plan -var "do_token=${DO_PAT}"

Se recibirá un resultado como el siguiente:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create ~ update in-place Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] será actualizado en lugar ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "first" -> "zero" ... } # digitalocean_droplet.test_droplet[1] será actualizado en lugar ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "second" -> "first" ... } # digitalocean_droplet.test_droplet[2] será actualizado en lugar ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "third" -> "second" ... } # digitalocean_droplet.test_droplet[3] será actualizado en lugar ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "fourth" -> "third" ... } # digitalocean_droplet.test_droplet[4] será creado + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... } Plan: 1 to add, 4 to change, 0 to destroy. ...

La salida muestra que Terraform renombraría los primeros cuatro Drops y crearía uno nuevo llamado cuarto, porque considera las instancias como una lista ordenada y identifica los elementos (Droplets) por su número de índice en la lista. Así es como Terraform inicialmente considera las cuatro instancias Droplet:

Index Number 0 1 2 3
Droplet Name first second third fourth

Cuando se agrega un nuevo Droplet llamado cero al comienzo, su representación interna en listas se ve así:

Index Number 0 1 2 3 4
Droplet Name zero first second third fourth

Los cuatro Droplets originales se desplazan una posición hacia la derecha. Luego, Terraform compara dos estados representados en tablas: en la posición 0, el Droplet era llamado primero, y porque es diferente en la segunda tabla, planea una acción de actualización. Esto continúa hasta la posición 4, que no tiene un elemento comparable en la primera tabla, sino una acción de provisión de Droplet.

Lo que esto significa es que agregar un nuevo elemento a la lista en cualquier lugar excepto al final resultaría en que se modifiquen recursos cuando no se necesitan. Las acciones de actualización similares serían planificadas si se eliminara un elemento de la lista droplet_names.

El principal problema de usar count para desplegar una cantidad dinámica y diferente de instancias iguales de la misma instancia es el mal manejo de seguimiento de recursos. Para un número constante de instancias constantes del mismo recurso, count es una solución simple que funciona bien. En situaciones como esta, sin embargo, cuando algunos atributos se obtienen de una variable, el bucle for_each, que te presentaré más tarde en este tutorial, es una elección mucho mejor.

Referenciar el Recurso Actual (self)

Otra desventaja de count es que no sea posible referenciar arbitrariamente una instancia de un recurso por su índice en algunos casos.

El ejemplo principal son los provisionalistas de tiempo de destrucción, que se ejecutan cuando se planea destruir el recurso. La razón es que la instancia solicitada ya no existe (ya ha sido destruida) o crearía un ciclo de dependencias mutuas. En tales situaciones, en lugar de referirse a la instancia de la lista a través de índice, puedes acceder solo al recurso actual mediante la palabra clave self.

Para demostrar su uso, ahora agregará un proveedor de tiempo local de destrucción al definición de test_droplet, el cual mostrará una mensaje cuando se ejecute. Abre droplets.tf para editar:

  1. nano droplets.tf

Agregue las líneas resaltadas siguientes:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = length(var.droplet_names)
  image  = "ubuntu-20-04-x64"
  name   =  var.droplet_names[count.index]
  region = "fra1"
  size   = "s-1vcpu-1gb"

  provisioner "local-exec" {
    when    = destroy
    command = "echo 'Droplet ${self.name} is being destroyed!'"
  }
}

Guarde y cierre el archivo.

El proveedor de ejecución local ejecuta una comanda en la máquina donde está corriendo Terraform. Porque la propiedad when está establecida en destroy, solo se ejecutará cuando la recurrencia será destruida. La comanda que se ejecuta muestra una cadena al stdout, la cual sustituye el nombre de la recurrencia actual utilizando self.name.

Porque crearás los Dropletos de manera diferente en la sección siguiente, destruirá los Dropletos actualmente implementados mediante la siguiente comanda:

  1. terraform destroy -var "do_token=${DO_PAT}"

Entrar cuando se le pregunte. Recibirás la ejecución del proveedor de ejecución local cuatro veces:

Output
... digitalocean_droplet.test_droplet[0] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet first is being destroyed!'"] digitalocean_droplet.test_droplet[1] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet second is being destroyed!'"] digitalocean_droplet.test_droplet[1] (local-exec): Droplet second is being destroyed! digitalocean_droplet.test_droplet[2] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet third is being destroyed!'"] digitalocean_droplet.test_droplet[2] (local-exec): Droplet third is being destroyed! digitalocean_droplet.test_droplet[3] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet fourth is being destroyed!'"] digitalocean_droplet.test_droplet[3] (local-exec): Droplet fourth is being destroyed! digitalocean_droplet.test_droplet[0] (local-exec): Droplet first is being destroyed! ...

En este paso, aprendió las desventajas de count. Ahora aprenderá sobre la construcción de bucle for_each, que supera sus limitaciones y funciona con un mayor número de tipos de variables.

Looping Usando for_each

En esta sección, considerará el bucle for_each, su sintaxis, y cómo ayuda a la flexibilidad al definir recursos con múltiples instancias.

for_each es un parámetro disponible en cada recurso, pero a diferencia de count, que requiere un número de instancias para crear, for_each acepta un mapa o un conjunto. Cada elemento de la colección proporcionada se recorre una sola vez y se crea una instancia para él. for_each hace que la clave y el valor estén disponibles bajo la palabra clave each como atributos (el par de clave y valor como each.key y each.value, respectivamente). Cuando se proporciona un conjunto, la clave y el valor serán los mismos.

Debido a que proporciona el elemento actual en el objeto each, no tendrá que acceder manualmente al elemento deseado como lo hacía con las listas. En el caso de conjuntos, eso ni siquiera es posible, ya que internamente no tiene un orden observable. También se pueden pasar listas, pero primero deben convertirse en un conjunto usando la función toset.

La principal ventaja de usar for_each, además de poder enumerar todos los tres tipos de datos de colección, es que solo se modificarán, crearán o eliminarán los elementos afectados. Si cambia el orden de los elementos en la entrada, no se planificarán acciones, y si agrega, elimina o modifica un elemento de la entrada, se planificarán acciones apropiadas solo para ese elemento.

Vamos a convertir el recurso Droplet de count a for_each y ver cómo funciona en la práctica. Abra droplets.tf para editarlo ejecutando:

  1. nano droplets.tf

Modifique las líneas destacadas:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  for_each = toset(var.droplet_names)
  image    = "ubuntu-20-04-x64"
  name     = each.value
  region   = "fra1"
  size     = "s-1vcpu-1gb"
}

Puede eliminar el proveedor local-exec. Cuando haya terminado, guarde y cierre el archivo.

La primera línea reemplaza count y invoca for_each, pasando la lista de nombres de Droplet en forma de conjunto usando la función toset. Para el nombre de Droplet especificas each.value, que contiene el valor del elemento actual del conjunto de nombres de Droplet proporcionado.

Planificar el proyecto ejecutando:

  1. terraform plan -var "do_token=${DO_PAT}"

El resultado detallará las acciones que Terraform tomaría:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet["primero"] será creado + resource "digitalocean_droplet" "test_droplet" { ... + name = "first" ... } # digitalocean_droplet.test_droplet["cuarto"] será creado + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... } # digitalocean_droplet.test_droplet["segundo"] será creado + resource "digitalocean_droplet" "test_droplet" { ... + name = "second" ... } # digitalocean_droplet.test_droplet["tercero"] será creado + resource "digitalocean_droplet" "test_droplet" { ... + name = "third" ... } # digitalocean_droplet.test_droplet["cero"] será creado + resource "digitalocean_droplet" "test_droplet" { ... + name = "zero" ... } Plan: 5 to add, 0 to change, 0 to destroy. ...

En contraposición al uso de count, Terraform ahora considera cada instancia individualmente, y no como elementos de una lista ordenada. Cada instancia está ligada a un elemento del conjunto proporcionado, como se indica por el elemento de cadena mostrado junto a cada recurso que será creado.

Aplique el plan al clúster mediante:

  1. terraform apply -var "do_token=${DO_PAT}"

Escriba cuando se le pregunte. Cuando termine, se eliminará uno de los elementos de la lista droplet_names para demostrar que otras instancias no serán afectadas. Abre variables.tf para editar:

  1. nano variables.tf

Modifica la lista para que se vea así:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

Guardar y cerrar el archivo.

Planee el proyecto de nuevo, y recibirás el siguiente resultado:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - destroy Terraform will perform the following actions: # El droplet de DigitalOcean ["cero"] será destruido - resource "digitalocean_droplet" "test_droplet" { ... - name = "zero" -> null ... } Plan: 0 to add, 0 to change, 1 to destroy. ...

Esta vez, Terraform destruiría solo la instancia eliminada (cero), y no tocaría ninguna de las otras instancias, lo que es el comportamiento correcto.

En este paso, has aprendido sobre for_each, cómo usarlo, y sus ventajas frente a count. Ahora, aprenderás sobre la sentencia for, su sintaxis y su uso, y cuándo puede utilizarse para automatizar ciertas tareas.

Looping Using for

El bucle for funciona con colecciones y crea una nueva colección aplicando una transformación a cada elemento de la entrada. El tipo exacto de salida dependerá si el bucle está rodeado de paréntesis ([]) o corchetes ({}), que dan una lista o un mapa respectivamente. Por lo tanto, es adecuado para consultar recursos y formar salidas estructuradas para posterior procesamiento.

La sintaxis general del bucle for es:

for element in collection:
transform(element)
if condition

Similar a otras lenguajes de programación, primero nombre la variable de recorrido (element) y especifica la colección a enumerar. El cuerpo del bucle es el paso de transformación, y la cláusula opcional if se puede usar para filtrar la colección de entrada.

Ahora trabajaremos con algunos ejemplos utilizando salidas. Guardarás estos en un archivo llamado outputs.tf. Crearlo para editar lo haces con la siguiente orden:

  1. nano outputs.tf

Agrega las siguientes líneas para guardar pares de nombres de los elementos deployados y sus direcciones IP:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = {
    for instance in digitalocean_droplet.test_droplet:
    instance.name => instance.ipv4_address
  }
}

Este código especifica una salida llamada ip_addresses, y especifica un bucle for que itera sobre las instancias de la resource_test_droplet que has personalizado en los pasos anteriores. Porque el bucle está encerrado entre llaves, su salida será un mapa. La transformación para mapas es similar a funciones lambda en otros lenguajes de programación, y aquí crea un par clave-valor combinando el nombre de la instancia como clave con su IP privada como valor.

Guarde y cierre el archivo, luego refresca el estado de Terraform para tener en cuenta la nueva salida dejando ejecutar:

  1. terraform refresh -var "do_token=${DO_PAT}"

El comando Terraform refresh actualiza el estado local con el estado real de la infraestructura en la nube.

Luego, verifique el contenido de las salidas:

Output
ip_addresses
= { "first" = "ip_address" "fourth" = "ip_address" "second" = "ip_address" "third" = "ip_address" }

Terraform ha mostrado el contenido de la salida ip_addresses, que es un mapa construido por el bucle for. La orden de las entradas puede ser diferente para ti. El bucle funcionará sin problemas con cualquier número de elementos—lo que significa que si agregas una nueva entrada al lista droplet_names, el nuevo Dropletto se crearía sin más intervención manual y también aparecería en esta salida automáticamente.

Al encerrar el bucle en corchetes cuadrados, puedes hacer que la salida sea una lista. Por ejemplo, podrías mostrar solo las direcciones IP del Droplet, lo que es útil para software externo que pueda analizar la información. El código se vería así:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
  ]
}

Aquí, el paso de transformación selecciona el atributo de dirección IP. Proporcionará la siguiente salida:

Output
ip_addresses
= [ "ip_address", "ip_address", "ip_address", "ip_address", ]

Como se indicó antes, también puedes filtrar la colección de entradas usando la cláusula if. Aquí está cómo escribir el bucle para filtrar por la región fra1:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
    if instance.region == "fra1"
  ]
}

En HCL, el operador == comprueba la igualdad de los valores de las dos partes—aquí se chequea si instance.region es igual a fra1. Si es así, la condición pasa y la transfomación de instance se agrega a la salida, de otra manera se omite. La salida de este código sería la misma que en el ejemplo anterior, porque todos los instancias de Droplet están en la región fra1, según la definición de recurso test_droplet. La condicional if también es útil cuando quieras filtrar la colección de entradas por otros valores en tu proyecto, como el tamaño del Droplet o la distribución.

Porque vas a crear recursos de manera diferente en la sección siguiente, destruye los que están deployados ejecutando el siguiente comando:

  1. terraform destroy -var "do_token=${DO_PAT}"

Escribe cuando se te pregunte para finalizar el proceso.

Hemos explorado el bucle for, su sintaxis y ejemplos de uso en salidas. Ahora aprenderás sobre condicionales y cómo pueden utilizarse juntamente con count.

Directivas y Condicionales

En una de las secciones anteriores, has visto la clave count y cómo funciona. Ahora aprenderás sobre operadores condicionales ternarios, que también puedes usar más adelante en tu código de Terraform.

La sintaxis del operador ternario es:

condition ? value_if_true : value_if_false

condición es una expresión que computa a un booleano (verdadero o falso). Si la condición es verdadera, entonces la expresión se evalúa como valor_si_verdadero. Por el contrario, si la condición es falsa, el resultado será valor_si_falso.

El principal uso de operadores ternarios es permitir o deshabilitar la creación de un solo recurso según el contenido de una variable. Esto se logra pasando el resultado de la comparación (ya sea 1 o 0) al valor de la clave count en el recurso deseado.

En el caso de que estés usando el operador ternario para obtener un solo elemento de una lista o conjunto, puedes utilizar la función one. Si la colección proporcionada está vacía, devuelve null. De lo contrario, devuelve el único elemento en la colección, o lanza un error si hay múltiples elementos.

Antes de empezar, abra variables.tf para editar:

  1. nano variables.tf

Agregue las líneas resaltadas:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

variable "create_droplet" {
  type = bool
  default = true
}

Este código define la variable create_droplet, la cual controlará si se creará un Droplet. Primero, abra droplets.tf para editar:

Modifique su archivo como siguiente:

  1. nano droplets.tf

Para count, usa un operador ternario para retornar 1 si la variable create_droplet es verdadera, o 0 si es falso, lo que resultará en que no se provisionen ningún Droplet. Guarde y cierre el archivo cuando haya terminado.

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = var.create_droplet ? 1 : 0
  image  = "ubuntu-20-04-x64"
  name   =  "test_droplet"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Luego, modifique la declaración de Droplet ejecutando:

Obtendrás la siguiente salida:

  1. terraform plan -var "do_token=${DO_PAT}" -var "create_droplet=false"

Debido a que create_droplet fue pasado con un valor de false, el count de instancias es 0, por lo que no se crearán Droplet, por lo que no habrá direcciones IP para mostrar.

Output
Changes to Outputs: + ip_addresses = {} You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Ya has visto cómo usar el operador condicional ternario junto con la clave count para habilitar una flexibilidad más alta en la elección de si desplegar los recursos deseados. Siguiente, aprenderás sobre establecer dependencias explícitas para tus recursos.

Estableciendo dependencias explícitas de recursos

Mientras se crea el plan de ejecución de tu proyecto, Terraform detecta cadenas de dependencia entre recursos y las ordena automáticamente de manera que se construirán en el orden correcto. En la mayoría de casos, Terraform puede detectar relaciones mediante la búsqueda de todas las expresiones en los recursos y construir un gráfico.

Por ejemplo, cuando un recurso necesita acceso de control de acceso ya provisto en el proveedor de nube para poder ser provisionado, no hay signo claro a Terraform de que sean relaciones entre ellos en términos de comportamiento. En consecuencia, Terraform no sabrá que son dependientes de otra manera. En tales casos, la dependencia debe especificarse manualmente usando el argumento depends_on.

El valor depends_on está disponible en cada recurso y se utiliza para especificar las relaciones ocultas de dependencia entre recursos específicos. Las relaciones ocultas de dependencia se forman cuando un recurso depende de otro sin usar ninguna de sus datos en su declaración, lo que provocaría que Terraform les conectara de una manera diferente.

Aquí es un ejemplo de cómo se especifica depends_on en código:

resource "digitalocean_droplet" "droplet" {
  image  = "ubuntu-20-04-x64"
  name   = "web"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  depends_on = [
    # Recursos...
  ]
}

Acepta una lista de referencias a otros recursos, y no acepta expresiones arbitrarias.

depends_on debe ser usado con precaución, y solo cuando todas las otras opciones estén agotadas. Su uso indica que lo que intentas declarar está saliendo fuera de los límites del sistema de detección automática de dependencias de Terraform; podría significar que el recurso que estás tratando de declarar está pasándose por encima de los límites de la infraestructura automáticamente detectable.

Ahora has aprendido cómo establecer dependencias adicionales para un recurso utilizando la clave depends_on. Y sólo cuando se hayan agotado todas las otras opciones.

Conclusión

En este artículo, hemos revisado características de HCL que mejoran la flexibilidad y escalabilidad de tu código, como count para especificar el número de instancias de recursos a deployar y for_each como forma avanzada de iterar sobre tipos de colecciones y personalizar instancias. Cuando se usan correctamente, reducen considerablemente la duplicación de código y la sobrecarga operacional de administrar la infraestructura implementada.

También has aprendido sobre condicionales y operadores ternarios, y cómo pueden utilizarse para controlar si un recurso será deployado. Mientras que el análisis automático de dependencias de Terraform es bastante capaz, hay casos en los que podrías necesitar especificar manualmente dependencias de recursos usando la clave depends_on.

Este tutorial forma parte de la serie Cómo Gestionar Infraestructuras con Terraform. La serie cubre una variedad de temas de Terraform, desde la instalación de Terraform por primera vez hasta la gestión de proyectos complejos.

Source:
https://www.digitalocean.com/community/tutorials/how-to-improve-flexibility-using-terraform-variables-dependencies-and-conditionals