CIDR-blocks specify IP ranges. But what if your service requires a list of IPs? In this blog I’ll introduce the CIDR expand Terraform module to get all IP addresses for a CIDR-block.
Terraform CIDR Expand Module
The CIDR expand module leverages the cidrhost function to generate IP addresses. The cidrhost
function accepts a CIDR-block and a host number and returns the associated IP address. Because we want all IP addresses, we call the cidrhost
function for each host number.
Only interested in the start and end IP? Read this blog post on how to use
cidrhost
for that.
locals {
cidr = "192.168.0.0/24"
ip_addresses = [ for host_number in [0, 1, 2, ..] : cidrhost(local.cidr, host_number) ]
}
With the ability to generate IP addresses, we update the code to generate the number of addresses specified by the CIDR prefix. The CIDR prefix is anything between /0
(all addresses) and /32
(a single address), for IPv4. Because the number of addresses is a power of 2 (1, 2, 4, ..), we use the pow
function.
Note that the prefix indicates the number of CIDR blocks e.g.
/32
has2 ^ 32
blocks. Use2 ^ (32 - prefix)
to get the number of addresses per block.
locals {
cidr = "192.168.0.0/24"
cidr_prefix = "24"
host_numbers = range(pow(2, 32 - local.cidr_prefix))
ip_addresses = [ for host_number in local.host_numbers : cidrhost(local.cidr, host_number) ]
}
The final challenge is to parse the input CIDR. Gladly, we can leverage Terraform validations and the cidrhost
function to deal with that.
variable "cidr" {
description = "The IP address range in CIDR notation. Required format: '0.0.0.0/0'"
type = string
validation {
condition = try(cidrhost(var.cidr, 0), null) != null
error_message = "The IP address range is invalid. Must be of format '0.0.0.0/0'."
}
}
locals {
cidr_prefix = split("/", var.cidr)[1]
host_numbers = range(pow(2, 32 - local.cidr_prefix))
ip_addresses = [ for host_number in local.host_numbers : cidrhost(var.cidr, host_number) ]
}
Terraform CIDR Expand Module Usage
The module is available through GitHub. Use it in your Terraform configuration with the following snippet.
module "cidr_expand" {
source = "git::https:/github.com/binxio/terraform-cidr-expand.git?ref=1.0.0"
cidr = "192.168.0.0/28"
}
output "cidr_expand_ip_addresses" {
description = "IP addresses in CIDR-block."
value = module.cidr_expand.ip_addresses
}
Google Cloud DNS Example
This example configures DNS records for private Google API access using the restricted virtual IP (restricted.googleapis.com). Because the DNS A-record data require fixed IP addresss, CIDR-expand is used.
module "cidr_restricted_googleapis" {
source = "git::https:/github.com/binxio/terraform-cidr-expand.git?ref=1.0.0"
cidr = "199.36.153.4/30"
}
# NOTE: This example is limited to 'googleapis.com'. Make sure to include other
# google service domains as well, https://cloud.google.com/vpc/docs/configure-private-google-access#config
resource "google_dns_record_set" "googleapis_com_base" {
project = var.project_id
managed_zone = google_dns_managed_zone.googleapis_com.name
name = "restricted.googleapis.com."
type = "A"
ttl = 300
rrdatas = module.cidr_restricted_googleapis.ip_addresses
}
resource "google_dns_record_set" "googleapis_com_redirect" {
project = var.project_id
managed_zone = google_dns_managed_zone.googleapis_com.name
name = "*.googleapis.com."
type = "CNAME"
ttl = 300
rrdatas = ["restricted.googleapis.com."]
}
resource "google_dns_managed_zone" "googleapis_com" {
project = var.project_id
name = "googleapis-com"
dns_name = "googleapis.com"
visibility = "private"
private_visibility_config {
networks {
network_url = var.network_id
}
}
}
Discussion
The simplicity of the implementation makes me wonder why there is no built-in feature for this. This greatly improves the performance and ease of use. Actual use-cases, at the same time, are limited. Nevertheless I created this module to have a unified IP abstraction: the CIDR notation. This abstraction makes it esasy to design my Terraform modules and pass along IP constraints.
Conclusion
The CIDR expand module complements the cidrhost function by listing all IP addresses.