Cross-Cloud Networking

Connecting networks across different cloud providers is one of the most complex aspects of multi-cloud architecture. Each provider has different networking models, security requirements, and connectivity options. This part covers practical patterns for establishing secure, reliable connections between AWS, Azure, and GCP.

AWS-Azure VPN Connection

Establish site-to-site VPN between AWS and Azure:

# AWS side configuration
data "aws_availability_zones" "available" {
  state = "available"
}

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true
  
  tags = {
    Name = "aws-vpc"
  }
}

resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = data.aws_availability_zones.available.names[0]
  
  tags = {
    Name = "aws-private-subnet"
  }
}

resource "aws_vpn_gateway" "main" {
  vpc_id = aws_vpc.main.id
  
  tags = {
    Name = "aws-vpn-gateway"
  }
}

resource "aws_customer_gateway" "azure" {
  bgp_asn    = 65000
  ip_address = azurerm_public_ip.vpn_gateway.ip_address
  type       = "ipsec.1"
  
  tags = {
    Name = "azure-customer-gateway"
  }
}

resource "aws_vpn_connection" "azure" {
  vpn_gateway_id      = aws_vpn_gateway.main.id
  customer_gateway_id = aws_customer_gateway.azure.id
  type                = "ipsec.1"
  static_routes_only  = true
  
  tags = {
    Name = "aws-azure-vpn"
  }
}

resource "aws_vpn_connection_route" "azure" {
  vpn_connection_id      = aws_vpn_connection.azure.id
  destination_cidr_block = "10.1.0.0/16"
}
# Azure side configuration
resource "azurerm_resource_group" "main" {
  name     = "multi-cloud-rg"
  location = "East US"
}

resource "azurerm_virtual_network" "main" {
  name                = "azure-vnet"
  address_space       = ["10.1.0.0/16"]
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
}

resource "azurerm_subnet" "gateway" {
  name                 = "GatewaySubnet"
  resource_group_name  = azurerm_resource_group.main.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.1.255.0/27"]
}

