Infraestructura como Código (IaC-001 a IaC-004)
🎯 Objetivo
Toda la infraestructura Cloudflare + Supabase debe estar definida en código (Terraform/Wrangler), NUNCA configurada a mano desde el dashboard.
⚡ REGLAS INQUEBRANTABLES
IaC-001: TODO RECURSO CLOUDFLARE EN TERRAFORM
[REQUIRED] Por qué: un recurso creado a mano en el panel no está en ninguna parte: no se revisa, no se replica y desaparece con la cuenta. El día que haya que reconstruir el entorno, lo que no esté en código no se reconstruye porque nadie recuerda que existía.
# terraform/main.tf
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
}
# API Gateway Worker
resource "cloudflare_worker_script" "api_gateway" {
account_id = var.cloudflare_account_id
name = "api-gateway"
content = file("../apps/api-gateway/dist/index.js")
plain_text_binding {
name = "SUPABASE_URL"
text = var.supabase_url
}
secret_text_binding {
name = "SUPABASE_KEY"
text = var.supabase_anon_key
}
}
# R2 Bucket
resource "cloudflare_r2_bucket" "uploads" {
account_id = var.cloudflare_account_id
name = "omnisuite-uploads"
location = "WNAM" # Oeste Norteamérica
}
# KV Namespace
resource "cloudflare_workers_kv_namespace" "cache" {
account_id = var.cloudflare_account_id
title = "omnisuite-cache"
}
# Durable Object para Rate Limiting
resource "cloudflare_workers_kv_namespace" "rate_limiter" {
account_id = var.cloudflare_account_id
title = "omnisuite-rate-limiter"
}IaC-002: ENTORNOS COMO CÓDIGO
[REQUIRED] Por qué: si staging y producción se configuran por separado, divergen — y entonces staging deja de predecir nada, que es su única razón de ser. Definir ambos desde el mismo código con distintos parámetros es lo que mantiene válida la prueba.
# terraform/environments/
# ├── dev.tfvars
# ├── staging.tfvars
# └── production.tfvars
# dev.tfvars
environment = "dev"
supabase_url = "https://dev-xxxxx.supabase.co"
domain = "dev.omnisuite.com"
rate_limit_public = 100 # Más permisivo en dev
# production.tfvars
environment = "production"
supabase_url = "https://prod-xxxxx.supabase.co"
domain = "omnisuite.com"
rate_limit_public = 10 # Estricto en producción# Deploy por entorno
terraform apply -var-file="environments/production.tfvars"IaC-003: SUPABASE COMO CÓDIGO (MIGRACIONES)
[REQUIRED] Por qué: un cambio de esquema aplicado desde el panel no tiene historial, no se puede revertir y no llega a los demás entornos. La migración versionada es lo que hace que el esquema sea reproducible y que el DOWN de DB-022 tenga dónde vivir.
-- supabase/migrations/20240315_initial_schema.sql
-- UP
CREATE TABLE clients (...);
CREATE TABLE proposals (...);
-- DOWN
DROP TABLE IF EXISTS proposals;
DROP TABLE IF EXISTS clients;# CI/CD ejecuta migraciones automáticamente
npx supabase db push --db-url $SUPABASE_URLIaC-004: WAF Y SEGURIDAD COMO CÓDIGO
[REQUIRED] Por qué: las reglas de seguridad son las que más caro sale perder y las que más silenciosamente se tocan a mano durante un incidente. En código quedan revisadas, versionadas y con una razón asociada, en vez de ser un ajuste temporal que nadie recuerda haber hecho.
# Cloudflare WAF Rules
resource "cloudflare_ruleset" "waf" {
zone_id = var.zone_id
name = "default"
kind = "zone"
phase = "http_request_firewall_managed"
rules {
action = "block"
expression = "(ip.geoip.country ne \"US\" and ip.geoip.country ne \"MX\" and not cf.client.bot)"
description = "Block traffic outside US/MX"
enabled = true
}
rules {
action = "managed_challenge"
expression = "(cf.threat_score gt 14)"
description = "Challenge suspicious requests"
enabled = true
}
}