Cloud & infrastructure

Build an Azure hub-and-spoke network with Azure Firewall and UDRs

Peer spoke virtual networks to a hub, send their traffic through Azure Firewall with user-defined routes, inspect on-premises flows and configure forced tunnelling without breaking routing.

12 min read
On this page

To build a hub-and-spoke network with centralised egress, deploy Azure Firewall in a hub virtual network, peer each spoke to the hub with forwarded traffic allowed, and associate a route table with every spoke subnet whose default route (0.0.0.0/0) points at the firewall's private IP as a Virtual appliance next hop with gateway route propagation disabled. If the hub also has a VPN or ExpressRoute gateway, add a route table on GatewaySubnet that sends spoke prefixes to the firewall, so on-premises traffic is inspected in both directions. Forced tunnelling to an on-premises firewall additionally needs the Firewall Management NIC.

Who this is for and what you will have

This guide is for network and platform engineers building the connectivity layer of an Azure landing zone, or retrofitting a firewall into peered virtual networks that currently route directly. At the end you will have:

  • A hub with Azure Firewall, a Firewall Policy and, optionally, a VPN or ExpressRoute gateway.
  • Spokes peered to the hub with the correct gateway transit and forwarding settings.
  • Route tables that force spoke-to-internet, spoke-to-spoke and on-premises-to-spoke traffic through the firewall.
  • Logging, verification commands and fixes for the routing mistakes that cause most outages.

If you are planning a larger migration, the enterprise Azure cloud migration playbook covers where this network fits in the overall sequence.

Architecture and address plan

                         On-premises 192.168.0.0/16
                                   |
                          VPN or ExpressRoute
                                   |
+--------------------------- Hub 10.0.0.0/16 ----------------------------+
|  GatewaySubnet 10.0.1.0/26        route table: spokes -> firewall       |
|  AzureFirewallSubnet 10.0.0.0/26  Azure Firewall (private IP noted)     |
|  AzureFirewallManagementSubnet 10.0.2.0/26  (only for forced tunnel)    |
|  AzureBastionSubnet 10.0.3.0/26   (optional)                            |
+-------------------------------------------------------------------------+
          | peering                                    | peering
+------ Spoke 1 10.1.0.0/16 ------+      +------ Spoke 2 10.2.0.0/16 ------+
| snet-app  route 0/0 -> firewall |      | snet-app  route 0/0 -> firewall |
+---------------------------------+      +---------------------------------+
SubnetMinimum sizeNotes
AzureFirewallSubnet/26Required name; NSGs not supported; /26 covers all firewall scaling
AzureFirewallManagementSubnet/26Only when the Management NIC is enabled
GatewaySubnet/26 recommendedRequired name for VPN and ExpressRoute gateways
Spoke workload subnetsYour choiceEach gets a route table

Plan non-overlapping address space across every hub, spoke and on-premises range before you deploy. Use one hub per region and connect only spokes from the same region to it.

Prerequisites

  • Permissions to create peerings and route tables on the hub and spoke virtual networks, including in other subscriptions if spokes live there.
  • The firewall, its virtual network and its public IP in the same subscription; the firewall and virtual network in the same resource group.
  • The Az PowerShell module for the scripted steps.
  • A decision on the firewall tier. Standard covers most egress filtering; Premium adds TLS inspection, IDPS and URL filtering. Microsoft states throughput starts at 2.5 to 3 Gbps and scales automatically to 30 Gbps on Standard or 100 Gbps on Premium.
  • A decision on forced tunnelling. Standard and Premium firewalls need the Management NIC enabled at creation, or a stop and start to add it later.

Step 1: Create the hub virtual network

Create the hub with AzureFirewallSubnet (in the portal, set Subnet purpose to Azure Firewall), GatewaySubnet if you need cross-premises connectivity, and AzureFirewallManagementSubnet if you'll force-tunnel internet traffic. Leave all three without NSGs. The Management subnet gets a system route table; Microsoft warns against attaching your own, and if you do, it must contain a default route to the internet.

Step 2: Deploy Azure Firewall with a Firewall Policy

In the portal, create a Firewall with:

  • Firewall tier: Standard or Premium
  • Firewall management: Use a Firewall Policy to manage this firewall, with a new policy in the same region
  • Choose a virtual network: the hub
  • Public IP address: a new Standard public IP
  • Enable Firewall Management NIC: enable only if you need forced tunnelling or want no public IP on the data path, and supply the management subnet and a management public IP

