Terraform Associate

Deep Dive

Practicar ahora
D4 · Configuración de Terraform

Expresiones, funciones y meta-argumentos

Count y for_each crean múltiples instancias de un recurso. Las expresiones for y los condicionales permiten transformar datos. Las funciones built-in son herramientas esenciales para el trabajo diario y el examen.

Meta-argumento count

count crea N instancias del recurso, indexadas de 0 a N-1. Cada instancia es identificada por su índice numérico.

# Crear 3 instancias EC2
resource "aws_instance" "web" {
  count         = 3                       # número entero
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name = "web-${count.index}"          # count.index: 0, 1, 2
  }
}

# Dirección de cada instancia:
# aws_instance.web[0]
# aws_instance.web[1]
# aws_instance.web[2]

# Acceso en outputs o referencias:
output "web_ids" {
  value = aws_instance.web[*].id         # splat: lista de todos los IDs
}

# Usar variable para el conteo:
variable "instance_count" { default = 3 }

resource "aws_instance" "app" {
  count = var.instance_count
  # ...
}

# Creación condicional (count = 0 = no crear, count = 1 = crear):
resource "aws_eip" "nat" {
  count  = var.create_nat_gateway ? 1 : 0
  domain = "vpc"
}

Meta-argumento for_each

for_each crea una instancia por cada elemento de un set o map. Cada instancia es identificada por su clave (string), no por un índice numérico.

# for_each con un SET de strings
resource "aws_iam_user" "team" {
  for_each = toset(["alice", "bob", "carol"])   # convierte lista a set
  name     = each.key                           # each.key == el elemento del set
}

# Dirección de cada instancia:
# aws_iam_user.team["alice"]
# aws_iam_user.team["bob"]
# aws_iam_user.team["carol"]

# for_each con un MAP (clave → objeto)
variable "buckets" {
  default = {
    logs    = { region = "us-east-1", versioning = true }
    backups = { region = "us-west-2", versioning = false }
  }
}

resource "aws_s3_bucket" "storage" {
  for_each = var.buckets
  bucket   = "empresa-${each.key}"             # each.key = "logs" o "backups"
}

resource "aws_s3_bucket_versioning" "storage" {
  for_each = var.buckets
  bucket   = aws_s3_bucket.storage[each.key].id  # referencia con la misma clave

  versioning_configuration {
    status = each.value.versioning ? "Enabled" : "Suspended"
  }
}

# Splat no funciona con for_each — usar for expression:
output "bucket_ids" {
  value = { for k, v in aws_s3_bucket.storage : k => v.id }
}

count vs for_each

Característicacountfor_each
Tipo de valor aceptadonumberset(string) o map(any)
Identificador de instanciaÍndice numérico (0, 1, 2…)Clave string ("alice", "logs"…)
Dirección en staterecurso[0], recurso[1]recurso["clave"]
Acceso al elementocount.index (entero)each.key (clave) + each.value (valor)
Qué pasa al borrar el 2° de 3⚠️ Terraform renombra el 3° al 2° y DESTRUYE+RECREA✅ Solo destruye el elemento borrado, los demás no cambian
Cuándo usarRecursos idénticos sin identidad propia (réplicas)Recursos con identidad única (usuarios, buckets con nombre)
RecomendaciónCasos simples o creación condicional (0 o 1)Preferido para la mayoría de casos de múltiples recursos

⚠️ El problema de count con listas cambiantes

Si tienes count = 3 con instancias [0]=alice, [1]=bob, [2]=carol y eliminas bob (elemento del medio), Terraform ve: [0]=alice, [1]=carol. Como el [1] cambió de "bob" a "carol", destruye y recrea el recurso [1]. Con for_each, cada recurso tiene una clave estable y eliminar "bob" solo destruye ese recurso sin afectar a los demás.

Expresiones for y condicionales

# Expresión for — genera listas o mapas
# [for <item> in <coleccion> : <expresion>]
# {for <key>, <val> in <mapa> : <nueva_key> => <nuevo_val>}

variable "users" {
  default = [
    { name = "alice", admin = true },
    { name = "bob",   admin = false },
  ]
}

# Lista de nombres
locals {
  names = [for u in var.users : u.name]
  # result: ["alice", "bob"]

  # Solo los admins (with if condition)
  admin_names = [for u in var.users : u.name if u.admin]
  # result: ["alice"]

  # Mapa nombre → admin
  user_map = {for u in var.users : u.name => u.admin}
  # result: {alice = true, bob = false}
}

# Expresión condicional (ternario)
# condition ? valor_si_true : valor_si_false
resource "aws_instance" "web" {
  instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
}

💡 For expressions vs for_each

Las expresiones for ([for x in list : x.name]) transforman colecciones en locales o valores — no crean recursos. El meta-argumento for_each crea múltiples instancias de un recurso. Son conceptos diferentes aunque usan palabras similares.

Funciones built-in por categoría

Terraform incluye decenas de funciones built-in. Estas son las más importantes para el examen, organizadas por categoría.

String

format(fmt, ...args)

Formatea un string tipo printf: format("Hello, %s!", "world")

join(sep, list)

Une una lista en string: join(",", ["a","b"]) → "a,b"

split(sep, str)

Divide string en lista: split(",", "a,b") → ["a","b"]

upper(str) / lower(str)

Convierte a mayúsculas/minúsculas

trimspace(str)

Elimina espacios y saltos de línea al inicio y final

replace(str, sub, new)

Reemplaza subcadena: replace("hello", "l", "r") → "herro"

Colección

length(list|map|str)

Número de elementos: length(["a","b"]) → 2

concat(list1, list2)

Combina listas: concat(["a"],["b"]) → ["a","b"]

flatten(list)

Aplana listas anidadas: flatten([["a"],["b","c"]]) → ["a","b","c"]

merge(map1, map2)

Combina mapas (map2 sobrescribe claves de map1)

lookup(map, key, def)

Busca clave en mapa con default: lookup({a=1}, "b", 0) → 0

toset(list)

Convierte lista a set (elimina duplicados, necesario para for_each)

keys(map) / values(map)

Lista de claves o valores de un mapa

contains(list, val)

True si la lista contiene el valor: contains(["a","b"],"a") → true

Codificación

jsonencode(value)

Convierte valor HCL a string JSON: jsonencode({a="b"}) → {"a":"b"}

jsondecode(str)

Parsea string JSON a tipo HCL

base64encode(str)

Codifica string en Base64

base64decode(str)

Decodifica string Base64

file(path)

Lee el contenido de un archivo como string

templatefile(path, vars)

Renderiza template .tftpl con variables

Numérico y otros

max(n1, n2, ...) / min(...)

Máximo o mínimo de los números dados

abs(n)

Valor absoluto

ceil(n) / floor(n)

Redondeo hacia arriba / hacia abajo

coalesce(v1, v2, ...)

Primer valor no nulo y no vacío de la lista

try(expr, fallback)

Evalúa expr; si falla, devuelve fallback (útil para atributos opcionales)

can(expr)

True si expr no genera error (para validación)

¿Entendiste este tema?

Pon a prueba lo que acabas de aprender

Un módulo gestiona 3 subnets con for_each usando las claves 'public-a', 'public-b' y 'private-a'. El equipo decide eliminar 'public-b' del mapa. ¿Qué ocurrirá en el siguiente terraform apply?