Dynamic subnet CIDR calculation with Terraform
terraform devops ip
Provisioning Azure Subnet dynamically with calculated CIDR address using Terraform
June 23, 2020
Networking is fun... In a recent project I got the chance to calculate subnet addresses and masks. The last time I did this was probably 15 years ago at school, so it was a good refresher. Nontheless, having tools which can help with CIDR (Classless Inter-Domain Routing) calculations is always good.
This post is about provisioning Azure Subnet dynamically with calculated CIDR address using Terraform.
tl;dr
Use cidrsubnet
function in Terraform to calculate subnet addresses within an IP address space.
cidrsubnet(iprange, newbits, netnum)
iprange
= CIDR of the virtual network- 172.10.1.0/23
newbits
= the difference between subnet mask and network mask- 27 - 23 = 4
netnum
= practically the subnet index (see the ipcalc output above)- 0 = 172.10.0.0/27
- 1 = 172.10.0.32/27
- 2 = 172.10.0.64/27
- 3 = 172.10.0.96/27
Context and goal
We used Terraform modules and for_each
logic to deploy mutliple Azure Web Apps and assign them into corresponding subnets within Azure VNET.
Given a variable IP address space the goal was to calculate subnets for (potentially) dynamic number of Web Apps (1..N) with the size of /27
and use the available address space efficiently. Each subnet was supposed to have the capacity to host at least 20 instances.
There are plenty of tools on the internet which can perform these calculations. There's also Linux ipcalc
:
$ ipcalc 172.10.1.0/23 /27
Address: 172.10.1.0 10101100.00001010.0000000 1.00000000
Netmask: 255.255.254.0 = 23 11111111.11111111.1111111 0.00000000
Wildcard: 0.0.1.255 00000000.00000000.0000000 1.11111111
=>
Network: 172.10.0.0/23 10101100.00001010.0000000 0.00000000
HostMin: 172.10.0.1 10101100.00001010.0000000 0.00000001
HostMax: 172.10.1.254 10101100.00001010.0000000 1.11111110
Broadcast: 172.10.1.255 10101100.00001010.0000000 1.11111111
Hosts/Net: 510 Class B
Subnets after transition from /23 to /27
Netmask: 255.255.255.224 = 27 11111111.11111111.11111111.111 00000
Wildcard: 0.0.0.31 00000000.00000000.00000000.000 11111
1.
Network: 172.10.0.0/27 10101100.00001010.00000000.000 00000
HostMin: 172.10.0.1 10101100.00001010.00000000.000 00001
HostMax: 172.10.0.30 10101100.00001010.00000000.000 11110
Broadcast: 172.10.0.31 10101100.00001010.00000000.000 11111
Hosts/Net: 30 Class B
2.
Network: 172.10.0.32/27 10101100.00001010.00000000.001 00000
HostMin: 172.10.0.33 10101100.00001010.00000000.001 00001
HostMax: 172.10.0.62 10101100.00001010.00000000.001 11110
Broadcast: 172.10.0.63 10101100.00001010.00000000.001 11111
Hosts/Net: 30 Class B
3.
Network: 172.10.0.64/27 10101100.00001010.00000000.010 00000
HostMin: 172.10.0.65 10101100.00001010.00000000.010 00001
HostMax: 172.10.0.94 10101100.00001010.00000000.010 11110
Broadcast: 172.10.0.95 10101100.00001010.00000000.010 11111
Hosts/Net: 30 Class B
4.
Network: 172.10.0.96/27 10101100.00001010.00000000.011 00000
HostMin: 172.10.0.97 10101100.00001010.00000000.011 00001
HostMax: 172.10.0.126 10101100.00001010.00000000.011 11110
Broadcast: 172.10.0.127 10101100.00001010.00000000.011 11111
Hosts/Net: 30 Class B
5.
Network: 172.10.0.128/27 10101100.00001010.00000000.100 00000
HostMin: 172.10.0.129 10101100.00001010.00000000.100 00001
HostMax: 172.10.0.158 10101100.00001010.00000000.100 11110
Broadcast: 172.10.0.159 10101100.00001010.00000000.100 11111
Hosts/Net: 30 Class B
...etc...
This is great and produces exactly the result I wanted, but is there a way to use just Terraform?
CIDR subnets in Terraform
Terraform provides handy function called cidrsubnet
which is able to calculate subnet address within the given IP network address space.
Since the function is quite cryptic, I was happy to find this awesome blogpost from Lisa Hagemann which dissects the individual parameters and explains how the calculation works.
In short: cidrsubnet(iprange, newbits, netnum)
where:
iprange
= CIDR of the virtual network- 172.10.1.0/23
newbits
= the difference between subnet mask and network mask- 27 - 23 = 4
netnum
= practically the subnet index (see the ipcalc output above)- 0 = 172.10.0.0/27
- 1 = 172.10.0.32/27
- 2 = 172.10.0.64/27
- 3 = 172.10.0.96/27
Implementation
Example parameters which define inputs for the cidrsubnet function:
location_short = "neu"
address_spaces = {
"neu" = "172.10.1.0/23"
"weu" = "172.10.3.0/23"
"uks" = "172.20.28.0/23"
}
subnet_list = {
"front" = 0
"api1" = 1
"api2" = 2
"other" = 3
}
And subnet definition itself:
resource "azurerm_subnet" "subnets" {
for_each = local.subnet_list
name = "${each.key}-subnet"
resource_group_name = azurerm_resource_group.main.name
virtual_network_name = azurerm_virtual_network.main.name
# Size of the subnets is /27 - exactly the size we need for AppService VNET-integration
address_prefixes = [cidrsubnet(var.address_spaces[var.location_short], 4, each.value)]
service_endpoints = ["Microsoft.Web"]
# Delegation to enable app services to join the subnet later
delegation {
name = "appservicedelegation"
service_delegation {
name = "Microsoft.Web/serverFarms"
actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
}
}
}
Feedback
Found something inaccurate or plain wrong? Was this content helpful to you? Let me know!
š§ codez@deedx.cz