Theoretical Foundation: Configure user-defined routes
1. Initial Intuitionβ
Imagine a city's street system. Normally, GPS determines the shortest path between two points, and cars automatically follow that route. But in certain situations, the city hall places "mandatory detour" signs: even if GPS indicates a shorter path, cars must pass through a specific control point before reaching the destination.
In Azure, default routing works like automatic GPS: Azure knows all addresses in the VNet, peered VNets, and the internet, and automatically routes traffic through the most efficient path. User-Defined Routes (UDRs) are the "mandatory detour" signs: you explicitly instruct that certain types of traffic must pass through a specific point before reaching the destination, even if that's not the shortest path.
The most common use case is forcing all traffic from a subnet to pass through an Azure Firewall or a Network Virtual Appliance (NVA) before going to the internet or other networks. Without UDRs, traffic would go directly to the destination, without inspection.
2. Contextβ
In previous modules, we saw that:
- VNets organize resources in isolated address spaces
- Peerings connect VNets
- NSGs control whether traffic is allowed or denied
UDRs operate in a different dimension: they don't control whether traffic is allowed, but where it passes through. It's path control, not permission control.
UDRs are the piece that completes the Azure network security architecture. NSGs alone control inbound/outbound, but can't redirect traffic for inspection. UDRs do this redirection.
3. Concept Constructionβ
3.1 How Routing Works in Azure: System Routesβ
Before understanding UDRs, you need to understand what happens without them. Azure automatically creates system routes for each subnet:
| Destination prefix | Next hop | What it does |
|---|---|---|
10.0.0.0/16 (local VNet) | Virtual network | Traffic within the VNet stays in the VNet |
0.0.0.0/0 | Internet | All non-local traffic goes to the internet |
10.1.0.0/16 (peered VNet) | VNet peering | Traffic to peered VNet goes via peering |
192.168.0.0/16 (on-premises via VPN) | Virtual network gateway | Traffic to on-premises goes via gateway |
100.64.0.0/10 | None | Discarded (reserved range) |
These routes are automatic and invisible, but you can see them by checking the effective routes of any NIC. They form each subnet's implicit routing table.
3.2 What is a User-Defined Routeβ
A UDR is an entry in a Route Table that you explicitly create. It has three components:
| Component | Description | Example |
|---|---|---|
| Address prefix | The destination: where this traffic is going | 0.0.0.0/0 (all internet) |
| Next hop type | The type of next hop | VirtualAppliance |
| Next hop address | The IP of the next hop (when applicable) | 10.0.0.4 (Firewall IP) |
3.3 Next Hop Typesβ
| Next hop type | Description | When to use |
|---|---|---|
| Virtual appliance | Redirects to a specific private IP (NVA, Firewall) | Force traffic inspection |
| Virtual network gateway | Forwards to the VNet's VPN/ExpressRoute gateway | Routing to on-premises via gateway |
| Virtual network | Traffic stays within the VNet (local routing) | Override a more specific route |
| Internet | Forwards to the internet via public IP | Direct internet egress |
| None | Drops traffic (blackhole) | Block specific routes via routing |
The None type is an alternative way to block traffic via routing, without NSG. The difference from NSG: NSG returns an explicit "access denied"; None simply drops packets without notification.
3.4 Route Tables: The UDR Containerβ
A Route Table is the Azure resource that contains a collection of UDRs. The Route Table itself does nothing; it needs to be associated with one or more subnets. A subnet can have at most one Route Table associated. A Route Table can be associated with multiple subnets.
3.5 Route Precedenceβ
When there are multiple routes that apply to the same destination, Azure follows a precedence order:
Example of precedence by specificity: if there's a system route for 0.0.0.0/0 β Internet, and you create a UDR for 10.1.0.0/16 β VirtualAppliance, traffic to 10.1.0.0/16 will use the UDR (more specific), and everything else will use the default system route. The UDR doesn't need to replace all routes; it only overlaps the ones you define.
4. Structural Viewβ
Classic Scenario: Force Tunneling via Azure Firewallβ
The Route Table rt-spoke-to-hub contains:
0.0.0.0/0β VirtualAppliance β10.0.1.4(Firewall)192.168.0.0/16β VirtualAppliance β10.0.1.4(Firewall)
This ensures that all traffic leaving the Spoke VNet subnets passes through Azure Firewall, whether to internet or on-premises.
Effective Routes: The Complete Viewβ
To understand a NIC's effective routing, Azure combines all sources:
5. Practical Operationβ
The IP Forwarding Problemβ
When a UDR redirects traffic to a VM or NVA (for example, an Azure Firewall or third-party appliance), there's a critical technical requirement: the destination VM's NIC must have IP Forwarding enabled.
By default, Azure drops packets that arrive at a NIC but whose destination IP is not that NIC's IP. This is protection against spoofing. But when a VM is acting as a router or firewall, it needs to accept and forward packets destined to other IPs.
IP Forwarding needs to be enabled in two places:
- On the VM's NIC in Azure (NIC resource configuration in portal/CLI)
- In the VM's operating system (enabling IP forwarding in Linux kernel or Windows registry)
For Azure Firewall, this is managed automatically by Microsoft. For third-party NVAs (Palo Alto, FortiGate, etc.), you need to enable it manually.
# Enable IP Forwarding on NIC via CLI
az network nic update \
--name nic-nva-appliance \
--resource-group rg-networking \
--ip-forwarding true
Blackhole Route for Routing-based Blockingβ
A less obvious use of UDRs is creating routing blackholes: dropping traffic to specific prefixes without using NSG. This is useful for blocking routes advertised via BGP that you don't want to be used:
# Create blackhole route to block egress to a specific range
az network route-table route create \
--route-table-name rt-application \
--resource-group rg-networking \
--name block-sensitive-range \
--address-prefix 203.0.113.0/24 \
--next-hop-type None
Route Behavior with BGP Propagationβ
Route Tables have a setting called "Disable BGP route propagation". By default, routes learned via BGP from a VPN Gateway or ExpressRoute are automatically propagated to VNet subnets. If you disable this propagation in a subnet's Route Table, BGP routes don't reach that subnet.
This behavior is important in scenarios where you want to explicitly control all on-premises routing from a subnet via UDRs, without interference from automatic BGP routes.
6. Implementation Methodsβ
6.1 Azure Portalβ
When to use: one-off creation, route verification, visual diagnostics.
Path to create Route Table: Create a resource > Networking > Route table
Path to add routes: Route table > Routes > Add
Path to associate to a subnet: Route table > Subnets > Associate or from the subnet side: Virtual Network > Subnet > Route table
Path to verify effective routes: Network interface > Help > Effective routes
6.2 Azure CLIβ
Create Route Table:
az network route-table create \
--name rt-spoke-to-hub \
--resource-group rg-networking \
--location brazilsouth \
--disable-bgp-route-propagation false
Add route to force internet traffic via Firewall:
az network route-table route create \
--route-table-name rt-spoke-to-hub \
--resource-group rg-networking \
--name route-internet-via-firewall \
--address-prefix 0.0.0.0/0 \
--next-hop-type VirtualAppliance \
--next-hop-ip-address 10.0.1.4
Add route for on-premises via Firewall:
az network route-table route create \
--route-table-name rt-spoke-to-hub \
--resource-group rg-networking \
--name route-onprem-via-firewall \
--address-prefix 192.168.0.0/16 \
--next-hop-type VirtualAppliance \
--next-hop-ip-address 10.0.1.4
Associate Route Table to a subnet:
az network vnet subnet update \
--name subnet-application \
--vnet-name vnet-spoke \
--resource-group rg-networking \
--route-table rt-spoke-to-hub
Verify effective routes of a NIC:
az network nic show-effective-route-table \
--name nic-vm-app \
--resource-group rg-producao \
--output table
Disassociate Route Table from a subnet:
az network vnet subnet update \
--name subnet-application \
--vnet-name vnet-spoke \
--resource-group rg-networking \
--remove routeTable
List all routes from a Route Table:
az network route-table route list \
--route-table-name rt-spoke-to-hub \
--resource-group rg-networking \
--output table
6.3 PowerShellβ
# Create Route Table
$routeTable = New-AzRouteTable `
-Name "rt-spoke-to-hub" `
-ResourceGroupName "rg-networking" `
-Location "brazilsouth"
# Add internet route via Firewall
Add-AzRouteConfig `
-RouteTable $routeTable `
-Name "route-internet-via-firewall" `
-AddressPrefix "0.0.0.0/0" `
-NextHopType "VirtualAppliance" `
-NextHopIpAddress "10.0.1.4" |
Set-AzRouteTable
# Add on-premises route via Firewall
Add-AzRouteConfig `
-RouteTable $routeTable `
-Name "route-onprem-via-firewall" `
-AddressPrefix "192.168.0.0/16" `
-NextHopType "VirtualAppliance" `
-NextHopIpAddress "10.0.1.4" |
Set-AzRouteTable
# Associate to a subnet
$vnet = Get-AzVirtualNetwork -Name "vnet-spoke" -ResourceGroupName "rg-networking"
$subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name "subnet-application"
$subnet.RouteTable = $routeTable
Set-AzVirtualNetwork -VirtualNetwork $vnet
# Verify effective routes
Get-AzEffectiveRouteTable `
-NetworkInterfaceName "nic-vm-app" `
-ResourceGroupName "rg-producao" |
Format-Table
6.4 Bicepβ
resource routeTable 'Microsoft.Network/routeTables@2023-05-01' = {
name: 'rt-spoke-to-hub'
location: location
properties: {
disableBgpRoutePropagation: false
routes: [
{
name: 'route-internet-via-firewall'
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: '10.0.1.4'
}
}
{
name: 'route-onprem-via-firewall'
properties: {
addressPrefix: '192.168.0.0/16'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: '10.0.1.4'
}
}
]
}
}
// Associate to subnet
resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-05-01' = {
name: 'subnet-application'
parent: vnet
properties: {
addressPrefix: '10.1.1.0/24'
routeTable: {
id: routeTable.id
}
}
}
7. Control and Securityβ
The Asymmetric Routing Problemβ
Asymmetric routing is the most critical error when configuring UDRs with NVAs/Firewalls. It occurs when inbound traffic and return traffic travel different paths, and the inspection appliance sees only half of the flow:
The solution is ensuring both inbound and return traffic pass through the same appliance. This is done by applying consistent UDRs in both traffic directions.
UDR on GatewaySubnet: Special Careβ
Associating a Route Table to the GatewaySubnet is possible, but extremely risky. An incorrect UDR can break all VPN/ExpressRoute connectivity. Microsoft doesn't recommend putting UDRs on GatewaySubnet unless you have absolute certainty of the impact.
The only safe UDR on GatewaySubnet would be to ensure traffic from specific addresses doesn't exit via internet, but even this should be done with great care and tested in non-production environments first.
8. Decision Makingβ
When to use UDR vs. NSG vs. Azure Firewall?β
| Objective | Tool | Reason |
|---|---|---|
| Block traffic from a specific port | NSG | NSG operates at L4, simpler and more efficient |
| Force traffic inspection before release | UDR + Azure Firewall/NVA | UDR redirects, Firewall inspects and decides |
| Block traffic to a prefix via routing | UDR with Next hop: None | Alternative to NSG, more efficient for large blocks |
| Ensure internet egress via centralized Firewall | UDR with 0.0.0.0/0 β VirtualAppliance | Standard force tunneling in hub-spoke |
| Route on-premises traffic via specific gateway | UDR with prefix β VirtualNetworkGateway | Precise control over which traffic uses the gateway |
Which subnets to associate Route Tables with?β
| Subnet | Associate RT? | Typical configuration |
|---|---|---|
| Workload subnets (app, backend, db) | Yes | 0.0.0.0/0 β Firewall; on-premises prefixes β Firewall |
| AzureFirewallSubnet | Yes (specific routes only) | Route to on-premises via VPN Gateway |
| GatewaySubnet | Rarely, with extreme care | Only if absolutely necessary |
| AzureBastionSubnet | Not recommended | Bastion has specific routing requirements |
| Delegated managed service subnets | Check service documentation | Some services don't support RTs |
9. Best Practicesβ
Always test UDRs in non-production environment first: an incorrect UDR can create routing loops or blackholes that completely interrupt subnet connectivity. A development environment where you can validate the behavior before applying to production is essential.
Verify effective routes before and after applying: use the Network Watcher effective routes tool to confirm exactly which routes are active on a NIC after applying a Route Table. This confirms that the UDR is behaving as expected.
Name routes descriptively: route-internet-via-azfw, route-onprem-192-168-via-azfw, blackhole-rfc1918-unused are names that immediately communicate the route's purpose. Routes with names like route1, route2 make maintenance and diagnosis much more difficult.
Keep Route Tables separate by function: one RT for spoke subnets that force traffic via hub, one RT for the AzureFirewallSubnet, one RT for management subnets. Sharing the same RT between subnets with very different functions makes changes more risky.
Use a shared Route Table for multiple subnets when routes are identical: if all subnets in a spoke need the same routes, a single Route Table associated to all is easier to maintain than multiple copies.
10. Common Errorsβ
Creating the UDR but forgetting to associate it to the subnet
The Route Table exists, routes are configured, but traffic still follows system routes. The cause is that the Route Table was not associated to any subnet. Checking the NIC's effective routes immediately shows that UDRs don't appear, revealing the problem.
Asymmetric routing due to partial UDR
The UDR is applied only to the outbound subnet (e.g.: subnet-backend β Firewall), but not to the inbound subnet or return path. The Firewall sees only half of the connections, cannot maintain state, and drops return packets. All connections become intermittent or fail. The solution is to ensure that traffic from both directions of a connection passes through the same appliance.
Routing loop between NVA and Route Table
A UDR directs 0.0.0.0/0 to the NVA IP (10.0.1.4). The NVA is in the same subnet that has the Route Table. Traffic from the NVA towards the next hop loops: goes to itself. The solution is to place the NVA in a separate subnet without the redirection Route Table, or ensure that the NVA subnet's Route Table has the route 0.0.0.0/0 β Internet (not β VirtualAppliance).
Not enabling IP Forwarding on the NVA NIC
The UDR redirects traffic to the NVA (10.0.1.4), but traffic arrives at the NIC and is dropped because the packet's destination IP is not 10.0.1.4 (it's the final destination, like 8.8.8.8). Azure drops the packet for security. Enabling IP Forwarding on the Azure NIC AND in the NVA's operating system solves the problem.
Applying UDR to GatewaySubnet without testing
An administrator applies a Route Table to the GatewaySubnet to control on-premises traffic. The route is slightly wrong, and all VPN connectivity drops immediately. Since access to Azure is now only possible via VPN (which dropped), the administrator cannot access the portal to fix it. Always maintain an alternative access path (for example, an emergency access account with portal access via internet) before modifying critical routing.
11. Operation and Maintenanceβ
Diagnosis with Network Watcherβ
Two Network Watcher tools are essential for routing diagnosis:
Effective Routes: shows all active routes on a NIC, including system, UDRs, BGP and peering. Shows the source of each route and which is being used for each destination.
az network watcher show-next-hop \
--resource-group rg-producao \
--vm vm-app-01 \
--source-ip 10.1.1.4 \
--dest-ip 8.8.8.8
This command directly answers: "if a VM with IP 10.1.1.4 tries to reach 8.8.8.8, what is the next hop and which route is being used?"
Connection Troubleshoot: tests end-to-end connectivity and identifies where in the path communication is being blocked or redirected.
Audit of Route Tables Without Associationβ
Route Tables without associated subnets are orphaned resources that have no utility but can cause confusion:
az network route-table list \
--query "[?subnets==null || length(subnets)==`0`].{Nome:name, RG:resourceGroup}" \
--output table
Important Limitsβ
| Item | Limit |
|---|---|
| Route Tables per subscription per region | 200 |
| Routes per Route Table | 400 |
| Route Tables per subnet | 1 (only one RT per subnet) |
| Subnets per Route Table | No documented limit |
The limit of 400 routes per Route Table is rarely reached in typical scenarios, but in environments with many on-premises networks advertised via BGP that need to be overridden by UDRs, it can be relevant.
12. Integration and Automationβ
UDRs in Complete Hub-Spoke Architectureβ
The complete UDR configuration in a typical hub-spoke architecture involves multiple Route Tables:
The Firewall subnet RT (rt-azfw-subnet) is necessary so that the Firewall knows to route traffic destined for on-premises via the VPN Gateway, instead of trying to access directly. Without it, the Firewall may drop or incorrectly route on-premises traffic.
Deploy Automation via Terraformβ
resource "azurerm_route_table" "spoke_rt" {
name = "rt-spoke-to-hub"
location = var.location
resource_group_name = var.resource_group
disable_bgp_route_propagation = false
route {
name = "route-internet-via-firewall"
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = var.firewall_private_ip
}
route {
name = "route-onprem-via-firewall"
address_prefix = "192.168.0.0/16"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = var.firewall_private_ip
}
}
resource "azurerm_subnet_route_table_association" "app" {
subnet_id = azurerm_subnet.application.id
route_table_id = azurerm_route_table.spoke_rt.id
}
Azure Policy for Routing Complianceβ
# Create policy to audit subnets without Route Table
az policy assignment create \
--name "audit-subnet-route-table" \
--policy "/providers/Microsoft.Authorization/policyDefinitions/fc5e4038-4584-4632-8c85-c0448d374b2c" \
--scope "/subscriptions/<sub-id>"
This built-in policy audits subnets that don't have an associated Route Table, helping to ensure that centralized routing via Firewall is in place on all workload subnets.
13. Final Summaryβ
Essential points:
- UDRs control the path of traffic, not whether it's allowed. NSGs control permission. Both are complementary.
- A Route Table is the UDR container. It needs to be associated to subnets to take effect. A subnet can have only one Route Table.
- Each route has: address prefix (destination), next hop type and next hop address (when applicable).
- UDRs have priority over system routes when they have the same prefix. More specific prefixes always win, regardless of source.
Critical differences:
- Next hop VirtualAppliance vs. VirtualNetworkGateway: the first redirects to a private IP of NVA/Firewall; the second uses the VNet's VPN/ExpressRoute gateway.
- Next hop None vs. NSG Deny: None drops silently (no response to sender); NSG Deny can return an explicit response. Both block traffic but through different mechanisms.
- Route Table vs. Effective Routes: the Route Table is what you configure; the Effective Routes are the final result after combination with system, BGP and peering routes.
- Disable BGP route propagation disabled (default): BGP routes reach the subnet. Enabled: only explicit UDRs are used, without automatic BGP routes.
What needs to be remembered:
- For third-party NVAs, enabling IP Forwarding on the Azure NIC AND in the operating system is mandatory. For Azure Firewall, it's automatic.
- Asymmetric routing is the most critical error: ensure that inbound and outbound traffic passes through the same inspection appliance.
- Never apply Route Tables to the GatewaySubnet without rigorous testing; it can bring down all VPN connectivity.
- Always verify effective routes of a NIC after applying a Route Table to confirm expected behavior.
- The command
az network watcher show-next-hopdirectly answers which route will be used for a specific IP pair, essential for diagnosis. - Route Table associated to a subnet that has resources but without configured routes still does nothing; system routes remain in effect.