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.
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:
| Route | Prefix | Next hop | azure_firewall (default) | spoke_nat |
|---|---|---|---|---|
default-to-firewall | 0.0.0.0/0 | the firewall's private IP | yes | no |
organization-to-firewall | the organization range | the firewall's private IP | yes | no |
front-door-return | AzureFrontDoor.Backend | Internet | yes | no |
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
| Term | Meaning |
|---|---|
| System route | A route Azure creates for every subnet without configuration. |
| User-defined route | A route in a route table that overrides the system routes. |
| Next hop | Where a matching packet goes: an appliance IP, the internet, a gateway or nowhere. |
| BGP route propagation | Whether a VPN gateway's learned routes are added to the subnet; off here. |
hub_egress | The network.hcl switch between the hub firewall and a NAT gateway per spoke. |
Where to read more
- Azure Enterprise Baseline overview for the network design.
- Virtual network peering for the paths the routes steer.
- Hub-and-spoke networking for central egress and inspection.