Conceptos

Terraform

Herramienta de Infrastructure as Code de HashiCorp que permite definir, provisionar y gestionar infraestructura multi-cloud mediante archivos declarativos en HCL.

evergreen#iac#terraform#cloud#devops

Terraform es la herramienta de IaC más adoptada del mercado. Usa un lenguaje declarativo (HCL) para definir infraestructura y un modelo de estado para rastrear qué recursos existen y cómo actualizarlos.

¿Cómo funciona?

Write (.tf files) → Plan (preview changes) → Apply (execute changes)

Ciclo de vida

terraform init      # Descargar providers y módulos
terraform validate  # Verificar sintaxis
terraform plan      # Ver qué va a cambiar
terraform apply     # Aplicar cambios
terraform destroy   # Eliminar toda la infraestructura

Conceptos fundamentales

Providers

Plugins que conectan Terraform con APIs de servicios:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
 
provider "aws" {
  region = "us-east-1"
}

Más de 4,000 providers: AWS, Azure, GCP, Kubernetes, GitHub, Datadog, Cloudflare, etc.

Resources

Unidad básica — un recurso de infraestructura:

resource "aws_s3_bucket" "data" {
  bucket = "my-app-data-${var.environment}"
 
  tags = {
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}
 
resource "aws_s3_bucket_versioning" "data" {
  bucket = aws_s3_bucket.data.id
  versioning_configuration {
    status = "Enabled"
  }
}

Variables

# variables.tf
variable "environment" {
  description = "Deployment environment"
  type        = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Must be dev, staging, or prod."
  }
}
 
variable "instance_count" {
  description = "Number of instances"
  type        = number
  default     = 2
}
# terraform.tfvars
environment    = "prod"
instance_count = 3

Outputs

output "bucket_arn" {
  description = "ARN of the S3 bucket"
  value       = aws_s3_bucket.data.arn
}
 
output "api_url" {
  description = "API Gateway URL"
  value       = aws_apigatewayv2_api.main.api_endpoint
  sensitive   = false
}

Data sources

Leer información de recursos existentes (no gestionados por Terraform):

data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]
 
  filter {
    name   = "name"
    values = ["al2023-ami-*-x86_64"]
  }
}
 
resource "aws_instance" "web" {
  ami           = data.aws_ami.amazon_linux.id
  instance_type = "t3.micro"
}

State

El state file es la fuente de verdad sobre qué recursos existen:

# Backend remoto (obligatorio para equipos)
terraform {
  backend "s3" {
    bucket         = "company-terraform-state"
    key            = "services/api/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"    # Locking
    encrypt        = true                  # Encryption at rest
  }
}

Comandos de state

terraform state list                    # Listar recursos
terraform state show aws_instance.web   # Detalle de un recurso
terraform state mv aws_instance.old aws_instance.new  # Renombrar
terraform state rm aws_instance.legacy  # Dejar de gestionar
terraform import aws_instance.web i-1234567890  # Importar existente

Módulos

Paquetes reutilizables de infraestructura:

# Módulo propio
module "api" {
  source = "./modules/lambda-api"
 
  function_name = "my-api"
  runtime       = "nodejs20.x"
  memory_size   = 256
  environment   = var.environment
}
 
# Módulo del registry
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.5.0"
 
  name = "production"
  cidr = "10.0.0.0/16"
}

Estructura de un módulo

modules/lambda-api/
├── main.tf          # Resources
├── variables.tf     # Inputs
├── outputs.tf       # Outputs
├── versions.tf      # Provider requirements
└── README.md        # Documentation

Patrones avanzados

Workspaces

Múltiples entornos con el mismo código:

terraform workspace new staging
terraform workspace new production
terraform workspace select staging
terraform apply -var-file="staging.tfvars"

for_each y count

# Crear múltiples recursos
resource "aws_iam_user" "team" {
  for_each = toset(["alice", "bob", "carol"])
  name     = each.key
}
 
# Condicional
resource "aws_cloudwatch_log_group" "api" {
  count = var.enable_logging ? 1 : 0
  name  = "/api/${var.environment}"
}

Moved blocks (refactoring seguro)

moved {
  from = aws_instance.server
  to   = aws_instance.web
}

Terraform vs alternativas

AspectoTerraformPulumiAWS CDKCloudFormation
LenguajeHCLTS, Python, GoTS, PythonYAML/JSON
Multi-cloudNo (AWS)No (AWS)
StateSelf-managed/CloudManaged/selfAWS-managedAWS-managed
Ecosistema4,000+ providers100+ providersAWS onlyAWS only
LicenciaBSL 1.1Apache 2.0Apache 2.0Propietario
Fork OSSOpenTofu

Seguridad

# Nunca hardcodear secretos
variable "db_password" {
  type      = string
  sensitive = true   # No aparece en logs ni plan
}
 
# Usar data sources para secretos
data "aws_secretsmanager_secret_version" "db" {
  secret_id = "prod/db/password"
}

Anti-patrones

  • State local — compartir state por Slack/email. Usar backend remoto con locking.
  • Mega-monolito — toda la infra en un solo main.tf. Dividir por dominio.
  • Sin pinear versiones — providers y módulos sin versión fija rompen builds.
  • Apply sin plan — siempre revisar el plan antes de aplicar.
  • Secretos en .tf — usar variables sensitive, secret managers, o Vault.
  • Ignorar terraform fmt — formateo consistente facilita code review.

¿Por qué importa?

Terraform se ha convertido en el estándar de facto para infraestructura como código. Dominarlo no es opcional para equipos que operan en la nube — es la diferencia entre infraestructura reproducible y auditable, y configuración manual que nadie puede reconstruir cuando falla.

Referencias

Conceptos