Módulo 3: The Iac Pipeline Fmt Validate Plan

3. Instalando Terraform dentro de un runner efímero

Descripción

En terraform-and-iac-guide instalaste Terraform una sola vez, en tu laptop, y ahí se quedó — cada terraform plan que corriste después, en cualquier lección de esa guía, encontró el binario ya instalado, exactamente donde lo dejaste. Esta lección te muestra por qué eso no funciona igual dentro de un job de GitHub Actions (real o simulado con act): cada corrida arranca desde un contenedor limpio, sin Terraform, sin nada que hayas instalado en una corrida anterior. Vas a confirmar esto con tus propios ojos —un comando que busca terraform antes de instalarlo, y lo encuentra recién después— y vas a conocer la pieza que resuelve esto en cada corrida: hashicorp/setup-terraform@v3, la Action oficial de HashiCorp, pineada a la versión exacta que este módulo usa de aquí en adelante.

Conexión con el módulo

La lección 2 te dio el patrón completo (ci.yml calcula, apply.yml aplica). Esta lección resuelve un prerequisito técnico de ambos: ninguno de los dos puede correr un solo comando de Terraform si el runner no tiene el binario instalado — y, a diferencia de tu laptop, un runner no lo recuerda de una corrida a la siguiente. Las lecciones 4, 5 y 6 —donde fmt, validate y plan corren de verdad dentro de act— dependen directamente de lo que instalas aquí.


Analogía: un taller que se desarma después de cada trabajo, no una caja de herramientas fija

Tu laptop, en terraform-and-iac-guide, es un taller permanente: instalaste el banco de trabajo, colgaste las herramientas en la pared, y al día siguiente todo sigue exactamente donde lo dejaste. Un runner de GitHub Actions —real o simulado con act— es lo opuesto: un taller de alquiler por hora que se desarma por completo al final de cada trabajo y se vuelve a armar desde cero para el siguiente. Si necesitas un martillo, no puedes asumir que "ya está ahí porque lo usaste ayer" — tienes que traerlo tú mismo, cada vez, como parte del trabajo. hashicorp/setup-terraform@v3 es, literalmente, el paso donde traes esa herramienta al taller recién armado, al principio de cada corrida — nunca asumida, siempre explícita.


Confirmándolo con tus propios ojos: el runner no recuerda nada

Antes de construir ci.yml, vale la pena ver este hecho de forma directa, sobre un workflow mínimo, desechable, fuera de andes-cargo-infra/:

name: tf-version-demo

on: workflow_dispatch

jobs:
  show-terraform-version:
    runs-on: ubuntu-latest
    steps:
      - name: Check for terraform before setup
        run: |
          which terraform || echo "terraform not found in this fresh container"

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.15.8"

      - name: Show terraform version
        run: terraform version
act workflow_dispatch -j show-terraform-version

Qué esperar (salida literal, ejecutada para escribir esta lección):

[tf-version-demo/show-terraform-version] ⭐ Run Main Check for terraform before setup
[tf-version-demo/show-terraform-version]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/0] user= workdir=
[tf-version-demo/show-terraform-version]   | terraform not found in this fresh container
[tf-version-demo/show-terraform-version]   ✅  Success - Main Check for terraform before setup [48.184584ms]
[tf-version-demo/show-terraform-version] ⭐ Run Main Set up Terraform
[tf-version-demo/show-terraform-version]   🐳  docker cp src=/Users/.../hashicorp-setup-terraform@v3/ dst=/var/run/act/actions/hashicorp-setup-terraform@v3/
[tf-version-demo/show-terraform-version]   🐳  docker exec cmd=[/opt/acttoolcache/node/24.19.0/arm64/bin/node /var/run/act/actions/hashicorp-setup-terraform@v3/dist/index.js] user= workdir=
[tf-version-demo/show-terraform-version]   | [command]/usr/bin/unzip -o -q /tmp/af7217a0-6559-4fde-bd65-19b381b0d85c
[tf-version-demo/show-terraform-version]   ✅  Success - Main Set up Terraform [2.287155375s]
[tf-version-demo/show-terraform-version] ⭐ Run Main Show terraform version
[tf-version-demo/show-terraform-version]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/2] user= workdir=
[tf-version-demo/show-terraform-version]   | Terraform v1.15.8
[tf-version-demo/show-terraform-version]   | on linux_arm64
[tf-version-demo/show-terraform-version]   ✅  Success - Main Show terraform version [475.684125ms]
[tf-version-demo/show-terraform-version] 🏁  Job succeeded

