IP Allocation Like a Boss: Winning Patterns for Azure Networking in Terraform

Elliott Leighton-Woodruff, Principle Architecture at Synextra
Article by:
Elliott Leighton-Woodruff
Principal Architect
IP allocation like a boss

If you know me, you’ll know I’m always searching for ways to cut effort and make our Terraform modules genuinely reusable.

The whole point of Infrastructure as Code is to keep things simple and let teams deliver fast without friction. The right patterns make the difference.

Here’s exactly how I use cidrsubnet, cidrsubnets and cidrhost together with actual Azure resources real world, no spreadsheets.

1. Use cidrsubnet for VNet allocation from a regional range

If you’re working within a big enterprise, chances are you’ll be handed a broad regional range by the network lead. Let’s say you get 10.32.0.0/11 for the whole of a region.

Now, each VNet needs a clean, isolated slice. Instead of allocating these ranges one at a time by hand, try this:

variable "region_cidr" { default = "10.32.0.0/11" }
variable "vnet_index" { default = 2 } # Choose third VNet for this example

locals {
vnet_cidr = cidrsubnet(var.region_cidr, 5, var.vnet_index) # /16 per VNet
}

resource "azurerm_virtual_network" "main" {
name = "vnet-${var.vnet_index}"
address_space = [local.vnet_cidr]
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
}

No copy-paste errors—just a variable and you’ve got unique, sequential VNet blocks ready for any region.

 

2. Use cidrsubnets for multiple subnets in your VNet

Your VNet’s no good without subnets for workloads, databases and management. Here’s how I divvy up the VNet range without calculators or trial-and-error:

locals {
    subnet_sizes = [4, 6, 8] # App /20, Data /22, Mgmt /24
    subnet_list  = cidrsubnets(local.vnet_cidr, local.subnet_sizes...)
}

resource "azurerm_subnet" "app" {
    name                 = "app"
    resource_group_name  = azurerm_resource_group.main.name
    virtual_network_name = azurerm_virtual_network.main.name
    address_prefixes     = [local.subnet_list[0]]
}

resource "azurerm_subnet" "data" {
    name                 = "data"
    resource_group_name  = azurerm_resource_group.main.name
    virtual_network_name = azurerm_virtual_network.main.name
    address_prefixes     = [local.subnet_list[1]]
}

resource "azurerm_subnet" "mgmt" {
    name                 = "management"
    resource_group_name  = azurerm_resource_group.main.name
    virtual_network_name = azurerm_virtual_network.main.name
    address_prefixes     = [local.subnet_list[2]]
} 

 

Now subnet sizes and assignments are variable-driven. Change your needs and the code adapts instantly. Need to expand? Add another tier, tack on another seed bit. The maths is all handled and there’s no risk of overlap with neighbouring subnets.

3. Use cidrhost for static IPs (e.g. Network Adapter, Gateway)

You want consistent addresses, say for a jump box, firewall, or gateway? Grab a fixed address in any subnet without counting:

locals {
    app_gateway_ip = cidrhost(local.subnet_list[0], 4) # The 4th IP in Application subnet
}

resource "azurerm_network_interface" "app_gw" {
    name                = "appgw-nic"
    location            = azurerm_resource_group.main.location
    resource_group_name = azurerm_resource_group.main.name

    ip_configuration {
        name                          = "internal"
        subnet_id                     = azurerm_subnet.app.id
        private_ip_address_allocation = "Static"
        private_ip_address            = local.app_gateway_ip
    }
}

This makes static address management dead simple and nobody’s risking duplicate assignments or manual missteps.

4. Bringing it all together dynamic, extensible, foolproof

Combine the three into a pattern your whole team can use, scale, and hand off:

variable "region_cidr"  { default = "10.32.0.0/11" }
variable "vnet_index"   { default = 2 }
variable "subnet_sizes" { default = [4, 6, 8] }

locals {
    vnet_cidr       = cidrsubnet(var.region_cidr, 5, var.vnet_index)
    subnet_list     = cidrsubnets(local.vnet_cidr, var.subnet_sizes...)
    subnet_names    = ["app", "data", "mgmt"]

    # Create maps with custom keys
    subnets         = { for i, subnet in local.subnet_list : local.subnet_names[i] => subnet }
    gateway_ips     = { for i, subnet in local.subnet_list : local.subnet_names[i] => cidrhost(subnet, 1) }
}

resource "azurerm_virtual_network" "main" {
    name                = "vnet-${var.vnet_index}"
    address_space       = [local.vnet_cidr]
    resource_group_name = azurerm_resource_group.main.name
    location            = azurerm_resource_group.main.location
}

resource "azurerm_subnet" "all" {
    for_each            = local.subnets
    name                = "subnet-${each.key}"
    resource_group_name = azurerm_resource_group.main.name
    virtual_network_name= azurerm_virtual_network.main.name
    address_prefixes    = [each.value]
}

resource "azurerm_network_interface" "gateways" {
    for_each            = local.subnets
    name                = "gw-nic-${each.key}"
    location            = azurerm_resource_group.main.location
    resource_group_name = azurerm_resource_group.main.name

    ip_configuration {
        name                          = "internal"
        subnet_id                     = azurerm_subnet.all[each.key].id
        private_ip_address_allocation = "Static"
        private_ip_address            = local.gateway_ips[each.key]
    }
}

Real-world advice

  • If environments grow, just bump the count or tier variables. No rewiring or error-prone copy-pasting required.
  • For environments that cover multiple regions, wrap the logic in a module and feed regional CIDRs and indices from a single, simple root.
  • cidrhost is gold for static assignments or gateways. Nobody needs to second-guess which IP’s in use.

Wrapping up

Getting this set up right means you sidestep so many headaches later: no overlapping networks, no scrambling for available IPs, no brittle hand-edited configs. Your Terraform modules become drop-in building blocks anyone on the team will grasp at a glance. If you’re still hardcoding subnets or tracking them in a spreadsheet, you’re giving yourself needless work.

Subscribe to our newsletter

Stay ahead of the curve with the latest trends, tips, and insights in cloud computing

thank you for contacting us image
Thanks, we'll be in touch.
Go back
By sending this message you agree to our terms and conditions.