Problema
En entornos de producción es habitual depender de servicios gestionados (Blob storage, bases de datos, colas, etc.) que se encuentran en una única región o proveedor. Cuando uno de esos recursos sufre una interrupción —por ejemplo, una caída del servicio de Azure Blob en “East US”— la aplicación deja de responder y el equipo de guardia puede no estar disponible. La pregunta central es: ¿cómo reaccionar de forma automática, sin introducir deriva de configuración y sin depender de procesos manuales?
El desafío no es solo crear el recurso de reemplazo; es mantener la coherencia con el repositorio de IaC, evitar dos fuentes de verdad y, al mismo tiempo, permitir ajustes dinámicos (cambio de SKU, escalado) basados en métricas reales.
Causa
Las causas más frecuentes de este tipo de incidentes son:
- Fallos de zona o región – Azure, AWS y GCP publican incidentes de zona que pueden afectar a todos los recursos de esa zona.
- Problemas de plataforma – Un bug en el servicio gestionado o una actualización que rompe la disponibilidad.
- Sobrecarga inesperada – Picos de tráfico que saturan el SKU provisionado, provocando throttling o time‑outs.
- Errores de configuración – Políticas de red o de IAM que impiden que una réplica en otra región sea accesible.
En la práctica, la mayoría de los equipos tienen monitorización (Azure Monitor, CloudWatch) pero no un mecanismo que convierta una alerta en una corrección de infraestructura sin intervención humana.
Solución
Una estrategia reutilizable combina tres capas:
- Detección basada en alertas estructuradas
- Usa métricas y logs (por ejemplo,
ResourceHealtho consultas KQL) para generar alertas que incluyan un payload estructurado (tipo, región, recurso).
- Usa métricas y logs (por ejemplo,
- Motor de decisión declarativo
- Define reglas condition → action en un archivo YAML dentro del mismo repositorio de Terraform. Cada regla especifica:
- Condición: expresión sobre la alerta (p. ej.,
resource.type == "azurerm_storage_account"yregion == "eastus"ystatus == "unhealthy"). - Acción: cambios a aplicar (cambiar
location, actualizarsku, crear recurso de respaldo).
- Condición: expresión sobre la alerta (p. ej.,
- Define reglas condition → action en un archivo YAML dentro del mismo repositorio de Terraform. Cada regla especifica:
- Ejecutor automatizado
- Un GitHub Action programado (cron) que:
- Consulta la API de monitorización.
- Evalúa las reglas contra las alertas recibidas.
- Modifica los archivos
.tfcorrespondientes (añade un nuevo bloque o actualiza variables). - Abre un Pull Request automáticamente; opcionalmente, si la regla está marcada como
auto‑apply, ejecutaterraform applydirectamente en un workspace aislado.
- Un GitHub Action programado (cron) que:
Paso a paso
1. Definir reglas en infra_rules.yaml
rules:
- name: "Failover storage account"
condition:
resource_type: "azurerm_storage_account"
region: "eastus"
health: "unhealthy"
action:
file: "modules/storage/main.tf"
replace:
location: "westus2"
sku: "Standard_LRS"
auto_apply: false
- name: "Scale up App Service on traffic spike"
condition:
resource_type: "azurerm_app_service"
metric: "HttpRequests"
threshold: 5000
period_minutes: 5
direction: "above"
action:
file: "modules/appservice/variables.tf"
replace:
sku_name: "P2v2"
auto_apply: true
Cada regla es autocontenida; el motor solo necesita saber qué archivo tocar y qué valores reemplazar.
2. GitHub Action que ejecuta el motor
name: Infra auto‑recovery
on:
schedule:
- cron: '*/5 * * * *' # cada 5 minutos
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install jq and yq
run: sudo apt-get update && sudo apt-get install -y jq yq
- name: Fetch alerts from Azure Monitor
env:
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: |
az login --service-principal -u $AZURE_CLIENT_ID -p $AZURE_CLIENT_SECRET --tenant $AZURE_TENANT_ID
az monitor metrics alert list --resource-group my-rg > alerts.json
- name: Evaluate rules
run: |
python3 scripts/evaluate_rules.py infra_rules.yaml alerts.json
- name: Create PR if changes
if: steps.evaluate.outputs.changes == 'true'
uses: peter-evans/create-pull-request@v5
with:
commit-message: "Auto‑recovery: apply rule ${{ steps.evaluate.outputs.rule_name }}"
branch: auto-recovery/${{ steps.evaluate.outputs.rule_name }}
title: "[Auto‑Recovery] ${{ steps.evaluate.outputs.rule_name }}"
body: "Cambios generados automáticamente por la regla de recuperación."
El script evaluate_rules.py carga las alertas, compara con las condiciones y, cuando coincide, usa yq para editar el archivo .tf. Si auto_apply es true, el mismo workflow ejecuta:
terraform init
terraform plan -out=tfplan
terraform apply -auto-approve tfplan
En entornos críticos se recomienda limitar auto_apply a cambios de escalado o a recursos sin datos persistentes.
3. Mantener la fuente única de verdad
Todas las modificaciones se hacen sobre los archivos versionados. El PR permite revisión humana; si se aprueba, el pipeline de CI/CD habitual ejecuta terraform apply en los entornos de producción. De esta forma no hay “drift” porque el estado deseado siempre proviene del repositorio.
Cuándo aplicar esta solución
Utilízala cuando:
- Tienes recursos sin datos críticos (almacenamiento estático, colas, caches) que pueden ser recreados rápidamente en otra región.
- Los costos de downtime superan el riesgo de cambios automáticos (SLA estricto, transacciones en tiempo real).
- Tu pipeline IaC ya está integrado con GitHub Actions o Azure Pipelines; de lo contrario, la inversión en la automatización será mayor que el beneficio.
No es adecuada si:
- El recurso contiene datos que requieren replicación consistente (bases de datos con transacciones).
- La política de tu organización prohíbe despliegues sin revisión humana.
- El número de reglas supera la capacidad de mantenimiento manual; en ese caso, una solución SaaS especializada puede ser más rentable.
Código
# Ejemplo de edición de Terraform con yq (instalado en el job)
yq eval '.resource.azurerm_storage_account.myaccount.location = "westus2"' -i modules/storage/main.tf
yq eval '.resource.azurerm_storage_account.myaccount.sku.name = "Standard_LRS"' -i modules/storage/main.tf
git add modules/storage/main.tf
git commit -m "Auto-recovery: switch storage to westus2"
Verificación
- Simular una alerta: crea una alerta falsa con
az monitor metrics alert createque marque el recurso comounhealthy. - Ejecutar el workflow: dispara manualmente el GitHub Action (
workflow_dispatch). - Revisar el PR: verifica que el archivo
.tfcontiene los cambios esperados. - Aplicar en entorno de pruebas: ejecuta
terraform applyen un workspace de staging y confirma que el recurso nuevo está operativo en la región indicada. - Monitorizar: una vez aplicado, revisa que la métrica de salud vuelve a
healthyy que no aparecen nuevos eventos de drift.
Notas adicionales
- Idempotencia: las reglas deben ser idempotentes; si el recurso ya está en la región objetivo, el motor no debe volver a generar cambios.
- Seguridad de secretos: usa los secretos de GitHub para credenciales de Azure; nunca los almacenes en el repositorio.
- Limitaciones de API: la frecuencia de polling está sujeta a cuotas; ajusta el cron a un intervalo que no supere los límites de tu suscripción.
- Rollback: incluye una regla espejo que revierta los cambios cuando la métrica vuelva a la normalidad (
auto_apply: trueen la regla de recuperación inversa). - Auditoría: habilita
terraform planen modo JSON y almacena los resultados en un bucket de logs para cumplir con requisitos de compliance.
Con este enfoque se consigue una respuesta automática a fallas de infraestructura, se preserva la única fuente de verdad (el repositorio IaC) y se mantiene la posibilidad de revisión humana cuando el impacto lo justifica. La clave está en tratar la detección, la decisión y la ejecución como componentes desacoplados pero orquestados mediante la misma cadena de CI/CD que ya utilizas.