Multi-Cloud Networking: Transit Gateway Patterns for AWS and Azure
Why Multi-Cloud Networking Is Harder Than It Looks
Most architecture diagrams show AWS and Azure connected with a single line labeled "VPN" and call it done. In production, that line hides dozens of routing decisions, redundancy requirements, bandwidth constraints, and monthly bills that nobody budgeted for.
I've helped three enterprise teams migrate from single-cloud to multi-cloud architectures. The network layer is consistently where timelines slip. Not because the technology is immature — AWS Transit Gateway and Azure Virtual WAN are both solid products — but because the patterns aren't well documented and the cost model is genuinely surprising.
This article covers the three connectivity patterns worth knowing, when to use each, and the exact commands to validate your setup.
The Three Patterns
Pattern 1: Site-to-Site VPN (Low Traffic, Low Cost)
The simplest option. You create a VPN connection from AWS Transit Gateway to Azure Virtual Network Gateway. Encrypted, BGP-capable, but limited to 1.25 Gbps per tunnel.
When to use it: Dev/staging environments, low-bandwidth workloads, initial PoC before committing to Express Route.
AWS side:
# Create a Transit Gateway
aws ec2 create-transit-gateway \
--description "Multi-cloud TGW" \
--options AmazonSideAsn=64512,AutoAcceptSharedAttachments=enable,DefaultRouteTableAssociation=enable
# Create Customer Gateway pointing at Azure VPN Gateway public IP
aws ec2 create-customer-gateway \
--type ipsec.1 \
--bgp-asn 65515 \
--public-ip <AZURE_VPN_GW_PUBLIC_IP>
# Create VPN connection
aws ec2 create-vpn-connection \
--type ipsec.1 \
--customer-gateway-id cgw-XXXXXXXX \
--transit-gateway-id tgw-XXXXXXXX \
--options TunnelOptions='[{PreSharedKey="changeme123!",TunnelInsideCidr="169.254.21.0/30"},{PreSharedKey="changeme456!",TunnelInsideCidr="169.254.22.0/30"}]'
Azure side (Terraform):
resource "azurerm_virtual_network_gateway" "vpn" {
name = "azure-vpn-gateway"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
type = "Vpn"
vpn_type = "RouteBased"
sku = "VpnGw2"
enable_bgp = true
bgp_settings {
asn = 65515
}
ip_configuration {
name = "vnetGatewayConfig"
public_ip_address_id = azurerm_public_ip.vpn.id
private_ip_address_allocation = "Dynamic"
subnet_id = azurerm_subnet.gateway.id
}
}
resource "azurerm_local_network_gateway" "aws" {
name = "aws-tgw-lgw"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
gateway_address = "<AWS_TGW_TUNNEL_IP>"
bgp_settings {
asn = 64512
bgp_peering_address = "169.254.21.1"
}
}
Pattern 2: ExpressRoute + Direct Connect (High Throughput, Production)
For production workloads with > 1 Gbps requirements or strict latency SLAs, you need dedicated circuits. Both clouds offer this through their carrier partners.
Key architecture point: You terminate both circuits at a colocation facility (Equinix, Megaport, or similar). The exchange provider creates a virtual cross-connect between your AWS Direct Connect and Azure ExpressRoute ports. Traffic never traverses the public internet.
AWS VPC
└── Transit Gateway
└── Direct Connect Gateway
└── Direct Connect (e.g. Equinix DC2)
|
[Exchange Fabric]
|
└── ExpressRoute (Equinix DC2)
└── Virtual Network Gateway
└── Azure VNet
Verify BGP sessions on AWS:
# Check Direct Connect virtual interface BGP state
aws directconnect describe-virtual-interfaces \
--query 'virtualInterfaces[*].{Name:virtualInterfaceName,BGP:bgpPeers[0].bgpStatus,State:virtualInterfaceState}'
Verify on Azure:
az network express-route list-route-tables \
--resource-group myRG \
--name myCircuit \
--peering-name AzurePrivatePeering \
--path primary \
--query 'value[*].{Prefix:network,NextHop:nextHopIP,ASPath:asPath}'
Pattern 3: SD-WAN Overlay (Multi-Region, Complex Topologies)
When you have 10+ VPCs across 3+ regions on two clouds, you outgrow static routing tables. SD-WAN products like Cisco Viptela, VMware VeloCloud, or Aviatrix sit above the underlying connectivity and handle policy-based routing, application-aware path selection, and automatic failover.
This pattern adds licensing cost but dramatically reduces operational overhead at scale.
Cost Comparison
| Pattern | Setup Complexity | Bandwidth | Monthly Cost (est.) | Best For |
|---|---|---|---|---|
| Site-to-Site VPN | Low | Up to 2.5 Gbps | $200–$400 | Dev/staging |
| ExpressRoute + DX | High | 1–100 Gbps | $2,000–$10,000+ | Production |
| SD-WAN Overlay | Medium | Depends on circuits | $500–$3,000 (+ licensing) | Multi-region |
Cost drivers on AWS: TGW attachment hours ($0.05/hr each), data processing ($0.02/GB), and VPN connection hours ($0.05/hr). On Azure: VPN Gateway SKU, ExpressRoute circuit tier, and egress data transfer charges.
Routing Gotchas That Will Bite You
BGP AS path prepending: When you have active/active VPN tunnels, Azure will load-balance across both unless you use AS path prepending to signal preference. Configure this in your Azure Local Network Gateway BGP settings.
Overlapping CIDRs: Map out your address space before you start. 10.0.0.0/8 is not big enough when AWS, Azure, on-prem, and SaaS platforms all want a slice. Use a proper IPAM tool — NetBox works well for this.
Transitive routing: AWS Transit Gateway enables transitive routing between attachments by default. Azure requires explicit route tables. A packet from AWS VPC A trying to reach Azure VNet B via a hub VNet requires a route on both the Azure hub VNet gateway subnet AND the spoke VNet.
MTU mismatches: IPSec encapsulation reduces effective MTU. Set TCP MSS clamping on both sides to avoid silent packet drops for large payloads:
# AWS side — via TGW route table options or VPC DHCP options
# Azure side — via Network Security Group or custom route
# Test end-to-end MTU:
ping -M do -s 1400 <REMOTE_IP> # Linux
ping -f -l 1400 <REMOTE_IP> # Windows
Monitoring the Cross-Cloud Link
Don't assume your VPN is healthy because it's in "Connected" state. Set up active monitoring:
# AWS CloudWatch alarm for VPN tunnel down
aws cloudwatch put-metric-alarm \
--alarm-name "vpn-tunnel-down" \
--metric-name TunnelState \
--namespace AWS/VPN \
--statistic Minimum \
--period 60 \
--threshold 1 \
--comparison-operator LessThanThreshold \
--dimensions Name=VpnId,Value=vpn-XXXXXXXX \
--evaluation-periods 2 \
--alarm-actions arn:aws:sns:us-east-1:123456789:ops-alerts
On Azure, use Connection Monitor in Network Watcher to run synthetic probes across the VPN every 30 seconds. Set alert thresholds at 150ms RTT — if you exceed that consistently, your SD-WAN failover policy should kick in.
The Decision You Actually Need to Make
Start with Site-to-Site VPN. Get traffic flowing. Measure actual bandwidth utilization for 30 days. If you're consistently above 800 Mbps or your latency is causing application issues, that's when you commit to Express Route and Direct Connect costs.
Multi-cloud networking is not a one-time project. It's infrastructure that needs quarterly review as your workloads evolve. The teams that do it well treat routing tables with the same version control discipline they apply to Terraform code — every change is in Git, every change is peer-reviewed.
Was this article helpful?
Senior Cloud Architect
AWS, GCP, and Azure — I've built production workloads on all three. From landing zone design to multi-region failover, I architect cloud infrastructure that scales without surprises. Well-Architected Framework isn't a checklist, it's a mindset.
Related Articles
The Complete AWS Cost Optimization Playbook: Compute, Storage, Networking, and Reserved Capacity
A data-driven playbook for cutting AWS costs across compute, storage, networking, and reserved capacity with real numbers and actions.
AWS Data Transfer Costs Exploding: How To Find And Fix Unexpected Cross-Region Traffic Charges
You opened your AWS bill last month and did a double-take. Data transfer charges that were $200 last quarter are now sitting at $1,800. Nothing major chang...
AWS Lambda Cost Optimization: Memory Tuning, Provisioned Concurrency, and ARM
Cut your AWS Lambda costs by 40-70% with memory right-sizing, ARM/Graviton migration, and smart provisioned concurrency strategies.
Automated Cloud Cost Anomaly Detection and Alerting
Set up automated cloud cost anomaly detection with AWS Cost Anomaly Detection and custom Lambda monitors to catch runaway spend early.
Reserved Instances vs Savings Plans: Which to Buy When
A data-driven comparison of AWS Reserved Instances vs Savings Plans — with decision frameworks, break-even math, and real purchase recommendations.
AWS EC2 Right-Sizing: Stop Overpaying for Compute
Find and fix oversized EC2 instances with this practical right-sizing guide. Save 30-50% on AWS compute costs using CloudWatch metrics and tooling.
More in Cloud Cost
View all →GCP Committed Use Discounts Vs Sustained Use Discounts: Maximize Savings With Workload Analysis
As platform engineers, we're constantly balancing performance, reliability, and cost optimization. Google Cloud Platform's discount models—Committed Use Di...
Kubecost Setup for Kubernetes Cost Visibility and Showback
Deploy Kubecost for real-time Kubernetes cost monitoring with namespace-level showback, idle cost detection, and actionable Slack alerts.
Cloud Cost Tagging Strategy: Make Every Dollar Traceable
A complete tagging strategy for cloud cost allocation — including the Terraform enforcement, AWS policies, and org-wide rollout plan that actually works.
S3 Storage Class Optimization: Stop Paying Hot Prices for Cold Data
A practical guide to S3 storage class selection and lifecycle policies — with real dollar figures showing how to cut storage costs by 60-80%.