Blog

Get all IP addresses of a CIDR-block using Terraform

21 Dec, 2021
Xebia Background Header Wave

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 has 2 ^ 32 blocks. Use 2 ^ (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.

Laurens Knoll
As a cloud consultant I enjoy taking software engineering practices to the cloud. Continuously improving the customers systems, tools and processes by focusing on integration and quality.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts