Problema

Muchas organizaciones están dejando de depender de un único proveedor y adoptan estrategias multi‑cloud o cambian de proveedor al iniciar un nuevo proyecto. El desafío no es solo aprender la consola de otro vendor; es mantener la coherencia operativa cuando los equipos gestionan clústers de Kubernetes, pipelines de CI/CD y recursos de infraestructura en distintas nubes.

En la práctica, los ingenieros se enfrentan a:

  • Re‑escritura de scripts de provisión que estaban atados a recursos específicos de AWS, GCP o Azure.
  • Inconsistencias en la configuración de Helm charts entre clústers que, aunque idénticos a nivel de Kubernetes, usan diferentes tipos de discos, balanceadores o IAM.
  • Dificultad para aplicar políticas de seguridad y observabilidad de forma homogénea.

El problema se vuelve crítico cuando el tiempo de entrega se reduce y la presión por evitar interrupciones aumenta. La solución necesita ser reutilizable, no un parche puntual para un solo proveedor.

Causa

  1. Acoplamiento a APIs propietarias – Cuando el código de Terraform, scripts de Bash o pipelines de Jenkins hacen referencia directa a recursos como aws_eks_cluster o google_container_cluster, la portabilidad se rompe.
  2. Falta de abstracción en los módulos de IaC – Módulos monolíticos que incluyen tanto la lógica de red como la de compute mezclan conceptos que varían entre nubes.
  3. Gestión de estados aislados – Cada proveedor mantiene su propio backend de estado (S3, GCS, Azure Blob). Sin una estrategia centralizada, la sincronización entre entornos se vuelve manual y propensa a errores.
  4. Políticas de seguridad divergentes – IAM en AWS, IAM en GCP y RBAC en Azure tienen sintaxis distintas; replicar las mismas reglas a mano genera desalineación.
  5. Pipeline de CI/CD rígido – Pipelines que asumen una única variable de entorno para el provider terminan fallando al cambiar de contexto.

Solución

Adoptar una arquitectura provider‑agnostic basada en tres capas:

  1. Módulos Terraform genéricos

    • Separar la lógica de infraestructura común (VPC, subnets, IAM roles) de la lógica específica del provider.
    • Cada módulo expone variables como cidr_block, instance_type y un bloque provider_config que se llena con los valores del vendor.
    • Utilizar el patrón “wrapper module” que delega al módulo nativo (aws_vpc, google_compute_network, etc.) según el valor de var.cloud_provider.
  2. Kubernetes como capa de convergencia

    • Mantener los Helm charts idénticos en todos los clústers.
    • Parametrizar los valores que cambian entre nubes (por ejemplo, service.type, storageClass) mediante values.yaml generados por Terraform (helm_release con set dinámico).
    • Usar Cluster Autoscaler y External DNS con proveedores específicos, pero encapsular su instalación en scripts que reciben el nombre del provider.
  3. CI/CD y gestión de estado centralizada

    • Configurar un backend de Terraform remoto común, como Terraform Cloud o un bucket S3 replicado, y usar workspaces para separar dev, staging y prod.
    • En los pipelines, definir una variable CLOUD_PROVIDER y cargar el provider config correspondiente antes de ejecutar terraform init.
    • Añadir pruebas de plan automáticas que comparen el diff entre proveedores; cualquier recurso inesperado detiene el despliegue.

Ejemplo de wrapper module

module "network" {
  source         = "./modules/network"
  cloud_provider = var.cloud_provider   # "aws", "gcp" o "azure"
  cidr_block     = var.cidr_block
  region         = var.region
}

Dentro de ./modules/network/main.tf:

variable "cloud_provider" {}
variable "cidr_block" {}
variable "region" {}

# Provider‑specific resources
resource "aws_vpc" "this" {
  count = var.cloud_provider == "aws" ? 1 : 0
  cidr_block = var.cidr_block
  tags = { Name = "vpc-aws-${var.region}" }
}

resource "google_compute_network" "this" {
  count = var.cloud_provider == "gcp" ? 1 : 0
  name = "vpc-gcp-${var.region}"
  auto_create_subnetworks = false
}

Este patrón permite añadir nuevos proveedores sin tocar la lógica de negocio.

Cuándo aplicar esta solución

  • Migraciones planificadas entre AWS, GCP o Azure, donde el tiempo de inactividad debe ser mínimo.
  • Entornos multi‑cloud que requieren despliegues simultáneos en al menos dos proveedores.
  • Equipos con experiencia en Kubernetes que buscan reutilizar Helm charts y pipelines.

No es la mejor opción cuando:

  • La arquitectura depende de servicios exclusivos de un vendor (por ejemplo, AWS Lambda con triggers específicos).
  • El proyecto es un prototipo de corta duración y la sobrecarga de abstraer el provider no se justifica.

Código

# Inicializar Terraform con backend remoto y seleccionar workspace
terraform init -backend-config="bucket=my-tf-state" -backend-config="key=${var.cloud_provider}/state.tfstate"
terraform workspace select ${ENVIRONMENT} || terraform workspace new ${ENVIRONMENT}

# Generar values.yaml para Helm según provider
cat <<EOF > helm-values-${var.cloud_provider}.yaml
service:
  type: ${var.cloud_provider == "aws" ? "LoadBalancer" : "NodePort"}
storageClass: ${var.cloud_provider == "gcp" ? "standard" : "gp2"}
EOF

# Deploy Helm chart con valores dinámicos
helm upgrade --install my-app ./charts/my-app \
  -f helm-values-${var.cloud_provider}.yaml \
  --set image.tag=${CI_COMMIT_SHA}

Verificación

  1. Ejecutar terraform plan en cada workspace y confirmar que el número de recursos creados coincide con el esperado para el provider.
  2. Verificar que el clúster Kubernetes se levante sin errores de helm install.
  3. Comprobar que los endpoints de la aplicación responden tanto en AWS como en GCP mediante curl -I https://my-app.${var.cloud_provider}.example.com.
  4. Revisar los logs de CI/CD; cualquier desviación en el plan debe generar una alerta.

Notas adicionales

  • Naming convention: incluye siempre el provider en los nombres de recursos (vpc-aws-us-east-1) para evitar colisiones al replicar el estado.
  • Políticas de IAM: crea plantillas JSON genéricas y usa terraform para rellenar los ARN específicos del provider.
  • Testing: emplea kitchen-terraform o Terratest para validar módulos en cada nube antes de mergear a producción.
  • Costos ocultos: al replicar infraestructura, revisa los precios de egress y almacenamiento; lo que parece idéntico puede tener facturación distinta.
  • Documentación interna: mantén un README por módulo que indique qué variables son provider‑specific y cuáles son common.

Adoptar esta estrategia reduce la fricción al pasar de un cloud a otro, permite que los equipos reutilicen sus conocimientos de Kubernetes y Helm, y mantiene la infraestructura bajo control mediante Terraform. Con una capa de abstracción bien definida, el salto entre proveedores deja de ser una tarea de último minuto y se convierte en un proceso repetible y fiable.