Lee las tres líneas clave en orden: terraform not found in this fresh container (antes de que corra setup-terraform, el binario simplemente no existe en esta imagen — la imagen catthehacker/ubuntu:act-latest que elegiste en el Módulo 1 no lo trae preinstalado, a propósito, según la advertencia de su propia documentación) → [command]/usr/bin/unzip -o -q ... (setup-terraform descarga y descomprime el binario exacto de la versión que pediste) → Terraform v1.15.8 on linux_arm64 (recién ahora el comando terraform existe y responde). Si corrieras este mismo workflow una segunda vez, en una segunda corrida, verías exactamente la misma secuencia — terraform not found de nuevo al principio — porque cada corrida es un contenedor nuevo, sin memoria de la corrida anterior.


hashicorp/setup-terraform@v3, la pieza que resuelve esto

- name: Set up Terraform
  uses: hashicorp/setup-terraform@v3
  with:
    terraform_version: "1.15.8"

Ya conoces uses/with del Módulo 2 (lección 5), donde viste actions/checkout@v4 y nombraste hashicorp/setup-terraform@v3 como la siguiente Action que ibas a usar. Esta lección la pone en uso: hashicorp/setup-terraform es la Action oficial, mantenida por HashiCorp, que descarga el binario de terraform para la versión que le pidas y lo agrega al PATH del runner —el mecanismo exacto que viste en la salida de arriba: TERRAFORM_CLI_PATH y un add-path al final del step—. A partir de ese punto, cualquier step siguiente en el mismo job puede invocar terraform como si estuviera preinstalado.

Por qué terraform_version: "1.15.8", exacto, no un rango

terraform-and-iac-guide fijó required_version = ">= 1.15.0" en el versions.tf de Andes Cargo —un mínimo, no una versión exacta, para darle margen a parches futuros sin romper—. Aquí, en cambio, terraform_version en setup-terraform se fija a "1.15.8", exacto: la misma versión que instalaste en tu laptop en esa guía, y la que satisface sin ambigüedad el required_version del proyecto. La diferencia de propósito importa: required_version en el HCL es una restricción que Terraform mismo valida ("no corras con una versión más vieja que esta"); terraform_version en setup-terraform es una instrucción de instalación ("descarga exactamente esta"). Fijar un número exacto en el runner —en vez de dejar que setup-terraform instale "la más reciente" (su comportamiento si omites este parámetro)— es la misma disciplina de reproducibilidad que ya viste con .actrc pineando la imagen del runner en el Módulo 1: cada corrida de ci.yml, hoy o dentro de seis meses, instala la misma versión exacta, sin sorpresas.


Profundización: qué SÍ persiste entre corridas, y qué no

Vale la pena ser preciso sobre qué exactamente desaparece entre una corrida y la siguiente, porque no es "absolutamente todo":

  • No persiste: el sistema de archivos del contenedor del job completo —cualquier binario que instalaste con un run: (como terraform, awslocal, tflocal en las próximas lecciones), cualquier archivo que hayas creado a mano dentro del job que no forme parte de tu repositorio ni se haya subido explícitamente como artefacto.
  • Si persiste (porque no vive en el contenedor del job): el propio código de tu repositorio —lo trae actions/checkout en cada corrida, desde Git, no desde el contenedor anterior—; el estado de Terraform, si usaras un backend remoto (no es el caso de esta guía, que usa backend local dentro del propio checkout — el Módulo 4 de terraform-and-iac-guide cubre esa distinción a fondo); y cualquier artefacto que un job suba explícitamente con actions/upload-artifact para que otro job lo descargue —el mecanismo exacto que el Módulo 5 de esta guía usa para pasar el plan de ci.yml a apply.yml—.

La regla simple: si no lo trajiste con checkout, no lo descargaste con una Action de artefactos, y no lo instalaste en esta misma corrida, no existe. setup-terraform existe precisamente porque el binario de Terraform cae en la categoría de "hay que instalarlo cada vez".


Errores comunes

Omitir terraform_version y asumir que instala "la que uses tú" (de configuración). Qué pasa: alguien escribe uses: hashicorp/setup-terraform@v3 sin ningún with:, asumiendo que la Action detecta y respeta el required_version del versions.tf del proyecto. Por qué pasa: parece razonable que la Action "lea" la configuración del proyecto antes de instalar algo. Cómo detectarlo: si tu ci.yml no tiene un terraform_version explícito y te sorprende qué versión terminó instalada. Cómo corregirlo: setup-terraform no lee tu HCL — sin terraform_version explícito, instala la versión más reciente disponible en el momento de la corrida, que podría no coincidir con la que usas en tu laptop. Fija siempre terraform_version de forma explícita, igual que fijaste la imagen del runner en .actrc.

