From n00b to ZeroCool / Profesionalización
Infrastructure as Code: servidores que se escriben (y no se rezan)
Guía práctica de IaC con Terraform: módulos, state, CI y rollback. Menos click-ops, más control para deploys cloud-native sin drama.
Lo que vale la pena leer aquí
Al día siguiente, nadie podía entrar al sistema. El balanceador “misteriosamente” apuntaba a un target group viejo, el security group tenía un puerto abierto “temporal” y el único que sabía qué se cambió… iba en el Uber, sin batería y con la laptop viejita apagada en la mochila.
Intro con gancho
El outage no empezó con un hackeo épico ni con un meteorito. Empezó con un “nomás le moví tantito” en la consola del cloud, un martes 6:40 pm, cuando ya estabas pensando en los tacos.
Al día siguiente, nadie podía entrar al sistema. El balanceador “misteriosamente” apuntaba a un target group viejo, el security group tenía un puerto abierto “temporal” y el único que sabía qué se cambió… iba en el Uber, sin batería y con la laptop viejita apagada en la mochila.
Infrastructure as Code (IaC) es la vacuna contra ese tipo de tragedias. No te quita el estrés, pero lo vuelve trazable, reproducible y reversible.
Qué vas a aprender
- Qué problema real resuelve IaC y cuándo sí vale la pena (y cuándo no).
- Una guía paso a paso para levantar infraestructura con Terraform de forma profesional (sin magia negra).
- Cómo organizar repos, módulos y ambientes (dev/stage/prod) sin que se vuelva una telenovela.
- Cómo manejar el state, secretos, cambios peligrosos y deploys con CI.
- Errores comunes que ya nos cobraron horas nalga (para que no te cobren a ti).
Contexto práctico
Piensa en IaC como tratar tu infraestructura igual que tu código:
- Git como fuente de verdad (quién cambió qué y cuándo).
- Pull requests para revisar cambios antes de romper production.
- Environments (dev/stage/prod) con diferencias controladas.
- Automatización: menos click-ops, más pipeline.
En México y LATAM esto pega doble:
- Si tu internet se pone punk y se cae a mitad de un cambio manual, IaC te salva porque puedes reintentar y dejar evidencia.
- Cuando el presupuesto está apretado (sí, AWS en dólares), IaC te ayuda a ver y auditar qué está encendido y por qué. Ese RDS “de prueba” que lleva 4 meses corriendo ya no se esconde tan fácil.
Tradeoff honesto: IaC mete disciplina. Al inicio se siente más lento que “nomás crear el recurso en la consola”. Pero a la tercera vez que lo replicas, o al primer rollback de verdad, se paga solo.
Paso a paso: tu primera base seria con Terraform
La idea es armar una infraestructura pequeña pero realista. Por ejemplo, en AWS:
- VPC + subnets
- Security group
- Un EC2 (o lo que uses) como placeholder
Si usas GCP/Azure aplica igual. Cambian providers y nombres, no el patrón.
1) Instala y prepara el repo
Requisitos:
- Terraform (ideal:
tfenvpara versionar) - Credenciales del cloud (AWS CLI con perfil, o variables)
- Git
Estructura recomendada (sencilla pero escalable):
infra/
modules/
network/
compute/
envs/
dev/
main.tf
providers.tf
variables.tf
outputs.tf
terraform.tfvars
prod/
...
.terraform.lock.hcl
README.md
Reglas de oro:
- Módulos para cosas repetibles (network, compute, db).
- envs para valores por ambiente (tamaños, CIDR, tags, regiones, etc.).
2) Configura provider y versiones
envs/dev/providers.tf
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
envs/dev/variables.tf
variable "region" {
type = string
default = "us-east-1"
}
variable "project" {
type = string
default = "byteit"
}
variable "env" {
type = string
default = "dev"
}
3) Crea un módulo de red (VPC + subnets)
modules/network/main.tf
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "${var.project}-${var.env}-vpc"
}
}
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnet_cidr
map_public_ip_on_launch = true
availability_zone = var.az
tags = {
Name = "${var.project}-${var.env}-public-a"
}
}
modules/network/variables.tf
variable "project" { type = string }
variable "env" { type = string }
variable "vpc_cidr" { type = string }
variable "public_subnet_cidr" { type = string }
variable "az" { type = string }
modules/network/outputs.tf
output "vpc_id" {
value = aws_vpc.this.id
}
output "public_subnet_id" {
value = aws_subnet.public_a.id
}
4) Crea un módulo de compute (EC2 simple)
modules/compute/main.tf
resource "aws_security_group" "this" {
name = "${var.project}-${var.env}-sg"
vpc_id = var.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_cidrs
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project}-${var.env}-sg"
}
}
resource "aws_instance" "this" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
vpc_security_group_ids = [aws_security_group.this.id]
tags = {
Name = "${var.project}-${var.env}-ec2"
}
}
modules/compute/variables.tf
variable "project" { type = string }
variable "env" { type = string }
variable "vpc_id" { type = string }
variable "subnet_id"{ type = string }
variable "ami_id" {
type = string
}
variable "instance_type" {
type = string
default = "t3.micro"
}
variable "ssh_cidrs" {
type = list(string)
default = ["0.0.0.0/0"]
}
5) Une todo desde el environment
envs/dev/main.tf
module "network" {
source = "../../modules/network"
project = var.project
env = var.env
vpc_cidr = "10.10.0.0/16"
public_subnet_cidr = "10.10.1.0/24"
az = "us-east-1a"
}
module "compute" {
source = "../../modules/compute"
project = var.project
env = var.env
vpc_id = module.network.vpc_id
subnet_id = module.network.public_subnet_id
# Nota: define un AMI real para tu región
ami_id = "ami-0c02fb55956c7d316"
instance_type = "t3.micro"
# Neta: no dejes 0.0.0.0/0 en prod. En dev quizá, pero que sea consciente.
ssh_cidrs = ["YOUR_PUBLIC_IP/32"]
}
6) Plan y apply (sin volarte un pie)
Desde infra/envs/dev:
terraform init
terraform fmt -recursive
terraform validate
terraform plan -out tfplan
terraform apply tfplan
Tips de batalla:
plan -outte deja un artefacto. Si alguien correapplycon ese plan, aplica exactamente lo revisado.- Si el plan está gigante y no lo entiendes: no lo apliques. Algo está raro (provider upgrade, drift, o módulo mal amarrado).

