Optimize Azure Traffic Flow with UDR

Traffic Flow Through Azure Firewall via UDR

The UDR (User Defined Route) is the mechanism that forces all spoke traffic through Azure Firewall — overriding Azure’s default system routes which would otherwise send traffic directly between peered VNets, bypassing inspection entirely.


Why UDRs Are Necessary

Azure VNet peering by default creates direct routing between peered VNets — packets travel peer-to-peer without touching any intermediate device. This means without UDRs, a VM in Spoke 1 talking to a VM in Spoke 2 completely bypasses Azure Firewall:

Default behaviour (NO UDR):
Spoke 1 VM (10.1.1.4) → Spoke 2 VM (10.2.1.4)
System route: 10.2.0.0/16 → VNet peering (direct)
Result: traffic bypasses firewall entirely ❌
With UDR applied to spoke subnet:
Spoke 1 VM (10.1.1.4) → Spoke 2 VM (10.2.1.4)
UDR overrides: 0.0.0.0/0 → 10.0.1.4 (Firewall private IP)
Result: traffic hits firewall → inspected → forwarded ✅

The UDR wins because custom routes always override system routes — Azure’s route selection priority is custom UDR first, then BGP routes, then system routes.


Route Table Structure

A route table is an Azure resource associated with one or more subnets. Every subnet that needs inspection gets the same core UDR:

Route Table: rt-spoke1-subnets
Associated to: Spoke 1 subnet A, Spoke 1 subnet B
Routes:
Name Prefix Next hop type Next hop IP
─────────────────────────────────────────────────────────────────
force-to-fw 0.0.0.0/0 Virtual Appliance 10.0.1.4

Deployed via ARM / Bicep:

resource routeTable 'Microsoft.Network/routeTables@2023-04-01' = {
name: 'rt-spoke1-subnets'
location: location
properties: {
disableBgpRoutePropagation: true // ← critical — explained below
routes: [
{
name: 'force-to-firewall'
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: '10.0.1.4' // Azure Firewall private IP
}
}
]
}
}
// Associate with spoke subnet
resource subnetAssociation 'Microsoft.Network/virtualNetworks/subnets@2023-04-01' = {
name: 'spoke1/snet-app'
properties: {
addressPrefix: '10.1.1.0/24'
routeTable: {
id: routeTable.id
}
}
}

The Three Traffic Paths

Path 1 — North-South Outbound (spoke VM → internet)

Step 1: Spoke 1 VM (10.1.1.4) sends packet to 8.8.8.8
Step 2: Subnet route table consulted
UDR match: 0.0.0.0/0 → next hop 10.0.1.4 (Firewall)
Step 3: Packet arrives at Azure Firewall private IP
Step 4: Firewall evaluates application rules
Rule: allow src=10.1.0.0/16 dest=*.google.com proto=HTTPS → ALLOW
Step 5: Firewall SNATs packet
Source IP changed: 10.1.1.4 → Firewall public IP (20.x.x.x)
Step 6: Packet exits to internet from Firewall public IP
Return traffic arrives at Firewall public IP
Step 7: Firewall translates back → forwards to 10.1.1.4

SNAT is automatic for internet-bound traffic — the spoke VM’s private IP is never exposed to the internet. Azure Firewall’s public IP is the only address the internet sees.


Path 2 — North-South Inbound (internet → spoke VM)

Step 1: External client sends to Firewall public IP 20.x.x.x:443
Step 2: Firewall DNAT rule fires:
dest 20.x.x.x:443 → translated to 10.1.4.5:443 (spoke VM)
Step 3: Firewall forwards to 10.1.4.5 via VNet peering path
Step 4: Packet arrives at spoke VM — no public IP needed on VM
Step 5: VM responds to Firewall private IP (it sees FW as source)
UDR on VM subnet ensures return goes back through Firewall
Step 6: Firewall forwards return to external client

Path 3 — East-West (spoke 1 VM → spoke 2 VM)

This is the most important path for security — lateral movement between spokes must be inspected:

Step 1: Spoke 1 VM (10.1.1.4) sends packet to Spoke 2 VM (10.2.1.8)
Step 2: Spoke 1 subnet route table consulted
UDR: 0.0.0.0/0 → 10.0.1.4 (matches — more specific than system route)
Step 3: Packet arrives at Azure Firewall
Step 4: Firewall evaluates network rules
Rule: allow src=10.1.0.0/16 dest=10.2.1.8 port=443 → ALLOW
(or deny if no rule matches)
Step 5: Firewall forwards to 10.2.1.8 via peering to Spoke 2
Step 6: Spoke 2 subnet route table:
UDR: 0.0.0.0/0 → 10.0.1.4
Return traffic: 10.2.1.8 → 10.1.1.4
UDR forces return through Firewall too
Step 7: Firewall forwards return packet to Spoke 1 VM

