Multicloud - Azure + AWS connectivity using VPN and BGP
After my first article to setup BGP inside VPN between Azure region, let's see if we can do the same thing between two cloud providers : Azure and AWS. We will do this using Terraform and see if we can do multicloud with this IaC tool.
AWS
First we will have to setup VPC (Virtual Private Cloud) and subnet part :
resource "aws_vpc" "vpc" {
cidr_block = "10.2.0.0/23"
tags = {
Name = "aws-vpc-001"
}
}
resource "aws_subnet" "subnet" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.2.0.0/24"
tags = {
Name = "AWSWorkloadSubnet"
}
}
Once we have the VPC, we can create a VPN gateway attached to it. In addition, we will enable the route propagation from the VPN gateway to the VPC route table. This step is mandatory to propagate the BGP route from the gateway to a VM connected to this subnet.
resource "aws_vpn_gateway" "vpn_gateway" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "aws-gateway-001"
}
}
resource "aws_vpn_gateway_route_propagation" "gateway_route_propagation" {
vpn_gateway_id = aws_vpn_gateway.vpn_gateway.id
route_table_id = aws_vpc.vpc.main_route_table_id
}
Next step will be to declare Azure VPN gateway in AWS and then setup VPN connection:
resource "aws_customer_gateway" "customer_gateway_primary" {
bgp_asn = 65515
ip_address = azurerm_public_ip.public_ip_gateway_primary.ip_address
type = "ipsec.1"
tags = {
Name = "azure-gateway-001-primary"
}
}
resource "aws_vpn_connection" "vpn_connection_primary" {
vpn_gateway_id = aws_vpn_gateway.vpn_gateway.id
customer_gateway_id = aws_customer_gateway.customer_gateway_primary.id
type = "ipsec.1"
tunnel1_inside_cidr = "169.254.21.0/30"
tunnel2_inside_cidr = "169.254.21.4/30"
tags = {
Name = "azure-vpn-001-primary"
}
}
To enable BGP between AWS and Azure we will to specify the tunnel_inside_cidr. Azure support only two possible ranges for this : 169.254.21.x or 169.254.22.x.
In AWS, every VPN connections will setup two tunnels for redundancy.
Azure
Exactly as AWS, the first step we will to setup network part with the VNet:
resource "azurerm_resource_group" "resource_group" {
name = "Azure-DC-NorthEurope"
location = "North Europe"
}
resource "azurerm_virtual_network" "virtual_network" {
name = "azure-vnet-001"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
address_space = ["10.1.0.0/23"]
}
resource "azurerm_subnet" "subet_gateway" {
name = "GatewaySubnet"
resource_group_name = azurerm_resource_group.resource_group.name
virtual_network_name = azurerm_virtual_network.virtual_network.name
address_prefixes = ["10.1.0.0/26"]
}
resource "azurerm_subnet" "subnet_workload" {
name = "AzureWorkloadSubnet"
resource_group_name = azurerm_resource_group.resource_group.name
virtual_network_name = azurerm_virtual_network.virtual_network.name
address_prefixes = ["10.1.1.0/24"]
}
We want to setup an active/active virtual network gateway, so it will required two public IP:
resource "azurerm_public_ip" "public_ip_gateway_primary" {
name = "azure-pip-001"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
sku = "Standard"
allocation_method = "Static"
}
resource "azurerm_public_ip" "public_ip_gateway_secondary" {
name = "azure-pip-002"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
sku = "Standard"
allocation_method = "Static"
}
resource "azurerm_virtual_network_gateway" "virtual_network_gateway" {
name = "azure-gateway-001"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
type = "Vpn"
vpn_type = "RouteBased"
active_active = true
enable_bgp = true
sku = "VpnGw1"
bgp_settings {
asn = 65515
peering_addresses {
ip_configuration_name = "gw-ip1"
apipa_addresses = [cidrhost("169.254.21.0/30", 2), cidrhost("169.254.21.4/30", 2)]
}
peering_addresses {
ip_configuration_name = "gw-ip2"
apipa_addresses = [cidrhost("169.254.22.0/30", 2), cidrhost("169.254.22.4/30", 2)]
}
}
ip_configuration {
name = "gw-ip1"
public_ip_address_id = azurerm_public_ip.public_ip_gateway_primary.id
private_ip_address_allocation = "Dynamic"
subnet_id = azurerm_subnet.subet_gateway.id
}
ip_configuration {
name = "gw-ip2"
public_ip_address_id = azurerm_public_ip.public_ip_gateway_secondary.id
private_ip_address_allocation = "Dynamic"
subnet_id = azurerm_subnet.subet_gateway.id
}
}
Terraform provides some built-in functions and some on them are dedicated to network : cidrhost /cidrnetmask / cidrsubnet / cidrsubnets
Next step will be to declare AWS VPN gateway in Azure and then setup VPN connections:
resource "azurerm_local_network_gateway" "local_network_gateway_primary_tunnel1" {
depends_on = [
azurerm_virtual_network_gateway.virtual_network_gateway
]
name = "aws-gateway-vpn-001-tunnel1"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
gateway_address = aws_vpn_connection.vpn_connection_primary.tunnel1_address
bgp_settings {
asn = 64512
bgp_peering_address = cidrhost("169.254.21.0/30", 1)
}
}
resource "azurerm_virtual_network_gateway_connection" "aws-primary-tunnel1" {
name = "aws-primary-tunnel1"
location = azurerm_resource_group.resource_group.location
resource_group_name = azurerm_resource_group.resource_group.name
type = "IPsec"
enable_bgp = true
virtual_network_gateway_id = azurerm_virtual_network_gateway.virtual_network_gateway.id
local_network_gateway_id = azurerm_local_network_gateway.local_network_gateway_primary_tunnel1.id
shared_key = aws_vpn_connection.vpn_connection_primary.tunnel1_preshared_key
}
VPN preshared keys will be generated by AWS on connection creation. The key can be easily reused on Azure side using attribute : tunnel1_preshared_key.
If you want to avoid issue while modify existing Azure Virtual Network Gateway, you will have to use at least version 3.4.0 of the azurerm provider.
Deployment + tests
All the code is available on this Github repository. To deploy the infrastructure, you will have to setup aws-cli + azure-cli to manage authentication.
Once everything is configured, terraform init + terraform apply and after arround 30 minutes, everything must be up and running:
Apply complete! Resources: 23 added, 0 changed, 0 destroyed.
After few seconds, all tunnels must be connected :
Thanks to BGP, I learn on Azure side the route to reach AWS VPC:
Same on AWS, VPN and BGP are up :
To do some tests, I provisioned one VM on each side. After opening flows, communication between VM was working as expected. To test the redundancy, I deleted 3 connections on Azure side (2 connections on the primary gateway + 1 connection on the secondary) and run a ping between the VM at the same time.
No packet lost and only few pings reach ~75ms. I just redo terraform apply to recreate the connections and everything was up again.
Conclusion
It was a simple example and my first multicloud script. Terraform can easily managed one infrastructure hosted on several providers. It can be really interesting because both side of the VPN need dynamic information from the other side (IP / PSK) and managing everything in a single script is nice and open many other possibilities.
Don't hesitate to contact me if you have any question or remark and don't forget the terraform destroy at the end of your tests.