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 planyterraform applyse 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
- Ve a app.terraform.io y crea una cuenta
- 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 agentesVariables 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=staginginstance_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/varsVCS-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
- En Terraform Cloud, ve a tu workspace > Settings > Version Control
- Conecta tu proveedor VCS (GitHub, GitLab, etc.)
- Selecciona el repositorio
- 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"
fiNotificaciones 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-onlyEn 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 adminAudit 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: yesEjercicio 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/workspacesPaso 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"
donePaso 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.jsonError 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 APIError 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.backupResumen 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.
Curso de Terraform
Módulo 1: Introducción a Terraform
- ¿Qué es Terraform?
- Instalando Terraform
- Conceptos Básicos de Terraform
- Primera Configuración de Terraform
Módulo 2: Lenguaje de Configuración de Terraform
Módulo 3: Gestión del Estado
Módulo 4: Módulos de Terraform
Módulo 5: Aprovisionamiento de Recursos
- Conceptos Básicos de Aprovisionamiento
- Aprovisionamiento de Recursos AWS
- Aprovisionamiento de Recursos Azure
- Aprovisionamiento de Recursos GCP
Módulo 6: Funcionalidades Avanzadas de Terraform
Módulo 7: Mejores Prácticas de Terraform
- Organización del Código
- Control de Versiones
- Pruebas del Código de Terraform
- Mejores Prácticas de Seguridad
Módulo 8: Terraform en CI/CD
- Integración de Terraform con CI/CD
- Automatización de Terraform con Jenkins
- Uso de Terraform con GitHub Actions
- Terraform Cloud y Enterprise
