Introducción

A lo largo de los módulos anteriores configuraste pipelines de CI/CD manualmente usando Jenkins, GitHub Actions y otras herramientas. Terraform Cloud y Terraform Enterprise son las soluciones gestionadas de HashiCorp que integran todas esas funcionalidades directamente en la plataforma: estado remoto, CI/CD, gestión de variables, control de acceso, políticas de cumplimiento y mucho más, sin necesidad de configurar y mantener infraestructura de CI/CD propia.

Este módulo te enseña cuándo y cómo usar estas plataformas, sus diferencias con Terraform Open Source, y cómo migrar un proyecto existente.


¿Qué es Terraform Cloud?

Terraform Cloud (TFC) es una plataforma SaaS (Software as a Service) de HashiCorp que proporciona:

  • Estado remoto gestionado: almacena y versiona el estado de Terraform de forma segura, sin necesidad de configurar S3 o Azure Blob manualmente.
  • Bloqueo de estado automático: previene ejecuciones concurrentes que podrían corromper el estado.
  • Ejecuciones remotas: los comandos terraform plan y terraform apply se ejecutan en los servidores de HashiCorp (o en tus propios agentes), no en tu máquina local.
  • Gestión de variables: centraliza las variables y credenciales de forma segura con control de acceso por workspace.
  • VCS Integration: conecta directamente con GitHub, GitLab, Bitbucket o Azure DevOps para disparar plans y applies automáticamente al hacer commits.
  • UI web: una interfaz gráfica que muestra el historial de runs, el estado de la infraestructura, logs, y más.
  • API completa: permite integrar Terraform Cloud con cualquier sistema externo.

Planes disponibles

Característica Free Plus Business
Usuarios Hasta 5 Ilimitados Ilimitados
Workspaces Ilimitados Ilimitados Ilimitados
Ejecuciones concurrentes 1 2+ Personalizable
Estado remoto Si Si Si
VCS Integration Si Si Si
Gestión de variables Si Si Si
Equipos y permisos No Si Si
SSO (SAML/OIDC) No Si Si
Política Sentinel No Si Si
Estimación de costos No Si Si
Agentes privados No Si Si
Audit logging No No Si
Soporte SLA No Business hours 24/7
Precio (por usuario/mes) Gratis ~$20 Personalizado

Conceptos clave de Terraform Cloud

Organizations

Una Organization (organización) es el contenedor principal en Terraform Cloud. Agrupa todos los workspaces, equipos y configuraciones de tu empresa o proyecto.

Organización: "mi-empresa"
├── Workspace: "staging-vpc"
├── Workspace: "staging-eks"
├── Workspace: "produccion-vpc"
├── Workspace: "produccion-eks"
└── Teams:
    ├── "devops" (acceso completo a todos los workspaces)
    ├── "developers" (acceso de lectura a staging)
    └── "prod-admins" (acceso de escritura a produccion)

Workspaces

Un Workspace en Terraform Cloud es diferente al workspace local de Terraform. Cada workspace tiene:

  • Su propio estado de Terraform (aislado de otros workspaces)
  • Sus propias variables (Terraform variables y variables de entorno)
  • Su propio historial de runs
  • Su propio conjunto de permisos de acceso

Generalmente, cada workspace corresponde a un ambiente (staging, produccion) o a un componente de infraestructura (vpc, eks, rds).

Teams

Los Teams (equipos) son grupos de usuarios con permisos específicos sobre los workspaces. Los permisos disponibles son:

  • Read: ver el estado y los runs, no puede iniciar runs
  • Plan: puede iniciar terraform plan pero no apply
  • Write: puede iniciar tanto plan como apply
  • Admin: control total sobre el workspace

Tabla: Terraform Cloud vs Terraform OSS

Aspecto Terraform OSS (local/CI) Terraform Cloud
Estado Manual (S3, Azure Blob, GCS, etc.) Automático y gestionado
Bloqueo de estado Manual (DynamoDB, etc.) Automático
Ejecución Local o en CI/CD En servidores de HashiCorp o agentes
Variables y secretos Variables de entorno, tfvars, secretos CI UI centralizada, con control de acceso
Historial de runs Logs del CI/CD UI con historial completo, diffs
Notificaciones Configurado en CI/CD Integrado (email, Slack, webhooks)
Política de cumplimiento Herramientas externas (OPA, etc.) Sentinel integrado
Control de acceso Basado en el sistema CI/CD Teams y permisos nativos
Estimación de costos No disponible nativamente Integrado en el plan
Configuración requerida Alta (backend, CI/CD, secretos) Baja (conectar repo y listo)
Costo Solo infraestructura de estado Suscripción mensual
Vendor lock-in Bajo (portable) Alto (propietario)