7) Profesionaliza: state remoto + locking
El state local (terraform.tfstate) en tu laptop es como llevar la contabilidad en servilletas. Funciona… hasta que alguien más corre apply, o hasta que tu disco muere el día que el jefe te pide “un cambio rápido” antes del deadline.
Lo sano:
- State remoto (por ejemplo S3)
- Locking (DynamoDB) para evitar cambios concurrentes
Ejemplo backend S3 (en cada env):envs/dev/backend.tf
terraform {
backend "s3" {
bucket = "byteit-terraform-state"
key = "dev/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "byteit-terraform-locks"
encrypt = true
}
}
Realidad: el bucket/tabla para el backend se vuelve “infra de la infra”. Puedes crearlo manualmente una vez (bien documentado) o con un bootstrap separado. Lo que no quieres es que cada quien lo invente a su modo.
8) Secretos: no los metas al repo (aunque “nadie lo vea”)
Terraform va a guardar valores en el state. Si metes passwords literal, terminas con secretos regados en:
- el state
- logs del CI
- PRs
Estrategias:
- Usa un secret manager (AWS Secrets Manager, SSM Parameter Store, Vault).
- Pasa referencias, no el secreto crudo.
- Marca variables como
sensitive = truepara reducir exposición en outputs.
Ejemplo (variable sensible):
variable "db_password" {
type = string
sensitive = true
}
9) Mete CI: plan en PR, apply con control
Un workflow típico (y bien aterrizado):
- PR:
terraform fmt,validate,plany publica el plan como comentario/artefacto. - Merge a main:
apply(ideal: con aprobación manual para prod).
Si tu equipo es chico y traen mil pendientes (clásico): mínimo
- PR obligatorio
- plan visible
- apply solo desde CI
Porque el “lo corrí en mi máquina” en infraestructura se traduce a “lo rompí en production”. Y luego viene el rollback a mano, con sudor.
Screenshots sugeridos
- Captura del output de
terraform planmostrando cambios claros (add/change/destroy). - Vista del state remoto en S3 y el lock en DynamoDB.
- Ejemplo de PR con el plan pegado (GitHub/GitLab) y un approve.
- Un diff de módulo (cambio de variable/tag) para mostrar trazabilidad.
Errores comunes + solución
1) Drift: alguien cambió cosas en consola
Síntoma: plan muestra cambios que nadie “hizo en código”.
Solución:
- Acepta la realidad: la consola existe. Define una regla y respétala.
- Corre
terraform refresh(oplannormal) para ver drift. - Decide: o regresas el cambio a lo que dice el código, o actualizas el código para que represente el nuevo estado.
- En prod, reduce click-ops con permisos (IAM) y separa roles.
2) Destroy accidental por cambios de nombre o count
Síntoma: Terraform quiere destruir y recrear recursos porque cambió un name, un count o un índice.
Solución:
- Usa
for_eachcon llaves estables en lugar decountcuando puedas. - Revisa
terraform planbuscando-/+(recreate). - Para recursos críticos, considera
lifecycle { prevent_destroy = true }.
3) State perdido o mezclado entre ambientes
Síntoma: aplicas dev y te cambia prod (terror real).
Solución:
- Backend remoto separado por
key/workspace. - Variables por environment bien definidas.
- Bloquea el apply de prod con aprobaciones.
4) Módulos sin versión: el “ya cambió algo” fantasma
Síntoma: un módulo cambia y rompe ambientes sin aviso.
Solución:
- Versiona módulos (tags/commits) si están en repo aparte.
- Si están en monorepo, usa PRs y releases internos; evita cambios sin revisión.
5) Plan enorme por upgrade del provider
Síntoma: actualizas provider y el plan se vuelve kilométrico.
Solución:
- Actualiza provider con intención (PR dedicado).
- Lee changelog, prueba en dev, luego stage, luego prod.
- Si es demasiado, congela versión y agenda la migración. Mejor un upgrade bien hecho que un deploy tarde con rollback improvisado.

