This repository contains a reusable Terraform / OpenTofu module and progressive examples for deploying Azure routing resources β starting from a simple User Defined Route (UDR) and evolving toward hub-and-spoke transit routing, forced tunneling, dual-NIC NVA patterns, and cross-spoke Private Endpoint access.
It is part of the FoggyKitchen.com training ecosystem and is designed as a clean, composable routing layer that builds on top of an existing Azure networking foundation (VNets, subnets, peering, and optional router appliances).
This module is also part of the Azure Advanced Networking with Terraform/OpenTofu β Building Real-World Azure Network Architectures with Reusable Modules (2026 Edition) course. In the course, it is used to show how Azure route tables turn basic hub-and-spoke peering into working transit, egress, and private connectivity designs. It provides the routing layer that connects the theory of Azure network flows with practical, reusable Terraform/OpenTofu implementations.
Support expectations are documented in SUPPORT.md.
This module is used as a building block by the higher-level FoggyKitchen Landing Zone Orchestrator, where it is composed into Azure, OCI, and multicloud landing zone patterns.
The goal of this module is to provide a clear, educational, and architecture-aware reference implementation for Azure routing:
- Focused on Route Tables, Routes, and Subnet Associations
- Explicit inputs and outputs, with no hidden dependencies
- Designed to integrate cleanly with:
- Azure VNets and subnets
- VNet peering
- Router VMs and NVAs
- Hub-and-spoke network topologies
- Centralized outbound egress designs
- Cross-spoke access to Private Endpoints
This is not a full landing zone or opinionated platform module.
It is a learning-first, building-block module.
Depending on configuration and example used, the module can create:
- Azure Route Tables
- One or more routes per route table
- Subnet-to-route-table associations
- Multiple route tables from a single map-based input
- Routing policies for:
- Basic UDR scenarios
- Transit routing through a router VM or NVA
- Hub-and-spoke network designs
- Forced tunneling through a centralized egress point
- Dual-NIC NVA next-hop patterns
- Cross-spoke Private Endpoint access through a hub router
The module intentionally does not create:
- Virtual Networks or subnets
- Network Security Groups
- Virtual Machines or router appliances
- VNet peering
- Azure Firewall
- NAT Gateway
Each of those concerns belongs in its own dedicated module.
terraform-az-fk-routing/
βββ examples/
β βββ 01_basic_udr/
β βββ 02_hub_spoke_with_routing/
β βββ 03_forced_tunneling/
β βββ 04_nva_dual_nic/
β βββ 05_hub_spoke_private_endpoint_access/
β βββ README.md
βββ main.tf
βββ inputs.tf
βββ outputs.tf
βββ versions.tf
βββ LICENSE
βββ README.mdmodule "routing" {
source = "git::https://github.com/foggykitchen/terraform-az-fk-routing.git?ref=v0.1.0"
resource_group_name = "fk-rg"
route_tables = {
rt-basic = {
location = "westeurope"
routes = [
{
name = "default-to-internet"
address_prefix = "0.0.0.0/0"
next_hop_type = "Internet"
}
]
subnet_ids = [
module.vnet.subnet_ids["app"]
]
}
}
}The module can also be used in a hub-and-spoke design where traffic is forwarded through a router VM or NVA in the hub:
module "routing" {
source = "git::https://github.com/foggykitchen/terraform-az-fk-routing.git"
resource_group_name = "fk-rg"
route_tables = {
rt-spoke1 = {
location = "westeurope"
routes = [
{
name = "to-spoke2-via-hub"
address_prefix = "10.2.0.0/16"
next_hop_type = "VirtualAppliance"
next_hop_ip = "10.0.1.4"
}
]
subnet_ids = [
module.vnet_spoke1.subnet_ids["fk-subnet-spoke1"]
]
}
}
}For a working transit-routing design in Azure, next_hop_ip must point to a real forwarding device such as a router VM, NVA, or Azure Firewall.
The module can also be used to force outbound Internet traffic from spoke subnets through a centralized router in the hub:
module "routing" {
source = "git::https://github.com/foggykitchen/terraform-az-fk-routing.git"
resource_group_name = "fk-rg"
route_tables = {
rt-spoke1 = {
location = "westeurope"
routes = [
{
name = "default-to-internet-via-hub"
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_ip = "10.0.1.4"
}
]
subnet_ids = [
module.vnet_spoke1.subnet_ids["fk-subnet-spoke1"]
]
}
}
}For a working forced tunneling design in Azure, the central router must provide IP forwarding and NAT for outbound traffic.
The module can also be used in a more appliance-like topology, where a router VM exposes:
- an inside NIC used as the
VirtualAppliancenext hop for spoke route tables - an outside NIC used for outbound egress and NAT
This keeps Azure route tables simple while allowing a clearer inside/outside separation in the router VM design.
| Variable | Description |
|---|---|
resource_group_name |
Name of the Azure resource group |
route_tables |
Map of route tables with routes and subnet associations |
tags |
Optional tags applied to route tables |
route_tables = {
rt-name = {
location = "westeurope"
routes = [
{
name = "route-name"
address_prefix = "10.2.0.0/16"
next_hop_type = "VirtualAppliance"
next_hop_ip = "10.0.1.4"
}
]
subnet_ids = [
"/subscriptions/.../subnets/example"
]
}
}| Output | Description |
|---|---|
route_table_ids |
Map of route table IDs |
route_table_names |
Map of route table names |
- Routing is a dedicated concern, separate from networking, security, and compute
- Route tables should be explicit and easy to reason about
- Transit routing is only useful when paired with a real forwarding device
- Forced tunneling is only useful when paired with a real egress device that can perform NAT
- Dual-NIC NVA topologies can better model real appliances than single-NIC router labs
- Private DNS does not replace routing; cross-spoke Private Endpoint access still needs a real transit path
- Outputs are first-class citizens for composition with other modules
- terraform-az-fk-vnet
- terraform-az-fk-vnet-peering
- terraform-az-fk-nsg
- terraform-az-fk-compute
- terraform-az-fk-loadbalancer
- terraform-az-fk-bastion
- terraform-az-fk-natgw
- terraform-az-fk-storage
- terraform-az-fk-private-endpoint
- terraform-az-fk-private-dns
- terraform-az-fk-aks
- Azure Hub-Spoke Routing with Terraform
- Azure Forced Tunneling with Terraform
- Azure Network Segmentation with Terraform
Licensed under the Universal Permissive License (UPL), Version 1.0.
See LICENSE for details.
Β© 2026 FoggyKitchen.com - Cloud. Code. Clarity.