Terraform en equipo: Remote State, Workspaces y buenas prácticas

Cómo organizar Terraform para trabajar en equipo sin pisarse los pies. Remote state en S3, workspaces, módulos reutilizables y estrategias de CI/CD.

El problema: Terraform en solitario vs en equipo

Terraform es genial cuando trabajas solo. El problema aparece cuando tu equipo crece y empiezan las preguntas:

  • “¿Quién tiene el state?”
  • “¿Por qué mi plan muestra cambios que no hice?”
  • “¿Cómo evitamos que dos personas apliquen al mismo tiempo?”

Remote State con S3 + DynamoDB

La primera regla de Terraform en equipo: nunca guardes el state local.

# backend.tf
terraform {
  backend "s3" {
    bucket         = "mi-empresa-terraform-state"
    key            = "production/networking/terraform.tfstate"
    region         = "eu-west-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

# Crear la tabla de locks
resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }

  tags = {
    Name        = "terraform-state-locks"
    Environment = "shared"
  }
}

Estructura de proyecto escalable

infrastructure/
├── modules/
│   ├── networking/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   ├── outputs.tf
│   │   └── README.md
│   ├── eks-cluster/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── monitoring/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── environments/
│   ├── production/
│   │   ├── main.tf
│   │   ├── backend.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   │   ├── main.tf
│   │   ├── backend.tf
│   │   └── terraform.tfvars
│   └── dev/
│       ├── main.tf
│       └── terraform.tfvars
└── .github/
    └── workflows/
        └── terraform.yml

Pipeline de CI/CD para Terraform

# .github/workflows/terraform.yml
name: Terraform CI/CD

on:
  pull_request:
    paths:
      - 'infrastructure/**'
  push:
    branches: [main]
    paths:
      - 'infrastructure/**'

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.7.0

      - name: Terraform Init
        run: terraform init
        working-directory: infrastructure/environments/production

      - name: Terraform Plan
        run: terraform plan -out=tfplan -no-color
        working-directory: infrastructure/environments/production

      - name: Comment PR with Plan
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const output = `#### Terraform Plan 📖
            \`\`\`
            ${process.env.PLAN}
            \`\`\`
            *Pushed by: @${{ github.actor }}*`;

  apply:
    needs: plan
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Terraform Apply
        run: terraform apply -auto-approve tfplan
        working-directory: infrastructure/environments/production

Buenas prácticas

  1. Usa módulos para código reutilizable
  2. Versiona tus módulos con tags de Git
  3. No hardcodees valores — usa variables y tfvars
  4. State por componente: separa networking, compute, databases
  5. Policy as Code: usa terraform-compliance o OPA

Conclusión

Terraform en equipo requiere disciplina, pero con la estructura correcta se convierte en una herramienta poderosa para gestionar infraestructura de forma colaborativa y segura.


¿Tienes tu propio setup de Terraform? Cuéntame en Twitter.