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.

GitHub - vmisson/terraform-azure-aws-vpn: This project provide some Terraform files to deploy a full VPN connectivity with Azure and AWS and to configure BGP to manage routing dynamicaly.
This project provide some Terraform files to deploy a full VPN connectivity with Azure and AWS and to configure BGP to manage routing dynamicaly. - GitHub - vmisson/terraform-azure-aws-vpn: This pr...
Terraform code
Excalidraw
Excalidraw is a whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel to them.
For nice drawing