The bank's rule: no workload talks to the internet directly; everything goes out through a firewall that allows only named destinations and logs every connection. An NSG can allow or deny, but it cannot send traffic somewhere. Routes do. This lesson is Azure's routing table, the same idea as ip route on your VM (8.11).
What you need to know already: 8.11 (routing tables, longest prefix match), 23.1 (VNets, subnets, peering, NVA), 23.3 (NSGs).
System routes
Every subnet has system routes Azure creates for you (destination range, then next hop - where the packet is sent next):
10.20.0.0/16 VnetLocal (the VNet's own space)
<peered ranges> VNetPeering
0.0.0.0/0 Internet
10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 None (dropped unless something more specific exists)
None means drop. The private ranges (10/8, 172.16/12, 192.168/16, and 100.64/10, the "shared" range) are dropped unless your VNet, a peering or a route of yours covers them.
A route table is an Azure resource holding your own routes - each one a UDR (user-defined route). Attached to a subnet, it adds or overrides routes for traffic leaving that subnet.
Choosing a route
- Longest prefix match first:
10.20.4.0/24beats10.20.0.0/16beats0.0.0.0/0. - On a tie in prefix length: user-defined > BGP > system. (BGP is the protocol routers use to tell each other which ranges they can reach; here, routes learnt from a VPN or ExpressRoute gateway, 23.1.)
So a UDR 0.0.0.0/0 -> VirtualAppliance 10.20.254.4 sends internet-bound traffic to a firewall, while traffic inside the VNet still matches the more specific 10.20.0.0/16 VnetLocal system route and goes direct.
Next hop types
VirtualAppliance an IP: Azure Firewall's private IP or an NVA (needs --next-hop-ip-address)
VirtualNetworkGateway the VPN / ExpressRoute gateway
VnetLocal stay inside the VNet
Internet out via Azure's internet edge
None drop (a black hole)
Three commands: az network route-table create makes an empty route table; az network route-table route create adds one route (--address-prefix the destination range, --next-hop-type, and --next-hop-ip-address for an appliance); az network vnet subnet update ... --route-table attaches it to a subnet.
az network route-table create -g rg-oncall-lab -n rt-egress
az network route-table route create -g rg-oncall-lab --route-table-name rt-egress -n default \
--address-prefix 0.0.0.0/0 --next-hop-type VirtualAppliance --next-hop-ip-address 10.20.254.4
az network vnet subnet update -g rg-oncall-lab --vnet-name vnet-sysop -n snet-app --route-table rt-egress
# rt-egress = the route table of the next mission
az network route-table route list -g rg-oncall-lab --route-table-name rt-egress -o table
AddressPrefix HasBgpOverride Name NextHopIpAddress NextHopType ProvisioningState ResourceGroup
------------- -------------- ------- ---------------- ---------------- ----------------- -------------
0.0.0.0/0 False default 10.20.254.4 VirtualAppliance Succeeded rg-oncall-lab
Why you would do this
Egress control (control over traffic leaving your network). Banks do not let workloads reach the internet directly. All outbound traffic goes through Azure Firewall (or a third-party NVA) that allows only named destinations (*.docker.io no, mcr.microsoft.com yes, your package mirror yes) and logs everything.
For an AKS cluster (23.20) this is the create option --outbound-type userDefinedRouting: the cluster does not create an outbound public IP on its load balancer, and relies on your route table to send egress to the firewall. The firewall must then allow the endpoints AKS needs (the control plane FQDN, mcr.microsoft.com, *.data.mcr.microsoft.com, management.azure.com, login.microsoftonline.com, package repos for node images, NTP - the time service...). An FQDN (fully qualified domain name, Ch 8) is a complete host name like mcr.microsoft.com; firewall FQDN rules allow by name instead of by IP. Miss one and nodes fail to join or image pulls hang - one of the classic AKS-behind-a-firewall outages.
The mistakes
- Asymmetric routing. Traffic goes out through the firewall but replies come back directly (or the other way round). Stateful firewalls drop the half-flow they never saw. Typical cause: a UDR on one side only, or a public IP on the VM that bypasses the firewall for inbound.
- Routing the firewall subnet through itself. Never attach the 0/0 route table to
AzureFirewallSubnet. - Breaking the platform. Some subnets (App Gateway v2, managed instance) require direct internet for their management traffic; a 0/0 to a firewall there breaks them.
- Forgetting BGP propagation.
--disable-bgp-route-propagation trueon a route table stops VPN-learnt routes arriving in that subnet - wanted for forced tunnelling (sending all traffic, even internet-bound, back through on-prem or the firewall), surprising otherwise.
Seeing what really applies
For a VM NIC, az network nic show-effective-route-table prints the merged result of system, BGP and user routes with their source - the routing equivalent of listing NSG rules with the defaults. The drill does the longest-prefix reasoning by hand.
System routes you can see
# an illustration (no ▶): a subnet whose route table sends 0.0.0.0/0 to a firewall (abridged)
$ az network nic show-effective-route-table -g rg-oncall-lab -n vm-jump-nic -o table
Source State Address Prefix Next Hop Type Next Hop IP
-------- ------- ---------------- ---------------- -------------
Default Active 10.20.0.0/16 VnetLocal
User Active 0.0.0.0/0 VirtualAppliance 10.20.254.4
Default Invalid 0.0.0.0/0 Internet
Default Active 157.59.0.0/16 None
Default Active 127.0.0.0/8 None
Run it on the lab's vm-jump-nic to see the real list for its subnet. Note the Invalid default route: your 0.0.0.0/0 replaced it. And the None routes: without a user default route, Azure drops packets to the private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10) that are not in your VNet or a peering - a packet to 10.99.0.1 matches the /8 None route, which is more specific than the /0 Internet route. Once a user 0.0.0.0/0 points at an appliance or a VPN gateway, Azure removes those four None routes, so 10.99.0.1 goes to the firewall (Microsoft Learn, "Azure virtual network traffic routing", routing example, Subnet1). On a tie of prefix length the user route wins.
Worked decisions
destination matching routes winner
10.20.4.9 10.20.0.0/16 VnetLocal, 0/0 appliance VnetLocal (/16)
8.8.8.8 0/0 appliance appliance
192.168.1.1 192.168.0.0/16 None, 192.168.0.0/16 gw gateway (user wins tie)
10.99.0.1 0/0 appliance (the /8 None is removed) appliance
10.99.0.1 no user 0/0: 10.0.0.0/8 None, 0/0 Internet None (/8 beats /0)
What you can now do
- Predict where a packet goes: longest prefix first, then user > BGP > system.
- Send a subnet's internet traffic through a firewall with a route table.
- Name the classic mistakes: asymmetric routing, the firewall subnet routed through itself, subnets that need direct internet.