On your VM the network was one interface and one address (Ch 1, Ch 8). In Azure you design the network yourself: which private addresses exist, how they are split up, who may talk to whom. Get the address plan wrong and it cannot be fixed later without moving everything. This chapter is Azure networking, then AKS on top of it.
What you need to know already: 8.3 (CIDR: /24 = 256 addresses), 8.6 (carving an address space, overlap), 8.9 (Azure subnets and AKS sizing), 8.11 (routing tables), 22.1 (subscriptions, resource groups, regions, az), 22.4 (--query), 12.20 (Terraform for_each).
Virtual networks and subnets
A virtual network (VNet) is a private address range (for example 10.20.0.0/16) that you own inside Azure, in one region and one subscription. Only things you put in it get those addresses, and by default nothing outside can reach them. A subnet is a slice of the VNet's range (10.20.4.0/24) - the same idea as in Ch 8.
Everything with a private IP sits in a subnet: a VM's NIC (network interface card - the virtual network port of a VM), AKS nodes (and, with one of the AKS network modes you will meet in 23.22, the pods too), private endpoints (23.10), internal load balancers and App Gateway instances (23.16).
az network vnet show -g rg-oncall-lab -n vnet-sysop --query ...: show one VNet; the JMESPath (22.4) picks its address space and each subnet's name and range.
$ az network vnet show -g rg-oncall-lab -n vnet-sysop --query "{space:addressSpace.addressPrefixes, subnets:subnets[].{name:name, prefix:addressPrefix}}"
{
"space": [
"10.20.0.0/16"
],
"subnets": [
{
"name": "snet-aks",
"prefix": "10.20.0.0/24"
},
{
"name": "snet-appgw",
"prefix": "10.20.1.0/24"
},
{
"name": "snet-pe",
"prefix": "10.20.2.0/28"
},
{
"name": "snet-app",
"prefix": "10.20.4.0/24"
}
]
}
Five addresses per subnet belong to Azure
Chapter 8 did the arithmetic (8.9); here it is on a real subnet. In every subnet:
x.x.x.0 network address
x.x.x.1 default gateway (the Azure router)
x.x.x.2 \ Azure DNS
x.x.x.3 /
x.x.x.255 broadcast (the last address, whatever the prefix)
So usable = 2^(32 - prefix) - 5, and the first address you ever get is .4:
| prefix | addresses | usable |
|---|---|---|
| /29 | 8 | 3 (.4 .5 .6) - the smallest subnet Azure allows |
| /28 | 16 | 11 |
| /27 | 32 | 27 |
| /26 | 64 | 59 |
| /24 | 256 | 251 |
| /22 | 1024 | 1019 |
A private endpoint (23.10) takes one address. Here four are created in a loop into a /29 subnet called snet-pe2; each prints the IP it got:
# the /29 mission: four endpoints into snet-pe2
for i in 1 2 3 4; do az network private-endpoint create ... --subnet snet-pe2 --query "customDnsConfigs[0].ipAddresses[0]" -o tsv; done
10.20.3.4
10.20.3.5
10.20.3.6
ERROR: (SubnetIsFull) Subnet snet-pe2 with address prefix 10.20.3.0/29 does not have enough
capacity for 1 IP addresses.
A /29 holds three things. Not eight, not six.
Creating subnets, and the two errors you will meet
az network vnet subnet create -g <rg> --vnet-name <vnet> -n <name> --address-prefixes <cidr>: add a subnet to a VNet; --address-prefixes its range.
az network vnet subnet create -g rg-oncall-lab --vnet-name vnet-sysop -n snet-data --address-prefixes 10.20.5.0/26
ERROR: (NetcfgSubnetRangesOverlap) Subnet 'snet-x' is not valid in virtual network 'vnet-sysop'
because its IP address range overlaps with that of an existing subnet 'snet-pe'.
ERROR: (NetcfgSubnetRangeOutsideVnet) Subnet 'snet-x' is not valid because its IP address
range is outside the IP address range of virtual network 'vnet-sysop'.
A subnet must sit inside the VNet's address space and not overlap a sibling. And you cannot resize a subnet that has anything in it - a full AKS subnet means a new subnet and a migration, not an edit. Size generously up front.
Planning an address space
- Pick VNet ranges that do not overlap anything you might ever peer with (connect to, below) or route to: other VNets, on-prem (the company's own data centres and offices), partner networks, the AKS service CIDR (the range ClusterIPs come from, Ch 16 - 10.0.0.0/16 by default!) and pod CIDR (the range pods get their IPs from - 10.244.0.0/16 for overlay, 23.22). Overlaps are the one networking mistake you cannot fix later.
- One subnet per purpose and per security boundary: AKS nodes, App Gateway (a dedicated subnet, nothing else in it - /24 recommended for v2), private endpoints, VMs, databases.
- Special subnets have required names:
AzureFirewallSubnet(/26 or larger) for Azure Firewall (Azure's managed firewall, 23.7),GatewaySubnetfor a VPN gateway or ExpressRoute (the two ways to connect on-prem to Azure: an encrypted tunnel over the internet, or a private line),AzureBastionSubnet(/26) for Bastion (Azure's managed SSH/RDP jump host · you connect to VMs through it instead of giving them public IPs).
Peering is not transitive
Peering connects two VNets so their addresses can reach each other directly, as if they were one network. Transitive would mean "if A talks to B and B talks to C, then A talks to C". Peering is not:
hub <-peer-> spoke-a
hub <-peer-> spoke-b
spoke-a X spoke-b (no route, unless traffic goes through a firewall/NVA in the hub)
Each peering is a direct link between two VNets. Spoke-to-spoke traffic needs a router in the hub (Azure Firewall, or an NVA - network virtual appliance, a VM running firewall or router software) plus route tables in the spokes pointing at it - lessons 23.7 and 23.14. (Hub and spoke: a central VNet everyone connects to, and the VNets hanging off it, like a bicycle wheel.)
The Terraform shape
resource "azurerm_subnet" "pe" {
for_each = { pe = "10.20.3.0/29", data = "10.20.5.0/26" }
name = "snet-${each.key}"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = [each.value]
}
The same subnets in Terraform (Ch 12): for_each makes one azurerm_subnet per map entry. for_each, not count - chapter 12's incident (12.20) was exactly this resource.
Reading a VNet end to end
$ az network vnet subnet show -g rg-oncall-lab --vnet-name vnet-sysop -n snet-app --query "{prefix:addressPrefix, nsg:networkSecurityGroup.id, rt:routeTable.id, pe:privateEndpointNetworkPolicies, delegations:delegations}"
{
"delegations": [],
"nsg": null,
"pe": "Disabled",
"prefix": "10.20.4.0/24",
"rt": null
}
Everything a subnet can carry is on that object: the NSG (23.3), the route table (23.7), a delegation (a subnet handed to one service - App Service VNet integration, Azure Container Instances, PostgreSQL Flexible Server - that nothing else may use) and the private endpoint network policies flag (whether NSGs and route tables apply to private endpoints in it; enable it if you want to filter private endpoint traffic with NSGs).
Worked sizing examples
What each line says: the thing, how many addresses it needs, the prefix that fits. (Surge nodes are the extra nodes AKS adds temporarily during an upgrade, 23.28; 60 nodes x 31 is 31 addresses per node when every pod gets a subnet address, 23.22.)
private endpoints for ~20 services 20 + growth -> /27 (27 usable)
App Gateway v2, autoscale to 125 Microsoft recommends a /24
Azure Firewall /26 minimum, name AzureFirewallSubnet
Bastion /26 minimum, name AzureBastionSubnet
AKS overlay, 60 nodes + 10 surge 70 + 5 -> /25 (123 usable)
AKS node subnet, 60 nodes x 31 1,860 + surge -> /21
Round up a prefix, never down, and leave unallocated space in the VNet: the next subnet you will need is the one you did not plan for.
Common mistakes
$ az network vnet subnet create -g rg-oncall-lab --vnet-name vnet-sysop -n snet-db --address-prefixes 10.20.4.128/25
ERROR: (NetcfgSubnetRangesOverlap) Subnet 'snet-db' is not valid in virtual network 'vnet-sysop'
because its IP address range overlaps with that of an existing subnet 'snet-app'.
10.20.4.128/25 is the upper half of snet-app's 10.20.4.0/24. Write the ranges down in a table (or let Terraform's cidrsubnet() do it) before creating anything. A prefix that is not aligned to its size (10.20.4.64/25) is not a valid CIDR at all - the base of a /25 must be .0 or .128.
What you can now do
- Read a VNet and its subnets with
az network vnet show/subnet show. - Size a subnet the Azure way:
2^(32-n) - 5usable, first address.4. - Plan address ranges that never overlap anything you may connect to later.