fbpx

Defining IP Address spaces using CIDR notation for network and subnets is a common task for network engineers, DevOps Engineers, and Site Reliability Engineers (SRE) when configuring infrastructure deployments using HashiCorp Terraform. CIDR notation is used uniquely to declare the IP Address space for an entire network, as well as to divide the full IP Address space into smaller segments when configuring the IP Address spaces for subnets. This article will explain what CIDR notation is and how to use the Terraform cidrsubnet function to help with the configuration of IP Address ranges using CIDR notation.

What is CIDR Notation?

CIDR notation is the format that used to define IP Address ranges when configuring networks and subnets. CIDR stands for “Classless Inter-Domain Routing” and provides a way of representing a range of IP addresses in a concise and standardized format. The ability to configure and define IP address ranges using CIDR notation is a required skill for network engineers, DevOps Engineers, Site Reliability Engineers (SREs), administrators, or any other professional needing to configure the deployment of networks and subnets.

CIDR notation is a flexible and efficient way to define the allocation of IP addresses by using a variable-length subnet mask. The notation is built using the {start ip address}/{cidr prefix} format. It starts with the first IP Address in the range, followed by a slash (/) character, then the CIDR prefix.

The following is an example of an IP Address range in CIDR notation format that represents the range of IPs starting with 10.0.0.0 and ending with 10.0.0.255 with a total of 256 IP addresses:

10.0.0.0/24

The CIDR prefix is written as a number between 0 (zero) and 32. The number represents the number of network bits in the subnet mask and is used to determine the range of IP addresses that belong to the range being defined.

Related: For more information about CIDR notation in general and a reference IP Address Range table for helping with defining CIDR notation, please refer to the “IPv4 Address CIDR Range Reference and Calculator” article written by Chris Pietschmann.

cidrsubnet Terraform Function Defined

You can always hard code the CIDR notation for an IP Address range in your HashiCorp Terraform code. However, when it comes to making the code more flexible and reusable with Terraform Modules, then using the cidrsubnet function can help make the code more dynamic.

The method signature for using the cidrsubnet Terraform function is as follows:

cidrsubnet(prefix, newbits, netnum)

The 3 arguments of theTerraform cidrsubnet function are defined as follows:

  • prefix
    The prefix must be given in CIDR notation and is the full IP Address range that will be divided up into smaller ranges.
  • newbits
    The newbits is the number of additional bits with which to extend the prefix. For example, if given a prefix ending in /16 and using a newbits value of 4, the resulting subnet address will have the length of /20.
  • netnum
    The netnum is a whole number that can be represented as a binary integer with no more than newbits binary digits. This is used to populate the additional bits added to the prefix.

The cidrsubnet function can accept and be used to define both IPv4 and IPv6 CIDR ranges. This article is focusing solely on IPv4 since that’s the most common use case.

Define IP Address Ranges and Subnet Address Spaces using CIDR Notation in Terraform

Now that you know the basis definition of the cidrsubnet Terraform function, we can now use it to define subnet address spaces. Let’s look at a couple examples!

For example, you have a network that uses the 10.0.0.0/16 IP address range in CIDR notation. This defines an address space that ranges from 10.0.0.0 to 10.0.255.255 with a total of 65,536 IP addresses. If you need to define a subnet range that starts at 10.0.1.0 and ends at 10.0.1.255 with a total of 256 IP addresses, you can call the cidrsubnet function with a newbits of 8 bits for the subnet mask and an a netnum of 1. This will result in the cidrsubnet function to output the 10.0.1.0/24 CIDR notation for the IP address range. This defines that the subnet mask will be 255.255.255.0.

The following is the code to call the function with these arguments:

cidrsubnet("10.0.0.0/16", 8, 1)

If you need to define multiple IP Address spaces for multiple subnets, then you can make multiple calls accordingly to the cidrsubnet function in your Terraform code passing in the necessary arguments.

This helps when writing Terraform code that’s taking input parameters or using Terraform Modules that is more reusable. The parameters could be used to define the CIDR range for the full network IP Address space, and then the cidrsubnet function could be used to divide that range up in the required subnets. When reusing the code, you could just make sure that you are always passing in a /16 CIDR notation to the code, then the calls to cidrsubnet to divide up that space into subnets would always be predictable without any other additional code changes.

Let’s say you need to divide up the /16 IP address space into .1.0/24, .2.0/24, and .3.0/24 subnet IP address ranges. You don’t need to always pass in the 10.0.0.0/16 CIDR notation, you could pass in any similar /16 and the code using cidrsubnet would just work as expected.

The following are the multiple calls to the cidrsubnet Terraform function to generate these 3 subnet ranges:

locals {
  prefix_cidr = "10.0.0.0/16"
}

cidrsubnet(local.prefix_cidr, 8, 1)
# outputs "10.0.1.0/24"

cidrsubnet(local.prefix_cidr, 8, 2)
# outputs "10.0.2.0/24"

cidrsubnet(local.prefix_cidr, 8, 3)
# outputs "10.0.3.0/24"

Now let’s say with code reuse, you have Terraform code that passes in the 172.16.0.0/16 address space for a different network IP address space. The following is the usage and output to expect with the same code using this different /16 CIDR notation:

locals {
  prefix_cidr = "172.16.0.0/16"
}

cidrsubnet(local.prefix_cidr, 8, 1)
# outputs "172.16.1.0/24"

cidrsubnet(local.prefix_cidr, 8, 2)
# outputs "172.16.2.0/24"

cidrsubnet(local.prefix_cidr, 8, 3)
# outputs "172.16.3.0/24"

As you can see, you don’t need to edit all the places that have CIDR subnets defined. All you need to change is the locals variable or input variable for the Terraform project, and all the calls to cidrsubnet will automatically do what they are programmed to do in a predictable manner.

Testing and Verifying the Correct cidrsubnet Arguments

If you aren’t that familia with the math around bits that is used in IP Address CIDR range notation, then it helps to have some test code you can use to verify you’re using the correct argument values when calling the cidrsubnet Terraform function.

The following is a full example of Terraform code that you can save in a .tf file and run terraform plan on to test out your usage of the cidrsubnet function:

locals {
  prefix_cidr = "172.16.0.0/16"
}

output "subnets" {
  value = [
    {
      name = "subnet1"
      cidr = cidrsubnet(local.prefix_cidr, 8, 1)
    },
    {
      name = "subnet2"
      cidr = cidrsubnet(local.prefix_cidr, 8, 2)
    },
    {
      name = "subnet3"
      cidr = cidrsubnet(local.prefix_cidr, 8, 3)
    },
  ]
}

Breaking out calls to cidrsubnet into an isolated and simple Terraform project like this, using a single file, is extremely helpful in testing just the calls to the function. This enables you to easily verify that you have the correct values for the newbits and netnum arguments to get the desired CIDR notation output from the function.

Conclusion

In this article, we looked at using the cidrsubnet function to define IP Address ranges and subnet address spaces using CIDR notation in your HashiCorp Terraform code. I hope the tips for using the cidrsubnet function help you write more reusable and maintainable Terraform code for configuring your network and subnet deployments.

Microsoft MVP

Chris Pietschmann is a Microsoft MVP, HashiCorp Ambassador, and Microsoft Certified Trainer (MCT) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to large enterprises. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.
HashiCorp Ambassador Microsoft Certified Trainer (MCT) Microsoft Certified: Azure Solutions Architect