Si el internet tradicional es un vasto sistema de cables físicos y centros de datos, Cloudflare es el sistema operativo inteligente construido sobre él. Gestiona la seguridad, el enrutamiento del tráfico, el rendimiento y el cómputo en el borde global. Hoy en día es prácticamente imposible evitarlo — la mayoría de los servidores web que gestiono están expuestos primero a través del DNS de Cloudflare, y se benefician de inmediato de sus capacidades de seguridad y rendimiento.
La gestión del DNS en Cloudflare puede hacerse manualmente, y así era exactamente como gestionaba todos mis dominios hasta este artículo.
Primero, en NixOS, habilitar cf-terraforming y terraform. Esto puede ser lento ya que parece requerir compilación, al menos en mi máquina NixOS.
{ pkgs ? import <nixpkgs> { config.allowUnfree = true; } }:
pkgs.mkShell {
buildInputs = with pkgs; [
pkgs.terraform
pkgs.cf-terraforming
];
shellHook = ''
echo "Cloudflare IaC environment loaded!"
'';
}
Una opción más rápida es obtener el script directamente
Clonar el repositorio e inicializar el espacio de trabajo de Terraform:
terraform init
Luego exportar la configuración DNS de Cloudflare:
sh ./exportZones.sh
Dos cosas ocurrirán:
zones.tf se creará en la carpeta raíz junto con imports.tfdns_<domain>.tf y un archivo import_dns_<domain>.tf se generarán por dominioA continuación se muestra el contenido del script exportZones.sh:
#!/bin/sh
if [ -f ".env.local" ]; then
CLOUDFLARE_API_TOKEN=$(grep -E '^CLOUDFLARE_API_TOKEN=' .env.local | cut -d '=' -f 2- | tr -d '"' | tr -d "'")
CLOUDFLARE_ACCOUNT_ID=$(grep -E '^CLOUDFLARE_ACCOUNT_ID=' .env.local | cut -d '=' -f 2- | tr -d '"' | tr -d "'")
echo "Loaded API token from .env.local"
fi
if [ -z "$CLOUDFLARE_API_TOKEN" ]; then
echo "ERROR: CLOUDFLARE_API_TOKEN is missing from .env.local and environment." >&2
exit 1
fi
if [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
echo "ERROR: CLOUDFLARE_ACCOUNT_ID is missing from .env.local and environment." >&2
exit 1
fi
export CLOUDFLARE_API_TOKEN
export CLOUDFLARE_ACCOUNT_ID
echo "1. Generating zones file..."
cf-terraforming generate --resource-type cloudflare_zone > zones.tf
echo "1.1. Generating root-level zone imports..."
awk '
/resource "cloudflare_zone"/ {
if (match($0, /terraform_managed_resource_[a-f0-9]{32}_[0-9]+/)) {
resource_name = substr($0, RSTART, RLENGTH)
token = substr($0, RSTART + 27, RLENGTH - 27)
split(token, parts, "_")
zone_id = parts[1]
print "import {"
print " to = cloudflare_zone." resource_name
print " id = \"" zone_id "\""
print "}"
print ""
}
}
' "zones.tf" > "import_zones.tf"
echo "2. Processing zones and exporting DNS records to root..."
awk '
/resource "cloudflare_zone"/ {
if (match($0, /terraform_managed_resource_[a-f0-9]+/)) {
id = substr($0, RSTART + 27, RLENGTH - 27)
}
}
/^[ \t]*zone[ \t]*=/ {
if (match($0, /"[^"]+"/)) {
domain = substr($0, RSTART + 1, RLENGTH - 2)
}
if (id != "" && domain != "") {
print id "|" domain
id = ""
domain = ""
}
}
' "zones.tf" | while IFS='|' read -r zone_id domain_name; do
if [ -n "$zone_id" ] && [ -n "$domain_name" ]; then
DNS_FILE="dns_${domain_name}.tf"
IMPORTS_FILE="import_dns_${domain_name}.tf"
echo "Exporting DNS for: $domain_name -> $DNS_FILE"
CLOUDFLARE_ACCOUNT_ID="" cf-terraforming generate --resource-type cloudflare_record --zone "$zone_id" > "$DNS_FILE"
echo "Calculating imports for $domain_name (Zone ID: $zone_id)"
awk -v zid="$zone_id" '
/resource "cloudflare_record"/ {
if (match($0, /terraform_managed_resource_[a-f0-9]{32}_[0-9]+/)) {
resource_name = substr($0, RSTART, RLENGTH)
token = substr($0, RSTART + 27, RLENGTH - 27)
split(token, parts, "_")
rec_id = parts[1]
print "import {"
print " to = cloudflare_record." resource_name
print " id = \"" zid "/" rec_id "\""
print "}"
print ""
}
}
' "$DNS_FILE" > "$IMPORTS_FILE"
fi
done
echo "Done! Zones, import_zones.tf, and individual dns/import files are saved at the root."
Revisa los archivos de zonas y el export de DNS.
El repositorio debería verse así — los archivos de importación y el estado no se versionan:
.
├── backend.tf --> Optional: your backend configuration
├── domains --> The domains you configure in your Cloudflare account
│ ├── dns_alcanzatuproposito.com.tf
│ ├── dns_comunidadsana.com.tf
│ ├── dns_emely.es.tf
│ ├── dns_jugandoalacomidita.com.tf
│ ├── dns_miespazio.pe.tf
│ └── versions.tf
├── exportZones.sh
├── LICENSE
├── modules.tf
├── README.md
├── shell.nix
├── versions.tf
└── zones.tf --> Zone files
terraform plan -out "plan.tfplan"
Hemos exportado los imports, las zonas y los archivos DNS desde Cloudflare. Como los formatos de lectura y escritura difieren ligeramente, Terraform planificará con imports y cambios, pero sin adiciones ni eliminaciones.
...
Plan: 41 to import, 0 to add, 36 to change, 0 to destroy.
Saved the plan to: plan.tfplan
...
Ahora ejecuta apply para persistir el estado en tu backend:
terraform apply "plan.tfplan"
Tras esto, ejecutar terraform plan debería mostrar:
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
Esto te permite gestionar tu configuración DNS, planificar y aplicar cambios mediante Terraform, o conectar una GitHub Action para desplegar tu configuración DNS de forma automática. En el próximo artículo, veré cómo gestionar el estado de Terraform usando Forgejo Packages y configurar un backend de Terraform para enlazarlo.
Cloudflare · Terraform · Git