Cloud Intermedio

Terraform en AWS — Infraestructura como Código

Provisiona infraestructura en AWS de forma reproducible y versionada con Terraform. VPC, EC2, S3, IAM y estado remoto en S3 + DynamoDB desde cero.

Recursos

Contenido

¿Qué es Infraestructura como Código?

IaC (Infrastructure as Code) significa definir y gestionar infraestructura mediante archivos de configuración en lugar de hacerlo manualmente desde la consola. Terraform es la herramienta IaC más usada y soporta más de 1000 proveedores cloud.

Ventajas clave:

  • Reproducibilidad: el mismo código genera la misma infraestructura siempre
  • Versionado: la infraestructura vive en Git como el código de la aplicación
  • Colaboración: los cambios pasan por Pull Requests y revisión de código
  • Rollback: revertir infraestructura es tan fácil como un git revert

Estructura básica de un proyecto Terraform

infra/
├── main.tf          # Recursos principales
├── variables.tf     # Definición de variables
├── outputs.tf       # Valores de salida
├── versions.tf      # Versión de Terraform y providers
└── terraform.tfvars # Valores de variables (no comitear secretos)

Configuración inicial

# versions.tf
terraform {
  required_version = ">= 1.7"

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

  # Estado remoto en S3 (recomendado para equipos)
  backend "s3" {
    bucket         = "mi-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "eu-south-2"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

provider "aws" {
  region = var.aws_region
}

Tu primera VPC

# main.tf
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

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

resource "aws_subnet" "public" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(aws_vpc.main.cidr_block, 8, count.index)
  availability_zone = var.availability_zones[count.index]

  map_public_ip_on_launch = true

  tags = {
    Name = "${var.project}-public-${count.index + 1}"
    Tier = "public"
  }
}

Comandos del ciclo de vida

# Inicializar (descarga providers, configura backend)
terraform init

# Ver los cambios que se aplicarán
terraform plan -out=tfplan

# Aplicar los cambios
terraform apply tfplan

# Ver el estado actual
terraform show

# Destruir infraestructura (¡cuidado en producción!)
terraform destroy

IAM con mínimo privilegio

# IAM role para una instancia EC2 con acceso a S3
resource "aws_iam_role" "app" {
  name = "${var.project}-app-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "ec2.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy" "app_s3" {
  name = "s3-access"
  role = aws_iam_role.app.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect   = "Allow"
      Action   = ["s3:GetObject", "s3:PutObject"]
      Resource = "${aws_s3_bucket.assets.arn}/*"
    }]
  })
}

Principio de mínimo privilegio: solo los permisos que la aplicación realmente necesita.