Skip to main content

Route tables

A route table holds user-defined routes that override Azure's system routes for the subnets it is attached to. The Azure Enterprise Baseline gives every spoke and the runner network one table that sends their traffic to the hub firewall, so nothing leaves a spoke without passing the firewall.

What it does​

Azure routes every subnet with system routes: within the virtual network, to peered networks, and 0.0.0.0/0 to the internet. A route table attached to a subnet adds user-defined routes (UDRs); the most specific prefix wins, and a UDR beats a system route of the same length. Each route has a next hop: VirtualAppliance with an IP address (a firewall), Internet, VirtualNetworkGateway or None. A route's prefix can also be a service tag, such as AzureFrontDoor.Backend, which Azure expands to that service's current ranges. BGP route propagation decides whether routes a VPN gateway learns are added to the subnet.

How BuiltForProd uses it​

The routes unit (definition routes-spoke, module route-table) runs in every stage spoke and in the runner network of acme-core-auto. It creates <prefix>-default-rt, such as acme-eus2-prd-default-rt, attaches it to every snet-* subnet except snet-pls (a Private Link Service keeps the system routes), and turns BGP propagation off, so routes from the VPN gateway can never bypass the firewall. The contract publishes the table as vnet--route-table--default.

Azure/acme-azure-platform-baseline/units/routes-spoke/terragrunt.hcl (lines 70-85)
inputs = {
route_table_name = "${include.root.locals.name_prefix}-default-rt"
routes = merge(
local.net.hub_egress == "azure_firewall" ? {
default-to-firewall = { address_prefix = "0.0.0.0/0", next_hop_type = "VirtualAppliance", next_hop_in_ip_address = dependency.firewall.outputs.firewall_private_ip }
organization-to-firewall = { address_prefix = dependency.ipam.outputs.organization_cidr, next_hop_type = "VirtualAppliance", next_hop_in_ip_address = dependency.firewall.outputs.firewall_private_ip }
} : {},
# Only needed when 0.0.0.0/0 points at the firewall; in spoke_nat mode the system route already returns directly.
local.net.hub_egress == "azure_firewall" && local.front_door_return_route ? {
front-door-return = { address_prefix = "AzureFrontDoor.Backend", next_hop_type = "Internet" }
} : {},
)
# Every workload subnet; snet-pls keeps Azure's system routes (Private Link Service NAT).
subnet_ids = { for k, v in dependency.vnet.outputs.subnet_ids : k => v if startswith(k, "snet-") && k != "snet-pls" }
bgp_route_propagation_enabled = false
}

The routes depend on hub_egress in network.hcl; the organization range is organization_cidr of the address plan, 10.0.0.0/8:

RoutePrefixNext hopazure_firewall (default)spoke_nat
default-to-firewall0.0.0.0/0the firewall's private IPyesno
organization-to-firewallthe organization rangethe firewall's private IPyesno
front-door-returnAzureFrontDoor.BackendInternetyesno

With the firewall, traffic to the internet and to every other stage leaves through it (the spokes are not peered with each other), so the Azure Firewall isolation-domain rules see every flow between stages. The Front Door return route keeps the dev stage working: its ingress is a public origin locked to Front Door, and without the route its replies would leave through the firewall on an asymmetric path and break the connection. It is free and harmless where no such origin exists, behind front_door_return_route in the unit.

In the spoke_nat mode the table exists but holds no route: each spoke's NAT gateway carries the egress, and the system routes do the rest. The Web App Blueprint reads the mode from the contract secret network--firewall-private-ip (an address, or none) and sets the cluster's outbound type to match: userDefinedRouting with the firewall, userAssignedNATGateway with the NAT gateway. A cluster with Node Auto Provisioning cannot change its outbound type after creation, which is why the mode is chosen before the first blueprint deployment.

One more table lives in the hub. When the point-to-site VPN is on and the firewall exists, <prefix>-gateway-rt on GatewaySubnet sends each spoke prefix and the runner network to the firewall, so VPN traffic takes the same stateful path in both directions.

Terms you will see​

TermMeaning
System routeA route Azure creates for every subnet without configuration.
User-defined routeA route in a route table that overrides the system routes.
Next hopWhere a matching packet goes: an appliance IP, the internet, a gateway or nowhere.
BGP route propagationWhether a VPN gateway's learned routes are added to the subnet; off here.
hub_egressThe network.hcl switch between the hub firewall and a NAT gateway per spoke.

Where to read more​