When deployment finishes, record the firewall's private IP address; every route table points at it. Treat that address as something that can change: Microsoft notes that stopping and starting a firewall can move it to a different address in the subnet, which breaks route tables that reference the old one.

Turn on diagnostic settings now. Choose Resource specific as the destination table so network, application and DNS proxy logs land in dedicated tables such as AZFWNetworkRule and AZFWApplicationRule. Logs can take up to 30 minutes to start appearing.

Step 3: Peer each spoke to the hub

On the hub virtual network, select Peerings > Add and configure both directions in one step:

SettingHub-to-spoke linkSpoke-to-hub link
Allow access to the remote virtual networkSelectedSelected
Allow receiving forwarded trafficSelectedSelected
Allow gateway or route server to forward trafficSelected (hub has a gateway)Not selected
Use the remote virtual network's gateway or route serverNot selectedSelected (hub has a gateway)

The same in PowerShell, assuming $hub and $spoke1 hold the virtual network objects:

Add-AzVirtualNetworkPeering -Name 'peer-hub-to-spoke1' `
    -VirtualNetwork $hub -RemoteVirtualNetworkId $spoke1.Id `
    -AllowForwardedTraffic -AllowGatewayTransit
 
Add-AzVirtualNetworkPeering -Name 'peer-spoke1-to-hub' `
    -VirtualNetwork $spoke1 -RemoteVirtualNetworkId $hub.Id `
    -AllowForwardedTraffic -UseRemoteGateways

Leave out -AllowGatewayTransit and -UseRemoteGateways if the hub has no gateway. Peering is non-transitive: after this step each spoke can reach the hub, but not the other spokes and not the internet through the firewall. Routing does that.

Step 4: Route spoke traffic to the firewall

Create one route table per spoke (or a shared one per region) with Propagate gateway routes set to No, a default route to the firewall, and associate it with every workload subnet. Disabling propagation stops routes learned from on-premises over BGP from overriding your default route; if you keep propagation on, add specific routes to the firewall for every on-premises prefix.

$fwIp = '<firewall-private-ip>'
 
$rt = New-AzRouteTable -Name 'rt-spoke1' -ResourceGroupName 'rg-spoke1' `
    -Location 'westeurope' -DisableBgpRoutePropagation
 
$rt | Add-AzRouteConfig -Name 'default-to-firewall' -AddressPrefix '0.0.0.0/0' `
    -NextHopType 'VirtualAppliance' -NextHopIpAddress $fwIp | Set-AzRouteTable
 
$subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $spoke1 -Name 'snet-app'
$subnet.RouteTable = $rt
$spoke1 | Set-AzVirtualNetwork

How this interacts with Azure's own routes:

  • Azure picks the longest matching prefix. Among routes with the same prefix, a user-defined route beats a BGP route, which beats a system route.
  • Your 0.0.0.0/0 UDR replaces the default internet route, and Azure also removes the default None routes for 10.0.0.0/8, 192.168.0.0/16 and 100.64.0.0/10 from that subnet. Traffic to other spokes therefore matches no more specific route, so it goes to the firewall, which forwards it to the destination spoke over its own peering. That is how spokes talk to each other through the hub.
  • Traffic to the hub's own address space matches the more specific peering route and goes direct. If you also peer two spokes directly, traffic between them goes direct too. To force those flows through the firewall, add a UDR for the destination subnet prefix on both sides.
  • A UDR with a 0.0.0.0/0 next hop of Virtual appliance also sends traffic for Azure services' public IPs to the firewall, unless you've enabled a service endpoint on the subnet.

Microsoft's recommended way to segment subnets inside one virtual network is NSGs, not UDRs through the firewall, because per-subnet routes are easy to get wrong.

Step 5: Send on-premises traffic through the firewall

Without extra routes, traffic arriving from on-premises through the hub gateway goes straight to the spokes, because the gateway subnet has peering routes to them. Create a route table for GatewaySubnet with one route per spoke prefix, next hop Virtual appliance at the firewall IP, and leave Propagate gateway routes enabled:

$rtGw = New-AzRouteTable -Name 'rt-gatewaysubnet' -ResourceGroupName 'rg-hub' -Location 'westeurope'
 