Both directions of every connection traverse the Firewall — request and response. This is essential for stateful inspection — if only one direction went through the Firewall, the session state table would be incomplete.


disableBgpRoutePropagation — Why It Matters

Every route table has a disableBgpRoutePropagation flag. On spoke subnets this must be set to true:

disableBgpRoutePropagation: false (default)
→ VPN Gateway pushes on-premises routes into spoke effective routes
→ Spoke VM sends on-premises traffic directly to Gateway
→ Bypasses Firewall for on-premises bound traffic ❌
disableBgpRoutePropagation: true (required for spoke subnets)
→ VPN Gateway routes suppressed on spoke subnets
→ Only UDR routes active: 0.0.0.0/0 → Firewall
→ All traffic including on-premises bound goes through Firewall ✅

Forgetting this setting is one of the most common misconfiguration errors in hub and spoke deployments — on-premises traffic silently bypasses the Firewall even though the UDR looks correct.


UDR on GatewaySubnet — On-Premises to Spoke

To inspect traffic arriving from on-premises destined for spoke VNets, a UDR must also be applied to the GatewaySubnet:

Route Table: rt-gateway-subnet
Associated to: GatewaySubnet
Routes:
Name Prefix Next hop type Next hop IP
────────────────────────────────────────────────────────────────
to-spoke1 10.1.0.0/16 VirtualAppliance 10.0.1.4
to-spoke2 10.2.0.0/16 VirtualAppliance 10.0.1.4
to-spoke3 10.3.0.0/16 VirtualAppliance 10.0.1.4

Note this uses specific spoke prefixes rather than 0.0.0.0/0 — applying a default route to GatewaySubnet breaks the gateway’s ability to communicate with Azure control plane endpoints.


Effective Route Inspection

You can verify UDRs are working correctly by checking a VM’s effective routes in the Azure portal or CLI:

az network nic show-effective-route-table \
--resource-group rg-spoke1 \
--name vm-prod-01-nic \
--output table
Source State Address Prefix Next Hop Type Next Hop IP
──────── ─────── ──────────────── ────────────────── ──────────
Default Active 10.1.0.0/16 VnetLocal
Default Invalid 0.0.0.0/0 Internet ← overridden
User Active 0.0.0.0/0 VirtualAppliance 10.0.1.4 ✅
Default Active 10.0.0.0/16 VNetPeering
Default Active 10.2.0.0/16 VNetPeering

The default 0.0.0.0/0 → Internet system route shows as Invalid — it has been overridden by the custom UDR pointing to the Firewall. This confirms all traffic is force-tunnelled correctly.


Common Misconfiguration Pitfalls

Forgetting disableBgpRoutePropagation — gateway-learned routes override UDRs for on-premises prefixes, silently bypassing Firewall for hybrid traffic.

Missing return path UDR — if Spoke 2 subnet has no UDR, return traffic goes directly back to Spoke 1 via peering, creating an asymmetric routing loop that breaks TCP sessions.

Applying UDR to AzureBastionSubnet — Bastion requires direct internet connectivity for its management plane. A UDR with 0.0.0.0/0 → Firewall on AzureBastionSubnet breaks Bastion entirely. Bastion subnet must have no UDR or a very specific one that excludes Bastion management ranges.

Applying 0.0.0.0/0 UDR to GatewaySubnet — breaks gateway health probes and control plane communication. Always use specific spoke prefixes on GatewaySubnet, never a default route.

Firewall private IP not static — Azure Firewall’s private IP should be configured as static during deployment. If it changes, every UDR next-hop entry becomes invalid and traffic black-holes.


Key Takeaway

The UDR is a deceptively simple mechanism — a single route entry 0.0.0.0/0 → Virtual Appliance → 10.0.1.4 — that transforms Azure’s default direct peering behaviour into a fully inspected, security-enforced network. Applied correctly to every spoke subnet with disableBgpRoutePropagation enabled, it ensures no traffic — outbound internet, inbound DNAT, or lateral east-west — can bypass Azure Firewall, giving you complete visibility and control over your entire hub and spoke estate.

Leave a comment