resource "azurerm_public_ip" "vpn_gateway" {
  name                = "azure-vpn-gateway-ip"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

resource "azurerm_virtual_network_gateway" "main" {
  name                = "azure-vpn-gateway"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  
  type     = "Vpn"
  vpn_type = "RouteBased"
  
  active_active = false
  enable_bgp    = false
  sku           = "VpnGw1"
  
  ip_configuration {
    name                          = "vnetGatewayConfig"
    public_ip_address_id          = azurerm_public_ip.vpn_gateway.id
    private_ip_address_allocation = "Dynamic"
    subnet_id                     = azurerm_subnet.gateway.id
  }
}

resource "azurerm_local_network_gateway" "aws" {
  name                = "aws-local-gateway"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  
  gateway_address = aws_vpn_connection.azure.tunnel1_address
  address_space   = ["10.0.0.0/16"]
}

resource "azurerm_virtual_network_gateway_connection" "aws" {
  name                = "azure-aws-connection"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  
  type                       = "IPsec"
  virtual_network_gateway_id = azurerm_virtual_network_gateway.main.id
  local_network_gateway_id   = azurerm_local_network_gateway.aws.id
  
  shared_key = aws_vpn_connection.azure.tunnel1_preshared_key
}

AWS-GCP Interconnect

Establish dedicated connection between AWS and GCP:

# GCP side configuration
resource "google_compute_network" "main" {
  name                    = "gcp-vpc"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "private" {
  name          = "gcp-private-subnet"
  ip_cidr_range = "10.2.1.0/24"
  region        = "us-central1"
  network       = google_compute_network.main.id
}

resource "google_compute_router" "main" {
  name    = "gcp-router"
  region  = "us-central1"
  network = google_compute_network.main.id
  
  bgp {
    asn = 64512
  }
}

resource "google_compute_vpn_gateway" "main" {
  name    = "gcp-vpn-gateway"
  network = google_compute_network.main.id
  region  = "us-central1"
}

resource "google_compute_address" "vpn_static_ip" {
  name   = "gcp-vpn-ip"
  region = "us-central1"
}

resource "google_compute_vpn_tunnel" "aws" {
  name          = "gcp-aws-tunnel"
  peer_ip       = aws_vpn_connection.gcp.tunnel1_address
  shared_secret = aws_vpn_connection.gcp.tunnel1_preshared_key
  
  target_vpn_gateway = google_compute_vpn_gateway.main.id
  
  depends_on = [
    google_compute_forwarding_rule.esp,
    google_compute_forwarding_rule.udp500,
    google_compute_forwarding_rule.udp4500,
  ]
}

resource "google_compute_route" "aws" {
  name       = "route-to-aws"
  network    = google_compute_network.main.name
  dest_range = "10.0.0.0/16"
  priority   = 1000
  
  next_hop_vpn_tunnel = google_compute_vpn_tunnel.aws.id
}

Multi-Cloud Transit Gateway

Create a hub-and-spoke network topology:

# Central transit hub in AWS
resource "aws_ec2_transit_gateway" "hub" {
  description = "Multi-cloud transit hub"
  
  tags = {
    Name = "multi-cloud-tgw"
  }
}

resource "aws_ec2_transit_gateway_vpc_attachment" "aws_vpc" {
  subnet_ids         = [aws_subnet.private.id]
  transit_gateway_id = aws_ec2_transit_gateway.hub.id
  vpc_id             = aws_vpc.main.id
  
  tags = {
    Name = "aws-vpc-attachment"
  }
}

resource "aws_ec2_transit_gateway_vpn_attachment" "azure" {
  vpn_connection_id  = aws_vpn_connection.azure.id
  transit_gateway_id = aws_ec2_transit_gateway.hub.id
  
  tags = {
    Name = "azure-vpn-attachment"
  }
}

resource "aws_ec2_transit_gateway_route_table" "main" {
  transit_gateway_id = aws_ec2_transit_gateway.hub.id
  
  tags = {
    Name = "multi-cloud-route-table"
  }
}

resource "aws_ec2_transit_gateway_route" "azure" {
  destination_cidr_block         = "10.1.0.0/16"
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpn_attachment.azure.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.main.id
}

Network Automation Script

Automate cross-cloud network setup and validation:

#!/bin/bash
# scripts/setup-cross-cloud-network.sh

set -e

AWS_REGION=${1:-"us-west-2"}
AZURE_REGION=${2:-"East US"}
GCP_REGION=${3:-"us-central1"}

setup_aws_networking() {
    echo "Setting up AWS networking..."
    
    cd aws/
    terraform init
    terraform plan -var="region=$AWS_REGION"
    terraform apply -auto-approve -var="region=$AWS_REGION"
    
    # Export connection details
    terraform output -json > ../aws-outputs.json
    
    cd ..
}

setup_azure_networking() {
    echo "Setting up Azure networking..."
    
    # Import AWS VPN details
    AWS_VPN_IP=$(jq -r '.vpn_tunnel1_address.value' aws-outputs.json)
    AWS_PRESHARED_KEY=$(jq -r '.vpn_tunnel1_preshared_key.value' aws-outputs.json)
    
    cd azure/
    terraform init
    terraform plan \
        -var="location=$AZURE_REGION" \
        -var="aws_vpn_ip=$AWS_VPN_IP" \
        -var="aws_preshared_key=$AWS_PRESHARED_KEY"
    
    terraform apply -auto-approve \
        -var="location=$AZURE_REGION" \
        -var="aws_vpn_ip=$AWS_VPN_IP" \
        -var="aws_preshared_key=$AWS_PRESHARED_KEY"
    
    cd ..
}

setup_gcp_networking() {
    echo "Setting up GCP networking..."
    
    cd gcp/
    terraform init
    terraform plan -var="region=$GCP_REGION"
    terraform apply -auto-approve -var="region=$GCP_REGION"
    
    cd ..
}

validate_connectivity() {
    echo "Validating cross-cloud connectivity..."
    
    # Test AWS to Azure
    AWS_INSTANCE_IP=$(jq -r '.test_instance_private_ip.value' aws-outputs.json)
    AZURE_INSTANCE_IP=$(jq -r '.test_instance_private_ip.value' azure-outputs.json)
    
    echo "Testing AWS ($AWS_INSTANCE_IP) to Azure ($AZURE_INSTANCE_IP)..."
    
    # This would typically involve SSH to instances and running ping tests
    # For demo purposes, we'll just check VPN status
    
    aws ec2 describe-vpn-connections \
        --region "$AWS_REGION" \
        --query 'VpnConnections[0].State' \
        --output text
}

# Execute setup
setup_aws_networking
setup_azure_networking
setup_gcp_networking
validate_connectivity

echo "✅ Multi-cloud network setup completed"

Network Monitoring

Monitor cross-cloud network performance:

#!/usr/bin/env python3
# scripts/network_monitor.py

import boto3
import time
from azure.identity import DefaultAzureCredential
from azure.mgmt.network import NetworkManagementClient
from google.cloud import monitoring_v3

class MultiCloudNetworkMonitor:
    def __init__(self):
        self.aws_ec2 = boto3.client('ec2')
        self.aws_cloudwatch = boto3.client('cloudwatch')
        
    def check_aws_vpn_status(self, vpn_connection_id: str) -> dict:
        """Check AWS VPN connection status"""
        
        response = self.aws_ec2.describe_vpn_connections(
            VpnConnectionIds=[vpn_connection_id]
        )
        
        connection = response['VpnConnections'][0]
        
        return {
            'state': connection['State'],
            'tunnel1_state': connection['VgwTelemetry'][0]['Status'],
            'tunnel2_state': connection['VgwTelemetry'][1]['Status'],
            'tunnel1_accepted_routes': connection['VgwTelemetry'][0]['AcceptedRouteCount'],
            'tunnel2_accepted_routes': connection['VgwTelemetry'][1]['AcceptedRouteCount']
        }
    
    def get_network_metrics(self, vpn_connection_id: str) -> dict:
        """Get network performance metrics"""
        
        end_time = time.time()
        start_time = end_time - 3600  # Last hour
        
        metrics = {}
        
        # Get tunnel state metrics
        for tunnel_num in [1, 2]:
            response = self.aws_cloudwatch.get_metric_statistics(
                Namespace='AWS/VPN',
                MetricName='TunnelState',
                Dimensions=[
                    {'Name': 'VpnId', 'Value': vpn_connection_id},
                    {'Name': 'TunnelIpAddress', 'Value': f'tunnel-{tunnel_num}'}
                ],
                StartTime=start_time,
                EndTime=end_time,
                Period=300,
                Statistics=['Average']
            )
            
            metrics[f'tunnel_{tunnel_num}_uptime'] = len([
                dp for dp in response['Datapoints'] if dp['Average'] == 1
            ]) / len(response['Datapoints']) * 100 if response['Datapoints'] else 0
        
        return metrics
    
    def generate_report(self, vpn_connection_id: str) -> str:
        """Generate network status report"""
        
        status = self.check_aws_vpn_status(vpn_connection_id)
        metrics = self.get_network_metrics(vpn_connection_id)
        
        report = [
            "Multi-Cloud Network Status Report",
            "=" * 40,
            f"VPN Connection: {vpn_connection_id}",
            f"Overall State: {status['state']}",
            "",
            "Tunnel Status:",
            f"  Tunnel 1: {status['tunnel1_state']} ({status['tunnel1_accepted_routes']} routes)",
            f"  Tunnel 2: {status['tunnel2_state']} ({status['tunnel2_accepted_routes']} routes)",
            "",
            "Uptime (Last Hour):",
            f"  Tunnel 1: {metrics.get('tunnel_1_uptime', 0):.1f}%",
            f"  Tunnel 2: {metrics.get('tunnel_2_uptime', 0):.1f}%"
        ]
        
        return "\n".join(report)

def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='Multi-Cloud Network Monitor')
    parser.add_argument('--vpn-connection-id', required=True, help='AWS VPN Connection ID')
    
    args = parser.parse_args()
    
    monitor = MultiCloudNetworkMonitor()
    report = monitor.generate_report(args.vpn_connection_id)
    
    print(report)

if __name__ == "__main__":
    main()

What’s Next

Cross-cloud networking provides the foundation for multi-cloud architecture, but managing identities and permissions across providers requires unified identity strategies. In the next part, we’ll explore how to implement consistent access control and identity management across AWS, Azure, and GCP.