$rtGw | Add-AzRouteConfig -Name 'to-spoke1' -AddressPrefix '10.1.0.0/16' `
    -NextHopType 'VirtualAppliance' -NextHopIpAddress $fwIp | Set-AzRouteTable
 
$gwSubnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $hub -Name 'GatewaySubnet'
$gwSubnet.RouteTable = $rtGw
$hub | Set-AzVirtualNetwork

Two rules for this subnet come straight from Microsoft's routing documentation: never disable route propagation on GatewaySubnet, or the gateway stops working, and never put a 0.0.0.0/0 route in a route table associated with the VPN gateway subnet. The spoke-side default route from Step 4 already sends return traffic to the firewall, so the flow is symmetric.

AzureFirewallSubnet itself needs no route table; it learns on-premises routes over BGP. The exception follows in the forced tunnelling section.

Step 6: Write the firewall rules

Rule collections are processed by priority, with DNAT collections before network collections and network before application collections. All rules are terminating, and anything not allowed is denied. A sensible starting policy:

  • A network rule collection for east-west and hybrid flows, for example on-premises 192.168.0.0/16 to spoke 10.1.0.0/16 on TCP 443 and 3389.
  • An application rule collection for internet egress by FQDN, for example operating system update sources for the spoke subnets.
  • No broad "any to any" rules. Allow only the destinations, ports and FQDNs each workload actually needs.

If you use FQDNs in network rules, enable DNS proxy on the firewall and set each virtual network's custom DNS server to the firewall's private IP, then restart the VMs to pick it up. The firewall listens on port 53 and forwards queries to Azure DNS or your custom servers, so it resolves names the same way the clients do. Microsoft's architecture guidance gives the same reason for application rules: clients and firewall must use the same DNS.

SNAT behaviour matters for the systems behind the firewall. Azure Firewall doesn't SNAT traffic to RFC 1918 or RFC 6598 destinations, so spokes and on-premises hosts see real client addresses. Application rule traffic is always SNATed; use network rules with FQDN destinations if you need the original source address in logs. Internet-bound traffic leaves from one of the firewall's public IPs, which partners must allow-list.

Forced tunnelling to an on-premises firewall

If policy requires internet traffic to exit through an on-premises security stack, Azure Firewall must have the Management NIC. Then add routes on AzureFirewallSubnet toward your on-premises device, or enable Propagate gateway routes so the default route arrives over BGP. With forced tunnelling, internet-bound traffic is SNATed to a firewall private IP, which hides the original source from the on-premises firewall; to stop that, set the private IP range to 0.0.0.0/0, after which the firewall never egresses directly to the internet. Microsoft states that DNAT isn't supported with forced tunnelling enabled because of asymmetric routing, although a firewall that only has the Management NIC enabled still supports DNAT.

Without the Management NIC the opposite applies: Azure Firewall needs direct internet connectivity, so if AzureFirewallSubnet learns a default route from on-premises over BGP, override it with a 0.0.0.0/0 UDR whose next hop type is Internet. To add the Management NIC to an existing Standard or Premium firewall, create the management subnet and public IP, then deallocate and reallocate the firewall during a maintenance window:

$azfw = Get-AzFirewall -Name 'afw-hub' -ResourceGroupName 'rg-hub'
$azfw.Deallocate()
Set-AzFirewall -AzureFirewall $azfw
 
$azfw = Get-AzFirewall -Name 'afw-hub' -ResourceGroupName 'rg-hub'
$vnet = Get-AzVirtualNetwork -Name 'vnet-hub' -ResourceGroupName 'rg-hub'
$pip = Get-AzPublicIpAddress -Name 'pip-afw' -ResourceGroupName 'rg-hub'
$mgmtPip = Get-AzPublicIpAddress -Name 'pip-afw-mgmt' -ResourceGroupName 'rg-hub'
$azfw.Allocate($vnet, $pip, $mgmtPip)
$azfw | Set-AzFirewall

Afterwards, confirm the firewall's private IP hasn't changed before you trust your route tables.

Verification

Check what a VM will actually do with a packet:

Get-AzEffectiveRouteTable -NetworkInterfaceName 'nic-app01' -ResourceGroupName 'rg-spoke1' |
    Format-Table
 
$vm = Get-AzVM -Name 'vm-app01' -ResourceGroupName 'rg-spoke1'
$nw = Get-AzNetworkWatcher -Name 'NetworkWatcher_westeurope' -ResourceGroupName 'NetworkWatcherRG'
Get-AzNetworkWatcherNextHop -NetworkWatcher $nw -TargetVirtualMachineId $vm.Id `
    -SourceIPAddress '10.1.0.4' -DestinationIPAddress '10.2.0.4'

The 0.0.0.0/0 user route should be Active, the default internet route Invalid, and the next hop for another spoke VirtualAppliance at the firewall IP. Then confirm the firewall saw the flow:

