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.
Table of Contents
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
Theprefix
must be given in CIDR notation and is the full IP Address range that will be divided up into smaller ranges.newbits
Thenewbits
is the number of additional bits with which to extend the prefix. For example, if given a prefix ending in/16
and using anewbits
value of4
, the resulting subnet address will have the length of/20
.netnum
Thenetnum
is a whole number that can be represented as a binary integer with no more thannewbits
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.