Confundir required_version (en el HCL) con terraform_version (en el YAML) como si fueran lo mismo (conceptual). Qué pasa: alguien piensa que cambiar uno automáticamente actualiza el otro, o que solo hace falta declarar uno de los dos. Cómo detectarlo: si editas versions.tf esperando que ci.yml instale una versión distinta sin tocar el YAML. Cómo corregirlo: son dos mecanismos independientes con propósitos distintos — required_version es una restricción mínima que Terraform valida en tiempo de ejecución; terraform_version es una instrucción de instalación que setup-terraform ejecuta antes de que Terraform corra siquiera. Mantenerlos consistentes (la versión instalada satisface el mínimo requerido) es responsabilidad de quien escribe el YAML, no algo automático.

Esperar que un run: terraform ... funcione en un step anterior a Set up Terraform (de orden). Qué pasa: alguien reordena los steps de ci.yml y pone un comando de Terraform antes del step uses: hashicorp/setup-terraform@v3. Cómo detectarlo: un error del tipo terraform: command not found en un step que debería funcionar. Cómo corregirlo: recuerda que steps es una lista ordenada (Módulo 2, lección 2) — setup-terraform tiene que aparecer, en el archivo, antes de cualquier step que invoque terraform, terraform fmt, terraform validate o tflocal.


Ejercicios

Ejercicio 1 — Predice el resultado de una segunda corrida. Si corrieras el workflow tf-version-demo.yml de esta lección una segunda vez, inmediatamente después de la primera, sin cambiar nada, ¿esperarías que el primer step (Check for terraform before setup) volviera a imprimir terraform not found? Justifica.

Ver solución

Sí, exactamente el mismo resultado. Cada corrida de act (y cada corrida de un job real en GitHub Actions) arranca un contenedor nuevo, basado en la imagen catthehacker/ubuntu:act-latest sin modificar — nada de lo que instalaste en la corrida anterior sobrevive. La segunda corrida no "recuerda" que ya instalaste Terraform la primera vez; vuelve a empezar desde cero, exactamente como la primera.

Ejercicio 2 — Explica la diferencia entre required_version y terraform_version a un colega. Sin repetir las definiciones exactas de la lección, explica en dos frases por qué un proyecto necesita ambos, y qué pasaría si solo tuviera uno.

Ver solución

Una respuesta completa suena, más o menos, así: "required_version, en el HCL, es el proyecto diciendo 'no me ejecutes con una versión de Terraform más vieja que esta' — protege contra usar una versión incompatible. terraform_version, en el YAML del workflow, es el paso que instala Terraform en el runner antes de que nada más pueda correr — sin eso, no hay ningún Terraform instalado que pueda siquiera revisar el required_version. Si solo tuvieras required_version sin terraform_version explícito en el workflow, setup-terraform instalaría 'lo más reciente' por defecto, que hoy probablemente satisface el mínimo, pero no es una garantía a largo plazo."

Ejercicio 3 — Diagnostica un terraform: command not found. Un colega te muestra un job de ci.yml que falla con terraform: command not found en un step llamado Terraform format check. Sin ver el resto del archivo, ¿qué es lo primero que revisarías, según lo que aprendiste en "Errores comunes"?

Ver solución

Revisarías si el step uses: hashicorp/setup-terraform@v3 aparece antes, en el orden del archivo, que el step Terraform format check. Como steps es una lista ordenada y cada job arranca sin Terraform instalado, cualquier step que invoque terraform tiene que venir, literalmente, después del step que lo instala — si el orden está invertido, o si el step de instalación falta por completo, el error command not found es exactamente lo que esperarías ver.


Resumen y siguiente paso

En esta lección confirmaste, con un comando que busca terraform antes y después de instalarlo, que un runner —real o simulado con act— no recuerda nada entre corridas: cada job arranca desde un contenedor limpio, sin ninguna herramienta que hayas instalado en una corrida anterior. hashicorp/setup-terraform@v3, pineado a terraform_version: "1.15.8" —la misma versión exacta que instalaste en terraform-and-iac-guide—, es la pieza que trae Terraform a ese runner limpio, en cada corrida, sin excepción.

Antes de avanzar deberías poder: explicar por qué un runner no se comporta como tu laptop en cuanto a herramientas instaladas; distinguir required_version (HCL) de terraform_version (YAML) sin confundirlos; y ubicar correctamente setup-terraform en el orden de steps de un job.

Con Terraform instalable en cada corrida, la lección 4 usa exactamente esta pieza para construir los dos primeros steps reales de ci.yml: terraform fmt -check y terraform validate, corridos dentro de act, con un error introducido a propósito para ver el job fallar en rojo.

Recursos

  1. GitHub — hashicorp/setup-terraform — el repositorio oficial de la Action usada en esta lección, con la referencia completa de sus parámetros.
  2. nektosact.com — Runners — la documentación oficial que explica por qué las imágenes de act no traen preinstaladas todas las herramientas de un runner real.
  3. terraform-and-iac-guide, Módulo 1, lección 5 (NIEVA) — la instalación de Terraform 1.15.8 en tu laptop, el contraste directo de esta lección.