Configurar un workspace en Terraform Cloud

Paso 1: Crear una cuenta y organización

  1. Ve a app.terraform.io y crea una cuenta
  2. Crea una organización (ej: mi-empresa)

Paso 2: Crear un workspace

Hay tres tipos de workflows para crear workspaces:

VCS-driven: conectado a un repositorio Git (recomendado para CI/CD) CLI-driven: el plan/apply se inicia desde la línea de comandos local pero se ejecuta remotamente API-driven: el plan/apply se inicia via API (para integración con otros sistemas)

Paso 3: Configurar el backend en tu código Terraform

# versions.tf o backend.tf
terraform {
  # Especificar la versión de Terraform requerida
  required_version = ">= 1.9.0"

  # Configurar el backend de Terraform Cloud
  cloud {
    # Nombre de tu organización en Terraform Cloud
    organization = "mi-empresa"

    workspaces {
      # Nombre del workspace en Terraform Cloud
      name = "staging-infraestructura"

      # Alternativa: usar tags para gestionar múltiples workspaces
      # tags = ["staging", "vpc"]
    }
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

Paso 4: Autenticarse con Terraform Cloud

# Iniciar sesión en Terraform Cloud
terraform login
# Este comando abre el navegador para generar un token de API
# El token se guarda en ~/.terraform.d/credentials.tfrc.json

# Alternativa: configurar el token manualmente
export TF_TOKEN_app_terraform_io="tu-token-de-api"

# O crear el archivo de credenciales manualmente
cat > ~/.terraform.d/credentials.tfrc.json << 'EOF'
{
  "credentials": {
    "app.terraform.io": {
      "token": "tu-token-de-api"
    }
  }
}
EOF

# Inicializar Terraform (detecta la configuración de cloud{})
terraform init
# Output:
# Initializing Terraform Cloud...
# Initializing provider plugins...
# Terraform Cloud has been successfully initialized!

Tipos de ejecución en Terraform Cloud

Terraform Cloud soporta tres modos de ejecución para cada workspace:

Remote (recomendado)

El plan y apply se ejecutan en los workers de Terraform Cloud. El estado se guarda en Terraform Cloud.

# Con modo remote, los comandos normales de Terraform
# se ejecutan remotamente en Terraform Cloud
terraform plan
# Output:
# Running plan in Terraform Cloud. Output will stream here.
# Waiting for the plan to start...
# Plan: 3 to add, 0 to change, 0 to destroy.

Local

El plan y apply se ejecutan localmente, pero el estado se guarda en Terraform Cloud. Útil si necesitas acceder a recursos privados desde tu máquina.

# En la UI de Terraform Cloud: Settings > General > Execution Mode: Local
# O en el código (si usas workspace via API):
resource "tfe_workspace" "ejemplo" {
  name         = "mi-workspace"
  organization = "mi-empresa"

  # Modo de ejecución
  execution_mode = "local"  # O "remote" o "agent"
}

Agent

El plan y apply se ejecutan en un agente que tú despliegas en tu propia infraestructura. Útil para:

  • Acceder a recursos en VPCs privadas
  • Compliance que requiere que el código no salga de tu infraestructura
  • Necesidad de software específico en el agente
# Instalar y ejecutar el agente de Terraform Cloud en tu infraestructura
docker run -e TFC_AGENT_TOKEN=tu-token-de-agente \
           -e TFC_AGENT_NAME=mi-agente-privado \
           hashicorp/tfc-agent:latest

# El agente se registra automáticamente en tu organización
# y puede ejecutar runs de los workspaces configurados para usar agentes

Variables en Terraform Cloud

Terraform Cloud tiene dos tipos de variables:

Terraform Variables

Equivalen a las variables definidas en variables.tf. Se pasan al terraform plan/apply como si fueran archivos .tfvars.

# variables.tf
variable "environment" {
  type        = string
  description = "Ambiente de despliegue"
}

variable "instance_count" {
  type        = number
  description = "Número de instancias"
}

En la UI de Terraform Cloud, configuras estos valores en el workspace:

  • environment = staging
  • instance_count = 2

Environment Variables

Se inyectan como variables de entorno en el runner. Se usan para credenciales de nube:

# Variables de entorno configuradas en Terraform Cloud (sensibles)
AWS_ACCESS_KEY_ID     = AKIAIOSFODNN7EXAMPLE   (Sensitive: ✓)
AWS_SECRET_ACCESS_KEY = wJalrXUtnFEMI/...      (Sensitive: ✓)
AWS_DEFAULT_REGION    = us-east-1

# Variables de Terraform (no sensibles)
TF_VAR_environment    = staging
TF_VAR_instance_count = 2

Variables con la API de Terraform

# Crear una variable en un workspace via API
curl \
  --header "Authorization: Bearer $TFC_TOKEN" \
  --header "Content-Type: application/vnd.api+json" \
  --request POST \
  --data '{
    "data": {
      "type": "vars",
      "attributes": {
        "key": "AWS_ACCESS_KEY_ID",
        "value": "AKIAIOSFODNN7EXAMPLE",
        "category": "env",
        "sensitive": true,
        "description": "AWS Access Key para staging"
      }
    }
  }' \
  https://app.terraform.io/api/v2/workspaces/ws-XXXXXXXX/vars

VCS-driven workflow: conectar un repositorio Git

El workflow dirigido por VCS (Version Control System) es el más popular porque automatiza todo el ciclo de vida de Terraform cuando se hacen cambios en el repositorio.

Configuración

  1. En Terraform Cloud, ve a tu workspace > Settings > Version Control
  2. Conecta tu proveedor VCS (GitHub, GitLab, etc.)
  3. Selecciona el repositorio
  4. Configura las opciones:
Repositorio:        mi-empresa/infraestructura-terraform
Branch:             main
Directorio Terraform: environments/staging
Disparadores:
  ☑ Auto plan en cualquier push al branch
  ☑ Auto apply (opcional, puede requerir aprobación)
  Rutas de archivos disparadoras: environments/staging/**

Funcionamiento del VCS-driven workflow

Developer hace push a un PR:
  1. GitHub notifica a Terraform Cloud via webhook
  2. Terraform Cloud ejecuta terraform plan
  3. El resultado del plan se muestra en el PR como un "check de estado"
  4. Los revisores ven el plan antes de aprobar el PR

Developer aprueba y mergea el PR:
  1. GitHub notifica a Terraform Cloud del push a main
  2. Terraform Cloud ejecuta terraform plan
  3. Si auto-apply está activado: ejecuta terraform apply automáticamente
  4. Si no: el plan queda pendiente de confirmación manual en la UI

API-driven workflow

En el API-driven workflow, los plans y applies se disparan programáticamente via la API de Terraform Cloud. Esto es útil cuando:

  • Quieres integrar Terraform Cloud con un pipeline CI/CD existente
  • Necesitas disparar runs desde scripts externos
  • Quieres automatización personalizada
#!/bin/bash
# Script para disparar un run via API de Terraform Cloud

TFC_TOKEN="tu-token-de-api"
WORKSPACE_ID="ws-XXXXXXXX"
TFC_API="https://app.terraform.io/api/v2"

# 1. Crear un Run en el workspace
RUN_ID=$(curl -s \
  --header "Authorization: Bearer ${TFC_TOKEN}" \
  --header "Content-Type: application/vnd.api+json" \
  --request POST \
  --data "{
    \"data\": {
      \"type\": \"runs\",
      \"attributes\": {
        \"message\": \"Run disparado desde script\",
        \"auto-apply\": false
      },
      \"relationships\": {
        \"workspace\": {
          \"data\": {
            \"type\": \"workspaces\",
            \"id\": \"${WORKSPACE_ID}\"
          }
        }
      }
    }
  }" \
  "${TFC_API}/runs" | jq -r '.data.id')

echo "Run creado: ${RUN_ID}"
echo "Ver en: https://app.terraform.io/app/mi-empresa/runs/${RUN_ID}"

# 2. Esperar a que el plan termine
echo "Esperando resultado del plan..."
while true; do
  STATUS=$(curl -s \
    --header "Authorization: Bearer ${TFC_TOKEN}" \
    "${TFC_API}/runs/${RUN_ID}" | jq -r '.data.attributes.status')

  echo "Estado actual: ${STATUS}"

  if [[ "${STATUS}" == "planned" ]]; then
    echo "Plan completado. Pendiente de aprobación."
    break
  elif [[ "${STATUS}" == "errored" ]]; then
    echo "Error en el plan. Revisar logs."
    exit 1
  elif [[ "${STATUS}" == "planned_and_finished" ]]; then
    echo "No hay cambios. Run completado."
    exit 0
  fi

  sleep 10
done

# 3. Aprobar el apply (si el plan fue exitoso)
read -p "¿Aprobar el apply? (s/N): " CONFIRM
if [[ "${CONFIRM}" == "s" ]]; then
  curl -s \
    --header "Authorization: Bearer ${TFC_TOKEN}" \
    --header "Content-Type: application/vnd.api+json" \
    --request POST \
    --data "{
      \"comment\": \"Aprobado manualmente desde script\"
    }" \
    "${TFC_API}/runs/${RUN_ID}/actions/apply"
  echo "Apply aprobado"
fi

Notificaciones y webhooks

Terraform Cloud puede enviar notificaciones automáticas cuando cambia el estado de un run:

Configuración en la UI:
Workspace > Settings > Notifications > Add notification

Tipos de notificaciones:
├── Email: notifica a usuarios específicos o a todos los miembros del workspace
├── Slack: envía mensajes a un canal de Slack via webhook
├── Webhook: envía un POST JSON a una URL de tu elección
└── PagerDuty: integración directa con PagerDuty

Eventos que disparan notificaciones:
├── Run created (plan iniciado)
├── Planning (terraform plan en progreso)
├── Needs attention (plan completado, esperando apply)
├── Applying (terraform apply en progreso)
├── Completed (run completado exitosamente)
├── Errored (run falló)
└── Needs override (bloqueado por Sentinel)
# Ejemplo de payload de webhook que envía Terraform Cloud
{
  "payload_version": 1,
  "notification_configuration_id": "nc-XXXXXXXX",
  "run_url": "https://app.terraform.io/app/mi-empresa/workspaces/staging/runs/run-XXXXXXXX",
  "run_id": "run-XXXXXXXX",
  "run_message": "Deploy de staging v1.5.0",
  "run_created_at": "2024-01-15T10:30:00.000Z",
  "run_created_by": "[email protected]",
  "workspace_id": "ws-XXXXXXXX",
  "workspace_name": "staging-infraestructura",
  "organization_name": "mi-empresa",
  "notifications": [
    {
      "message": "Run Applied in mi-empresa/staging-infraestructura",
      "trigger": "run:completed",
      "run_status": "applied",
      "run_updated_at": "2024-01-15T10:35:00.000Z",
      "run_updated_by": "[email protected]"
    }
  ]
}

Sentinel: Policy as Code en Terraform Enterprise

Sentinel es el framework de Policy as Code de HashiCorp, disponible en los planes Plus y Business. Permite definir políticas en código que se evalúan entre el plan y el apply: si una política falla, el apply se bloquea automáticamente.

Casos de uso de Sentinel

  • Prevenir la creación de instancias EC2 que no estén en la lista de tipos permitidos
  • Requerir que todos los recursos tengan etiquetas específicas (cost-center, owner, environment)
  • Prohibir recursos en regiones no autorizadas
  • Limitar los costos máximos de un plan
  • Requerir que los buckets S3 tengan cifrado habilitado

Ejemplo de política Sentinel

# policies/require-resource-tags.sentinel
# Política: todos los recursos de AWS deben tener etiquetas obligatorias

import "tfplan/v2" as tfplan

# Lista de etiquetas requeridas
required_tags = ["environment", "owner", "cost-center", "project"]

# Función que verifica si un recurso tiene todas las etiquetas requeridas
has_required_tags = func(resource) {
    tags = resource.change.after.tags else {}
    for required_tags as tag {
        if not (tag in keys(tags)) {
            return false
        }
    }
    return true
}

# Obtener todos los recursos de AWS del plan
all_aws_resources = filter tfplan.resource_changes as _, resource {
    resource.provider_name is "registry.terraform.io/hashicorp/aws" and
    resource.change.actions contains "create"
}

# Verificar que todos tienen las etiquetas requeridas
all_tagged = all all_aws_resources as _, resource {
    has_required_tags(resource)
}

# La política falla si algún recurso no tiene las etiquetas
main = rule {
    all_tagged
}
# policies/restrict-instance-types.sentinel
# Política: solo se permiten tipos de instancias aprobados

import "tfplan/v2" as tfplan

# Tipos de instancias permitidos (lista aprobada)
allowed_types = ["t3.micro", "t3.small", "t3.medium", "m5.large"]

# Obtener todas las instancias EC2 que se van a crear o modificar
ec2_instances = filter tfplan.resource_changes as _, resource {
    resource.type is "aws_instance" and
    resource.change.actions contains "create"
}

# Verificar que todos los tipos están en la lista permitida
valid_instances = all ec2_instances as _, instance {
    instance.change.after.instance_type in allowed_types
}

# Mensaje de error descriptivo si la política falla
violations = filter ec2_instances as _, instance {
    not (instance.change.after.instance_type in allowed_types)
}

print("Tipos no permitidos encontrados:")
for violations as _, v {
    print("  -", v.address, "usa", v.change.after.instance_type,
          "(permitidos:", allowed_types, ")")
}

main = rule {
    valid_instances
}

Configurar Sentinel en Terraform Cloud

# sentinel.hcl - Configuración del conjunto de políticas
policy "require-resource-tags" {
  source            = "./policies/require-resource-tags.sentinel"
  enforcement_level = "hard-mandatory"
  # Opciones de enforcement:
  # "hard-mandatory": bloquea el apply, no se puede saltear
  # "soft-mandatory": bloquea el apply, pero puede ser salteado por admin
  # "advisory": solo muestra un aviso, no bloquea
}

policy "restrict-instance-types" {
  source            = "./policies/restrict-instance-types.sentinel"
  enforcement_level = "soft-mandatory"
}

Cost Estimation (Estimación de costos)

Terraform Cloud puede estimar el costo de los cambios de infraestructura antes de aplicarlos. Esta característica está disponible en los planes Plus y Business.

La estimación aparece automáticamente en el run, después del plan:

Plan: 3 to add, 1 to change, 0 to destroy.

Cost Estimation:
┌──────────────────────────────────────────────────────────────┐
│ Resources: 4 estimated, 0 unresolvable                       │
│                                                              │
│ Monthly cost change: +$127.84                                │
│                                                              │
│ + aws_instance.web_server (3 instances)         +$89.28/mes  │
│   Instance type: t3.medium                                   │
│   Running hours: 730                                         │
│                                                              │
│ ~ aws_rds_instance.database                     +$38.56/mes  │
│   Instance class: db.t3.medium → db.t3.large                │
│                                                              │
│ Total estimado: $265.12/mes (era $137.28/mes)               │
└──────────────────────────────────────────────────────────────┘

⚠ Sentinel Policy Check:
  cost-limit: Advertencia - el costo mensual supera $200

Drift Detection

El Drift Detection detecta diferencias entre el estado de Terraform y la infraestructura real. Esto ocurre cuando alguien modifica recursos manualmente sin usar Terraform.

# Terraform Cloud ejecuta periódicamente terraform plan -refresh-only
# Si detecta drift, notifica al equipo

# También puedes ejecutarlo manualmente:
terraform plan -refresh-only
# Output si hay drift:
# aws_instance.web_server: Refreshing state... [id=i-0abc123def456789]
#
# Note: Objects have changed outside of Terraform
#
# aws_instance.web_server has been changed:
#   ~ resource "aws_instance" "web_server" {
#       ~ instance_type = "t3.medium" -> "t3.large"  # Cambiado manualmente!
#     }
#
# This is a drift from the configuration. To update the configuration,
# run terraform apply -refresh-only

En Terraform Cloud, la detección de drift se puede configurar:

Workspace > Settings > Health
├── Enable Health Assessments: ON
├── Drift Detection: ON
│   Frecuencia: cada 24 horas
└── Notificar si hay drift: ON (via email/Slack)

Terraform Enterprise: Características adicionales

Terraform Enterprise (TFE) es la versión self-hosted de Terraform Cloud. La instalas en tu propia infraestructura (on-premises o nube privada).

Diferencias principales con Terraform Cloud

Característica Terraform Cloud Terraform Enterprise
Hosting SaaS (HashiCorp) Self-hosted (tu infraestructura)
Datos En servidores de HashiCorp En tus servidores
SSO SAML básico SAML avanzado + Active Directory
Audit logs Limitado Completo y configurable
Clustering No Si (alta disponibilidad)
Air-gapped No Si (sin acceso a internet)
Precio Por usuario/mes Licencia anual (precio enterprise)
SLA Standard Enterprise (99.9%+)
Personalización Limitada Alta

Características exclusivas de Terraform Enterprise

SSO empresarial avanzado:

# Configuración SAML en Terraform Enterprise
saml:
  enabled: true
  idp_cert: |
    -----BEGIN CERTIFICATE-----
    MIIC...
    -----END CERTIFICATE-----
  sso_api_token_session_timeout: 1209600
  acs_consumer_url: https://terraform.empresa.com/users/saml/auth
  metadata_url: https://terraform.empresa.com/users/saml/metadata
  site_admin_role: terraform-admins  # Grupo de AD que es admin

Audit logging completo:

# Los audit logs de Terraform Enterprise incluyen:
# - Quién ejecutó cada plan/apply
# - Qué recursos fueron creados/modificados/destruidos
# - Cambios en configuración de workspaces
# - Cambios en miembros del equipo
# - Accesos a variables sensibles

# Consultar audit logs via API
curl \
  --header "Authorization: Bearer $TFE_TOKEN" \
  "https://terraform.empresa.com/api/v2/organization/audit-trail?page[number]=1&page[size]=20"

Clustering para alta disponibilidad:

Arquitectura de Terraform Enterprise en HA:
┌─────────────────────────────────────────────────────────┐
│                    Load Balancer                        │
└────────────────┬────────────────┬───────────────────────┘
                 │                │
     ┌───────────┴──┐      ┌──────┴────────┐
     │  TFE Node 1  │      │  TFE Node 2   │
     └──────────────┘      └───────────────┘
                 │                │
     ┌───────────┴────────────────┴───────────────────┐
     │              PostgreSQL (HA)                    │
     └────────────────────────────────────────────────┘
                              │
     ┌────────────────────────┴───────────────────────┐
     │         Almacenamiento compartido               │
     │         (S3, Azure Blob, GCS)                  │
     └────────────────────────────────────────────────┘

Ejemplo: Configurar un workspace y ejecutar el primer plan

Vamos a configurar un workspace completo en Terraform Cloud paso a paso:

El código Terraform

# main.tf
terraform {
  cloud {
    organization = "mi-empresa"

    workspaces {
      name = "staging-demo"
    }
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

variable "aws_region" {
  type    = string
  default = "us-east-1"
}

variable "environment" {
  type = string
}

variable "instance_type" {
  type    = string
  default = "t3.micro"
}

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name        = "vpc-${var.environment}"
    Environment = var.environment
  }
}

resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "${var.aws_region}a"

  tags = {
    Name        = "subnet-public-${var.environment}"
    Environment = var.environment
  }
}

output "vpc_id" {
  value       = aws_vpc.main.id
  description = "ID de la VPC creada"
}

output "subnet_id" {
  value       = aws_subnet.public.id
  description = "ID de la subnet pública"
}

Configurar el workspace en Terraform Cloud

# 1. Autenticarse
terraform login
# Sigue las instrucciones del browser

# 2. Inicializar (detecta la configuración cloud{})
terraform init
# Output:
# Initializing Terraform Cloud...
#
# The following Terraform Cloud workspaces were created automatically:
# * staging-demo
#
# Terraform Cloud has been successfully initialized!

# 3. Configurar las variables en la UI de Terraform Cloud:
# Workspace: staging-demo > Variables

# Variables de Terraform:
# environment = staging
# instance_type = t3.micro

# Variables de entorno (sensibles):
# AWS_ACCESS_KEY_ID = (tu clave de acceso)
# AWS_SECRET_ACCESS_KEY = (tu clave secreta)

# 4. Ejecutar el plan (se ejecuta en Terraform Cloud, no localmente)
terraform plan
# Output:
# Running plan in Terraform Cloud. Output will stream here.
# Waiting for the plan to start...
#
# 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:
#
#   # aws_vpc.main will be created
#   + resource "aws_vpc" "main" {
#       + cidr_block = "10.0.0.0/16"
#       + tags       = {
#           + "Environment" = "staging"
#           + "Name"        = "vpc-staging"
#         }
#     }
#
#   # aws_subnet.public will be created
#   + resource "aws_subnet" "public" {
#       + cidr_block        = "10.0.1.0/24"
#       + vpc_id            = (known after apply)
#     }
#
# Plan: 2 to add, 0 to change, 0 to destroy.

# 5. Aplicar los cambios
terraform apply
# Terraform Cloud pedirá confirmación (a menos que auto-apply esté activado)
# Enter a value: yes

Ejercicio práctico: Migrar un proyecto local a Terraform Cloud

Enunciado

Tienes un proyecto Terraform que actualmente usa un backend de S3 para el estado. Necesitas migrarlo a Terraform Cloud manteniendo el estado existente y configurando el workflow de VCS.

El proyecto tiene:

  • Backend actual: S3 con DynamoDB para locking
  • Proveedor: AWS
  • Ambientes: staging y produccion (workspaces de Terraform)
  • Repositorio en GitHub: mi-empresa/infraestructura

Solución paso a paso

Paso 1: Preparar Terraform Cloud

# 1. Crear dos workspaces en Terraform Cloud:
#    - staging-infraestructura
#    - produccion-infraestructura
#
# Desde la UI:
#   Organización > New Workspace > CLI-driven workflow
#   Nombre: staging-infraestructura
#
# O desde la CLI con la API:
curl \
  --header "Authorization: Bearer ${TFC_TOKEN}" \
  --header "Content-Type: application/vnd.api+json" \
  --request POST \
  --data '{
    "data": {
      "type": "workspaces",
      "attributes": {
        "name": "staging-infraestructura",
        "execution-mode": "remote",
        "terraform-version": "1.9.0"
      }
    }
  }' \
  https://app.terraform.io/api/v2/organizations/mi-empresa/workspaces

Paso 2: Modificar el backend en el código

# ANTES - backend.tf con S3
terraform {
  backend "s3" {
    bucket         = "mi-empresa-terraform-state"
    key            = "infraestructura/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}

# DESPUÉS - Cambiar a Terraform Cloud
terraform {
  cloud {
    organization = "mi-empresa"

    workspaces {
      # Usar tags para gestionar múltiples workspaces (staging, produccion)
      # En lugar de un nombre fijo
      tags = ["infraestructura"]
    }
  }
}

Paso 3: Migrar el estado

# 1. Autenticarse en Terraform Cloud
terraform login

# 2. Inicializar con el nuevo backend
terraform init
# Terraform detecta el cambio de backend y pregunta si migrar el estado:
# "Do you want to copy existing state to the new backend?"
# Enter a value: yes
#
# Terraform copiará el estado de S3 a Terraform Cloud automáticamente

# 3. Verificar que el estado se migró correctamente
terraform state list
# Debe mostrar todos los recursos del estado anterior

# 4. Verificar en la UI de Terraform Cloud
# Organization > Workspace > staging-infraestructura > States
# Debe mostrar el estado migrado

# 5. Una vez verificado, eliminar el backend de S3
# (mantener el bucket por un tiempo como respaldo)

Paso 4: Configurar las variables en Terraform Cloud

# Variables de Terraform (equivalen a staging.tfvars)
# En la UI: Workspace > Variables > Add variable

# environment    = staging        (Terraform variable, no sensible)
# instance_type  = t3.medium     (Terraform variable, no sensible)
# replica_count  = 2             (Terraform variable, no sensible)

# Variables de entorno (credenciales - sensibles)
# AWS_ACCESS_KEY_ID     = (clave de acceso)     (Environment, Sensitive: ✓)
# AWS_SECRET_ACCESS_KEY = (clave secreta)       (Environment, Sensitive: ✓)
# AWS_DEFAULT_REGION    = us-east-1             (Environment, no sensible)

# Con la API:
for VAR_KEY in "environment" "instance_type"; do
  curl \
    --header "Authorization: Bearer ${TFC_TOKEN}" \
    --header "Content-Type: application/vnd.api+json" \
    --request POST \
    --data "{
      \"data\": {
        \"type\": \"vars\",
        \"attributes\": {
          \"key\": \"${VAR_KEY}\",
          \"value\": \"staging\",
          \"category\": \"terraform\",
          \"sensitive\": false
        }
      }
    }" \
    "https://app.terraform.io/api/v2/workspaces/${WORKSPACE_ID}/vars"
done

Paso 5: Conectar el repositorio VCS

En la UI de Terraform Cloud:
1. Workspace: staging-infraestructura
2. Settings > Version Control
3. Connect to VCS
4. Seleccionar GitHub
5. Autorizar Terraform Cloud en GitHub
6. Seleccionar repositorio: mi-empresa/infraestructura
7. Configurar:
   - Branch: main
   - Working directory: environments/staging
   - Terraform Working Directory: environments/staging
8. En GitHub, verificar que el webhook fue creado:
   Repositorio > Settings > Webhooks
   Debe aparecer: https://app.terraform.io/webhooks/vcs/...

Paso 6: Configurar la integración PR con GitHub

En Terraform Cloud:
Workspace > Settings > Version Control > Pull Requests
├── Enable speculative plans on pull requests: ON
│   (Ejecuta terraform plan en cada PR y muestra el resultado en GitHub)
└── Auto apply: OFF (para producción) / ON (opcional para staging)

Paso 7: Verificar la integración

# Hacer un cambio de prueba en el código
cd environments/staging
echo "" >> main.tf  # Agregar una línea vacía
git add main.tf
git commit -m "test: verificar integración Terraform Cloud"
git push origin feature/test-tfc

# Crear un PR en GitHub
# Terraform Cloud debería:
# 1. Detectar el PR via webhook
# 2. Ejecutar terraform plan
# 3. Mostrar el resultado como "check" en el PR de GitHub
# ✓ Terraform Cloud: Plan succeeded (0 changes)

Errores comunes con Terraform Cloud

Error 1: Token de autenticación incorrecto o expirado

Problema: Error: Invalid token

# Solución: renovar el token
terraform logout
terraform login  # Genera un nuevo token

# O generar token manualmente:
# UI: User Settings > Tokens > Create an API token
# Copiar el token a ~/.terraform.d/credentials.tfrc.json

Error 2: El workspace no existe en la organización

Problema: Error: Workspace "mi-workspace" not found in organization "mi-empresa"

# Verificar que el workspace existe
curl \
  --header "Authorization: Bearer ${TFC_TOKEN}" \
  "https://app.terraform.io/api/v2/organizations/mi-empresa/workspaces/mi-workspace"

# Si no existe, crearlo desde la UI o via API

Error 3: Permisos insuficientes en la organización

Problema: El usuario puede ver la organización pero no puede crear workspaces o ejecutar runs.

Solución: Contactar al administrador de la organización para que otorgue los permisos necesarios en la sección Teams de la organización.

Error 4: Variables sensibles no disponibles en el plan

Problema: Las variables marcadas como "Sensitive" en Terraform Cloud no se muestran en los logs (es correcto, es por seguridad), pero Terraform falla porque no encuentra la variable.

# Verificar que la variable está configurada correctamente
# En la UI: Workspace > Variables
# La variable debe aparecer con un candado (🔒) indicando que es sensible

# Si la variable es de tipo "Terraform variable", asegurarse de que
# el nombre coincide exactamente con el de variables.tf
# Por ejemplo: si variables.tf tiene "variable "db_password"",
# la variable en TFC debe llamarse "db_password" (no "DB_PASSWORD")

Error 5: El plan se ejecuta pero el apply nunca se aplica

Problema: El plan completa pero el estado queda en "needs confirmation" indefinidamente.

Solución: Verificar la configuración de auto-apply:

Workspace > Settings > General > Apply Method
├── Auto apply: OFF → debes confirmar manualmente en la UI
└── Auto apply: ON  → aplica automáticamente después del plan

Error 6: Conflicto al migrar el estado de S3 a Terraform Cloud

Problema: Al hacer terraform init con el nuevo backend de cloud{}, el proceso de migración falla.

# Solución: hacer la migración en pasos
# 1. Primero descargar el estado actual de S3 localmente
terraform state pull > terraform.tfstate.backup

# 2. Cambiar el backend a cloud{}
# 3. Inicializar y confirmar la migración
terraform init

# 4. Si falla, restaurar el estado manualmente
terraform state push terraform.tfstate.backup

Resumen del Módulo 8

A lo largo de este módulo has aprendido a llevar Terraform de un uso manual y local a un flujo de trabajo profesional y automatizado:

08-01: Conceptos de CI/CD con Terraform

  • Por qué automatizar (consistencia, seguridad, trazabilidad)
  • El patrón estándar: plan en PR, apply en merge
  • Herramientas disponibles y cuándo usar cada una
  • Seguridad en CI/CD: gestión de credenciales y principio de mínimo privilegio

08-02: Jenkins

  • Jenkinsfile declarativo completo con todas las etapas
  • Gestión de credenciales con el sistema de Jenkins
  • Aprobaciones manuales con input()
  • Notificaciones a Slack y email

08-03: GitHub Actions

  • Workflows completos para plan en PR y apply en merge
  • La action hashicorp/setup-terraform
  • OIDC para autenticación sin credenciales estáticas
  • GitHub Environments para aprobaciones manuales
  • Cache de providers y control de concurrencia

08-04: Terraform Cloud y Enterprise

  • Plataforma gestionada que integra estado, CI/CD, variables y políticas
  • Planes disponibles y cuándo usar cada uno
  • VCS-driven workflow: integración directa con Git
  • Sentinel para Policy as Code
  • Migración de proyectos locales a Terraform Cloud

En el próximo módulo aprenderás sobre troubleshooting y resolución de problemas comunes con Terraform: errores de estado, problemas de providers, debugging de recursos y cómo abordar situaciones de emergencia en producción.

© Copyright 2026. Todos los derechos reservados