Checklist final
- Infra en Git con estructura
modules/+envs/. -
terraform fmt,validatepasan local y en CI. - State remoto con locking (S3 + DynamoDB o equivalente).
- Secrets fuera del repo; variables sensibles marcadas.
- PR con
planvisible y apply controlado (idealmente desde CI). - Regla anti-click-ops definida (y permisos alineados).
- Estrategia para cambios peligrosos (
for_each,prevent_destroy, revisiones).
FAQ
1) ¿Terraform es lo mismo que Ansible?
No. Terraform es más para provisioning (crear/gestionar recursos). Ansible es más de config management (configurar cosas dentro del servidor). Se complementan, no compiten.
2) ¿IaC solo sirve en cloud?
No. Puedes usar IaC para on-prem (VMs, redes) dependiendo de tooling/proveedores. Pero donde más brilla es en cloud porque todo se mueve por API.
3) ¿Qué tan “cloud-native” es IaC?
Mucho. Cloud-native no es solo Kubernetes: es operar con automatización, reproducibilidad y cambios auditables. IaC es base para pipelines, clusters, observabilidad y escalamiento.
4) ¿Qué hago si ya tengo infraestructura hecha a mano?
Empieza por lo más doloroso o repetible. Puedes:
- Importar recursos (
terraform import) y luego limpiar el código. - Recrear por etapas (blue/green) si importar es un infierno.
- Hacer migración gradual: primero redes, luego compute, luego datos (con cuidado).
5) ¿Cómo evito que Terraform borre cosas críticas?
- Revisión humana del plan.
prevent_destroyen recursos sensibles.- Separar stacks (por ejemplo, datos en un stack diferente al de compute).
- Backups y runbooks de rollback (porque la vida pasa).
Siguiente episodio: teaser
Ya escribes servidores. Ahora toca que se desplieguen solos, sin que alguien esté “picándole” a mano.
Lo que sigue: pipelines CI/CD para infraestructura y apps, con guardrails para no quemar production por accidente.
Idea para cerrar bien este post: toma una sola práctica de aquí y conviértela en algo que tu equipo pueda aplicar hoy.
Cuando un artículo aterriza en decisiones reales, deja de ser contenido y se vuelve ventaja.


