TL;DR

  • El testing multi-cloud requiere aserciones agnósticas de proveedor y setup/teardown especificos por cloud
  • Usa Terratest con multiples configuraciones de proveedor, no suites de test separadas por cloud
  • El error #1: testear clouds en aislamiento en lugar de testear interacciones cross-cloud

Ideal para: Organizaciones con workloads distribuidos en AWS, Azure y/o GCP Omite si: Estas comprometido con un solo proveedor cloud sin planes de migracion Tiempo de lectura: 11 minutos

Tu empresa corre Kubernetes en GKE, bases de datos en AWS RDS e identidad en Azure AD. Un cambio de Terraform necesita funcionar en los tres. Como testeas que el networking permite comunicacion cross-cloud? Que el DNS resuelve correctamente? Que los permisos IAM funcionan end-to-end?

El testing de infraestructura multi-cloud es dificil porque cada proveedor tiene APIs diferentes, modelos de recursos diferentes y modos de falla diferentes. Pero en 2026, multi-cloud no es opcional para muchas organizaciones — es realidad. Tu estrategia de testing necesita coincidir.

El Problema Real

El testing single-cloud es directo: levantar recursos, validar, destruir. Multi-cloud introduce complejidad:

Diferentes modelos de autenticacion: AWS usa IAM roles, Azure usa service principals, GCP usa service accounts. Tu test runner necesita credenciales para todos.

Diferentes ciclos de vida de recursos: Un recurso Azure podria tomar 5 minutos para provisionar mientras el equivalente AWS toma 30 segundos. Timeouts y reintentos necesitan tuning especifico por cloud.

Dependencias cross-cloud: Tu app en GCP necesita alcanzar una base de datos en AWS. Testear esto requiere ambos clouds corriendo simultaneamente, con networking configurado.

APIs inconsistentes: El SDK de cada cloud se comporta diferente. Manejo de errores, paginacion y consistencia eventual varian.

Arquitectura de Testing

El insight clave: los modulos de test deben ser agnósticos de cloud donde sea posible, con implementaciones especificas de cloud conectadas.

tests/
├── integration/
│   ├── network_test.go          # Aserciones de red agnósticas
│   ├── database_test.go         # Aserciones de DB agnósticas
│   └── identity_test.go         # Aserciones de identidad agnósticas
├── providers/
│   ├── aws/
│   │   └── setup.go             # Setup de test especifico AWS
│   ├── azure/
│   │   └── setup.go             # Setup de test especifico Azure
│   └── gcp/
│       └── setup.go             # Setup de test especifico GCP
└── fixtures/
    ├── aws/
    ├── azure/
    └── gcp/

Patron Multi-Proveedor de Terratest

Terratest soporta testing entre clouds. El patron: inicializar multiples proveedores, deployar a cada uno, luego ejecutar aserciones cross-cloud.

package test

