Classic hub and spoke on Azure works well until it does not. You build one hub VNet with a firewall, peer your spokes to it, and life is good. Then the business opens a second region, then a third, then acquires a company with two hundred branch offices that all need VPN connectivity. Suddenly you are managing a mesh of VNet peerings, user defined routes on every subnet, and BGP configurations that only one person on the team understands. Azure Virtual WAN exists to make that entire layer a managed service.
What Virtual WAN Changes
Virtual WAN gives you Microsoft managed hubs. Each hub is a Microsoft operated virtual network in a region that contains the gateways (site to site VPN, point to site VPN, ExpressRoute) and a fully managed router. You connect your spoke VNets and branches to the hub, and hub to hub connectivity across regions rides the Microsoft global backbone automatically with full transit routing. No more manually maintained UDRs for spoke to spoke traffic, and no more gateway transit peering settings.
My rule of thumb: one region and a handful of spokes, classic hub and spoke is cheaper and simpler. Two or more regions, branch VPN at any scale, or SD-WAN integration, and Virtual WAN pays for itself in removed operational load.
Deploying with Terraform
resource "azurerm_virtual_wan" "this" {
name = "vwan-global"
resource_group_name = azurerm_resource_group.net.name
location = "westeurope"
type = "Standard"
}
resource "azurerm_virtual_hub" "weu" {
name = "vhub-weu"
resource_group_name = azurerm_resource_group.net.name
location = "westeurope"
virtual_wan_id = azurerm_virtual_wan.this.id
address_prefix = "10.100.0.0/23"
}
resource "azurerm_virtual_hub" "eus" {
name = "vhub-eus"
resource_group_name = azurerm_resource_group.net.name
location = "eastus"
virtual_wan_id = azurerm_virtual_wan.this.id
address_prefix = "10.101.0.0/23"
}
resource "azurerm_virtual_hub_connection" "spoke_app" {
name = "conn-spoke-app"
virtual_hub_id = azurerm_virtual_hub.weu.id
remote_virtual_network_id = azurerm_virtual_network.spoke_app.id
internet_security_enabled = true
}
Give every hub at least a /23. The hub hosts gateways and the firewall, and undersizing the prefix is a rebuild, not a resize.
Secured Hubs and Routing Intent
A secured virtual hub embeds Azure Firewall inside the hub itself. Combined with routing intent, you declare that all private traffic and all internet traffic must pass through the firewall, and the managed router programs every route table for you. This is the feature that removes the biggest source of outages I used to see in classic designs: someone forgetting a UDR on a new subnet and traffic bypassing inspection.
resource "azurerm_firewall" "hub_weu" {
name = "afw-vhub-weu"
resource_group_name = azurerm_resource_group.net.name
location = "westeurope"
sku_name = "AZFW_Hub"
sku_tier = "Premium"
firewall_policy_id = azurerm_firewall_policy.global.id
virtual_hub {
virtual_hub_id = azurerm_virtual_hub.weu.id
public_ip_count = 1
}
}
resource "azurerm_virtual_hub_routing_intent" "weu" {
name = "ri-weu"
virtual_hub_id = azurerm_virtual_hub.weu.id
routing_policy {
name = "PrivateTraffic"
destinations = ["PrivateTraffic"]
next_hop = azurerm_firewall.hub_weu.id
}
routing_policy {
name = "InternetTraffic"
destinations = ["Internet"]
next_hop = azurerm_firewall.hub_weu.id
}
}
Design Notes from Production
A few things that bite people. First, DNS: the hub firewall should be your DNS proxy so FQDN rules resolve consistently, and spokes point their VNet DNS at the firewall IP. Second, plan address space with zero overlap across all regions and on premises before the first hub goes live, because fixing overlap later means renumbering. Third, hub capacity is measured in routing infrastructure units, and the default handles about 3 Gbps aggregate, so raise it for serious east west traffic. Finally, keep one firewall policy with regional child policies rather than independent policies per hub, or rule drift between regions is guaranteed.
Cheers
Osama
Leave a comment