AZFWNetworkRule
| where TimeGenerated > ago(1h)
| where SourceIp startswith "10.1." and DestinationIp startswith "10.2."
| project TimeGenerated, SourceIp, DestinationIp, DestinationPort, Protocol, Action, RuleCollection, Rule, ActionReason
| order by TimeGenerated desc

Finally, test from on-premises to a spoke VM and check that the flow appears in the logs with the expected rule.

Troubleshooting

Spoke VMs lost internet access after the route table was applied. That's expected until an application or network rule allows the traffic. Look for denied flows in AZFWApplicationRule and AZFWNetworkRule; in the network rule table, an ActionReason of Default Action means no rule matched.

Spoke-to-spoke works but never appears in the firewall logs. The spokes are peered directly, or the route table isn't associated with the source subnet. Check effective routes on both NICs.

On-premises users reach spokes, but replies are dropped. Asymmetric routing: the gateway subnet has no UDR for the spoke prefixes, so requests bypass the firewall while replies hit it via the spoke's default route. Add the GatewaySubnet routes from Step 5.

The VPN gateway stopped working after routing changes. Someone disabled propagation on GatewaySubnet or added a 0.0.0.0/0 route there. Remove it.

The firewall can't reach the internet and metrics stop arriving. AzureFirewallSubnet learned a default route from on-premises. Add the 0.0.0.0/0 Internet UDR or move to forced tunnelling with the Management NIC. Microsoft lists published default routes as a cause of missing firewall metrics.

Everything broke after the firewall was stopped and started. The private IP changed. Update the next hop in every route table.

A TCP ping succeeds even though no rule allows it. With no matching allow rule, the firewall answers TCP pings itself and doesn't log them. Test with a real application connection.

Long-lived idle connections drop. The TCP idle timeout is four minutes and isn't user-configurable for east-west traffic. Use TCP keep-alives.

Closing checklist

  • Non-overlapping address plan; one hub per region.
  • AzureFirewallSubnet and, if needed, AzureFirewallManagementSubnet at /26, with no NSGs.
  • Firewall Policy, resource-specific diagnostic logs, and the firewall private IP documented.
  • Peerings allow forwarded traffic; gateway transit on the hub side and remote gateway on spokes.
  • Spoke route tables with 0.0.0.0/0 to the firewall and propagation disabled.
  • GatewaySubnet route table with spoke prefixes to the firewall, propagation enabled, no default route.
  • DNS proxy enabled if network rules use FQDNs.
  • Forced tunnelling only with the Management NIC; otherwise an Internet UDR if BGP injects a default route.

References

Questions people ask

Why doesn't traffic between two spokes go through Azure Firewall?

Peering is non-transitive, so spokes peered only to the hub can't reach each other until each spoke subnet has a route table that sends traffic to the firewall's private IP. If the two spokes are also peered directly, Azure routes between them directly unless both subnets have a UDR for the other spoke's prefix.

Can I put an NSG or route table on AzureFirewallSubnet?

Subnet-level NSGs aren't supported on AzureFirewallSubnet. A route table is normally unnecessary, but if the subnet learns a default route from on-premises over BGP you must override it with a 0.0.0.0/0 route whose next hop is Internet, or deploy the firewall with a Management NIC for forced tunnelling.

Does Azure Firewall SNAT traffic between spokes and on-premises?

Not for destinations in RFC 1918 or RFC 6598 private ranges, so spoke and on-premises hosts see the real source address. Traffic processed by application rules is always SNATed, and organisations that use public ranges internally must configure the private range setting.

Can Azure Firewall run without a public IP address?

Yes, if it is deployed with the Firewall Management NIC enabled. The management interface keeps its own public IP for platform operations only, and the data path can have no public IP, with internet traffic forced to another device or blocked.

Azure Virtual NetworkAzure FirewallVNet PeeringUser-Defined RoutesAzure VPN Gateway
  1. Azure Default Outbound Access Retirement: Migrate VMs to NAT Gateway

    New Azure virtual networks now get private subnets with no default outbound access. Find the VMs that depend on it, add a NAT gateway, make the subnet private and verify egress.

  2. Azure site-to-site VPN with a branch firewall: setup and troubleshooting

    Connect an office firewall to an Azure virtual network over IPsec: plan the gateway, local network gateway and crypto settings, add BGP, then fix tunnels that won't come up.

  3. Azure Bastion SKUs compared: choose Developer, Basic, Standard or Premium

    Compare the four Azure Bastion SKUs on features, capacity and requirements, then deploy the right one to RDP and SSH into VMs that have no public IP address.