import (
    "testing"
    "time"

    "github.com/gruntwork-io/terratest/modules/aws"
    "github.com/gruntwork-io/terratest/modules/azure"
    "github.com/gruntwork-io/terratest/modules/gcp"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestMultiCloudNetworking(t *testing.T) {
    t.Parallel()

    // Deploy infraestructura AWS
    awsOpts := &terraform.Options{
        TerraformDir: "../fixtures/aws/networking",
        Vars: map[string]interface{}{
            "environment": "test",
            "vpc_cidr":    "10.0.0.0/16",
        },
        EnvVars: map[string]string{
            "AWS_DEFAULT_REGION": "us-east-1",
        },
    }
    defer terraform.Destroy(t, awsOpts)
    terraform.InitAndApply(t, awsOpts)

    // Deploy infraestructura GCP
    gcpOpts := &terraform.Options{
        TerraformDir: "../fixtures/gcp/networking",
        Vars: map[string]interface{}{
            "environment": "test",
            "vpc_cidr":    "10.1.0.0/16",
        },
        EnvVars: map[string]string{
            "GOOGLE_PROJECT": "my-project",
            "GOOGLE_REGION":  "us-central1",
        },
    }
    defer terraform.Destroy(t, gcpOpts)
    terraform.InitAndApply(t, gcpOpts)

    // Obtener outputs para validacion cross-cloud
    awsVpcId := terraform.Output(t, awsOpts, "vpc_id")
    gcpNetworkName := terraform.Output(t, gcpOpts, "network_name")

    // Validar lado AWS
    vpc := aws.GetVpcById(t, awsVpcId, "us-east-1")
    assert.Equal(t, "10.0.0.0/16", vpc.CidrBlock)

    // Validar lado GCP
    network := gcp.GetNetwork(t, "my-project", gcpNetworkName)
    assert.True(t, network.AutoCreateSubnetworks == false)

    // Testear conectividad cross-cloud (via VPN/interconnect)
    testCrossCloudConnectivity(t, awsOpts, gcpOpts)
}

func testCrossCloudConnectivity(t *testing.T, awsOpts, gcpOpts *terraform.Options) {
    awsInstanceIP := terraform.Output(t, awsOpts, "test_instance_private_ip")
    gcpInstanceIP := terraform.Output(t, gcpOpts, "test_instance_private_ip")

    // SSH a instancia AWS y ping a instancia GCP
    aws.CheckSshCommand(t,
        terraform.Output(t, awsOpts, "test_instance_public_ip"),
        "ubuntu",
        fmt.Sprintf("ping -c 3 %s", gcpInstanceIP),
    )
}

Manejando Autenticacion de Proveedores

Los tests multi-cloud necesitan credenciales para cada proveedor. Usa variables de entorno con prefijos:

# Credenciales AWS
export AWS_ACCESS_KEY_ID="..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_DEFAULT_REGION="us-east-1"

# Credenciales Azure
export ARM_CLIENT_ID="..."
export ARM_CLIENT_SECRET="..."
export ARM_SUBSCRIPTION_ID="..."
export ARM_TENANT_ID="..."

# Credenciales GCP
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
export GOOGLE_PROJECT="my-project"

Para CI/CD, usa OIDC donde sea posible:

# GitHub Actions con OIDC multi-cloud
jobs:
  multi-cloud-test:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/github-actions
          aws-region: us-east-1

      - name: Configure Azure credentials
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Configure GCP credentials
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/123/locations/global/workloadIdentityPools/github/providers/github-actions
          service_account: github-actions@my-project.iam.gserviceaccount.com

      - name: Run multi-cloud tests
        run: go test -v ./tests/integration/...

Aserciones de Test Agnósticas de Cloud

Escribe aserciones que funcionen independientemente del proveedor:

package assertions

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

// NetworkConfig representa propiedades de red agnósticas de cloud
type NetworkConfig struct {
    CIDR          string
    SubnetCount   int
    HasNATGateway bool
    HasVPNGateway bool
}

// DatabaseConfig representa propiedades de DB agnósticas de cloud
type DatabaseConfig struct {
    Engine            string // "postgres", "mysql"
    Version           string
    MultiAZ           bool
    EncryptedAtRest   bool
    BackupRetention   int
}

// AssertNetworkConfig valida red independientemente del cloud
func AssertNetworkConfig(t *testing.T, expected, actual NetworkConfig) {
    assert.Equal(t, expected.CIDR, actual.CIDR, "CIDR mismatch")
    assert.Equal(t, expected.SubnetCount, actual.SubnetCount, "Subnet count mismatch")
    assert.Equal(t, expected.HasNATGateway, actual.HasNATGateway, "NAT gateway mismatch")
}

// AssertDatabaseConfig valida base de datos independientemente del cloud
func AssertDatabaseConfig(t *testing.T, expected, actual DatabaseConfig) {
    assert.Equal(t, expected.Engine, actual.Engine)
    assert.True(t, actual.EncryptedAtRest, "Database must be encrypted at rest")
    assert.GreaterOrEqual(t, actual.BackupRetention, 7, "Backup retention must be >= 7 days")
}

Luego implementa adaptadores especificos por cloud:

// aws/adapter.go
func GetNetworkConfig(t *testing.T, vpcId, region string) NetworkConfig {
    vpc := aws.GetVpcById(t, vpcId, region)
    subnets := aws.GetSubnetsForVpc(t, vpcId, region)
    natGateways := aws.GetNatGatewaysForVpc(t, vpcId, region)

    return NetworkConfig{
        CIDR:          vpc.CidrBlock,
        SubnetCount:   len(subnets),
        HasNATGateway: len(natGateways) > 0,
    }
}

// gcp/adapter.go
func GetNetworkConfig(t *testing.T, projectId, networkName string) NetworkConfig {
    network := gcp.GetNetwork(t, projectId, networkName)
    subnets := gcp.GetSubnetsForNetwork(t, projectId, networkName)
    routers := gcp.GetRoutersForNetwork(t, projectId, networkName)

    hasNAT := false
    for _, router := range routers {
        if len(router.Nats) > 0 {
            hasNAT = true
            break
        }
    }

    return NetworkConfig{
        CIDR:          subnets[0].IpCidrRange, // GCP usa CIDR de subnet
        SubnetCount:   len(subnets),
        HasNATGateway: hasNAT,
    }
}

Testeando Servicios Cross-Cloud

Algunos tests deben validar comunicacion cross-cloud real:

func TestCrossCloudDatabaseAccess(t *testing.T) {
    // Deploy AWS RDS
    awsOpts := terraform.Options{
        TerraformDir: "../fixtures/aws/rds",
    }
    defer terraform.Destroy(t, &awsOpts)
    terraform.InitAndApply(t, &awsOpts)

    // Deploy GCP VM que conecta a AWS RDS
    gcpOpts := terraform.Options{
        TerraformDir: "../fixtures/gcp/client-vm",
        Vars: map[string]interface{}{
            "database_host": terraform.Output(t, &awsOpts, "rds_endpoint"),
        },
    }
    defer terraform.Destroy(t, &gcpOpts)
    terraform.InitAndApply(t, &gcpOpts)

    // Ejecutar query SQL desde GCP VM a AWS RDS
    vmIP := terraform.Output(t, &gcpOpts, "instance_ip")
    result := gcp.RunCommandOnInstance(t, vmIP, "psql -h $DB_HOST -c 'SELECT 1'")
    assert.Contains(t, result, "1 row")
}

Testing Paralelo vs Secuencial

Los tests multi-cloud pueden correr en paralelo entre clouds pero pueden necesitar ejecucion secuencial dentro de un cloud:

func TestMultiCloudInParallel(t *testing.T) {
    t.Parallel()

    // Estos pueden correr simultaneamente
    t.Run("AWS", func(t *testing.T) {
        t.Parallel()
        testAWSInfrastructure(t)
    })

    t.Run("Azure", func(t *testing.T) {
        t.Parallel()
        testAzureInfrastructure(t)
    })

    t.Run("GCP", func(t *testing.T) {
        t.Parallel()
        testGCPInfrastructure(t)
    })
}

func TestCrossCloudDependent(t *testing.T) {
    // Estos deben correr secuencialmente - GCP depende de AWS
    awsResult := testAWSNetworking(t)
    testGCPWithAWSConnection(t, awsResult)
}

Enfoques Asistidos por IA

El testing multi-cloud involucra entender multiples APIs de proveedores y sus diferencias. Las herramientas de IA aceleran esto.

Lo que la IA hace bien:

  • Traducir logica de test del SDK de un cloud a otro
  • Identificar recursos equivalentes entre clouds (RDS vs Cloud SQL vs Azure Database)
  • Generar frameworks de aserciones agnósticas de cloud
  • Explicar diferencias en comportamiento de recursos entre proveedores

Lo que aun necesita humanos:

  • Disenar arquitectura de test para dependencias cross-cloud
  • Decidir que propiedades realmente necesitan validacion cross-cloud
  • Entender requerimientos de negocio que impulsan multi-cloud
  • Debuguear problemas de autenticacion especificos de cloud

Prompt util:

Tengo un Terratest que valida AWS VPC con estas propiedades:
- 3 subnets en AZs
- NAT gateway en cada AZ
- VPC flow logs habilitados

Genera codigo de test equivalente para:
1. GCP VPC Network
2. Azure Virtual Network
Incluye aserciones agnósticas de cloud que funcionen para los tres.

Cuando Esto Falla

El testing multi-cloud tiene limitaciones:

Multiplicacion de costos: Correr tests en tres clouds cuesta 3x. Ambientes efimeros ayudan pero no eliminan el costo.

Complejidad de credenciales: Gestionar OIDC o service accounts para multiples clouds en CI no es trivial. Rotacion, least-privilege y audit trails se multiplican.

Inconsistencias de tiempo: Azure podria tomar 10 minutos para un recurso que AWS provisiona en 1 minuto. Los tests necesitan timeouts especificos por cloud.

Gaps de paridad de features: No todo cloud tiene servicios equivalentes. Testear “database” funciona, pero testear “Aurora Serverless” no tiene equivalente Azure.

Considera enfocarte:

  • Testear interacciones cross-cloud exhaustivamente (son riesgos unicos)
  • Testear features especificos de cloud en suites single-cloud
  • Usar contract tests para aserciones agnósticas de cloud

Framework de Decision

Usa tests multi-cloud unificados cuando:

  • Workloads realmente abarcan multiples clouds
  • Networking cross-cloud esta configurado (VPN, interconnect)
  • Un solo equipo es dueno de infraestructura en todos los clouds

Usa tests separados por cloud cuando:

  • Cada cloud sirve workloads diferentes (sin dependencias cross-cloud)
  • Diferentes equipos son duenos de diferentes clouds
  • Features especificos de cloud dominan sobre patrones comunes

Usa capas de abstraccion cuando:

  • Planeas migracion de cloud o futuro multi-cloud
  • Quieres infraestructura portable (modulos agnósticos de cloud)
  • Optimizacion de costos impulsa seleccion de cloud dinamicamente

Midiendo el Exito

MetricaAntesDespuesComo Rastrear
Incidentes cross-cloudDesconocido0Reportes de incidentes
Cobertura de test por cloudVariable80%+ cada unoReportes de cobertura
Duracion media de testN/A<15 minMetricas CI
Verificacion de paridad cloudManualAutomatizadoAserciones de test

Senales de alarma de que no funciona:

  • Tests pasando pero issues cross-cloud en produccion
  • Omitiendo clouds en CI por problemas de credenciales
  • Massive .gitignore para outputs de test especificos de cloud
  • Diferentes equipos escribiendo tests duplicados por cloud

Que Sigue

Empieza con tus puntos de contacto cross-cloud reales:

  1. Mapea que recursos en cloud A dependen de recursos en cloud B
  2. Escribe tests para esas interacciones especificas primero
  3. Construye libreria de aserciones agnósticas de cloud incrementalmente
  4. Expande a cobertura completa de infraestructura por cloud
  5. Corre tests cross-cloud en cada PR que afecte modulos compartidos

El objetivo no es testear cada cloud igual — es testear las costuras donde los clouds conectan.


Articulos relacionados:

Recursos externos: