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
| Metrica | Antes | Despues | Como Rastrear |
|---|---|---|---|
| Incidentes cross-cloud | Desconocido | 0 | Reportes de incidentes |
| Cobertura de test por cloud | Variable | 80%+ cada uno | Reportes de cobertura |
| Duracion media de test | N/A | <15 min | Metricas CI |
| Verificacion de paridad cloud | Manual | Automatizado | Aserciones 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:
- Mapea que recursos en cloud A dependen de recursos en cloud B
- Escribe tests para esas interacciones especificas primero
- Construye libreria de aserciones agnósticas de cloud incrementalmente
- Expande a cobertura completa de infraestructura por cloud
- 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:
- Testing de Infraestructura AWS con LocalStack
- Testing de Infraestructura Azure
- Terratest para Infrastructure as Code
- Testing de Infrastructure as Code
Recursos externos: