0
votes

Is there a way in Terraform where I can interpolate all three components of cidrsubnet function ? The reason I'm asking this is because I want to create VPCs with different CIDR prefix and accordingly the cidrsubnet function takes care of creating subnets for me.

Supposingly, VPC "prod" has CIDR 10.10.0.0/16 and VPC "dev" has CIDR 10.20.0.0/24. I'm looking for a solution wherein the same code will create subnets for me by interpolating values of cidrsubnet function something like below:

# map for different CIDR

variable "VPC_CIDR" {
    type = "map"
    default = {
        "dev" = "10.10.0.0/24"
        "prod" = "10.10.0.0/16"
    }
}

variable "PRI_SUBNET_COUNT" {
  default = "1"
}

# intended logic which interpolates netnum (impractical code)

if var.VPC_CIDR = "prod"
  netnum = 4
elif var.VPC_CIDR = "dev"
  netnum = 3   
else 
  netnum = 2

resource "aws_subnet" "sub-node-private" {
    count = var.PRI_SUBNET_COUNT
    cidr_block = cidrsubnet(var.VPC_CIDR, var.netnum, count.index + 2) #all three components interpolated

The above code will create subnet CIDR 10.10.0.0/20 for prod and 10.10.0.0/28 for dev. This way my code remains same, only the variables get interpolated.

N.B: This code is for demonstration purpose. It is known that this is not the practical code for if/else in Terraform.

1

1 Answers

2
votes

If the keys of var.VPC_CIDR are selectable by the caller then it may be best to combine the CIDR prefix and the number of new bits to use for its subnets together in the variable. Since the conventional way to name Terraform variables is in lowercase, I'm going to also rename it to vpc_cidr in the following examples.

variable "vpc_cidr" {
  type = map(object({
    cidr_block  = string
    subnet_bits = number
  }))
  default = {
    dev = {
      cidr_block  = "10.10.0.0/24"
      subnet_bits = 3
    }
    prod = {
      cidr_block  = "10.10.0.0/16"
      subnet_bits = 4
    }
  }
}

variable "pri_subnet_count" {
  type    = number
  default = 1
}

locals {
  vpc_subnets = flatten([
    for name, vpc in var.vpc_cidr : [
      for i in count(var.pri_subnet_count) : {
        name        = "${name}-${i}"
        vpc_name    = name
        cidr_block  = vpc.cidr_block
        subnet_bits = vpc.subnet_bits
        network_num = i + 2
      }
    ]
  ])
}

resource "aws_vpc" "example" {
  for_each = var.vpc_cidr

  cidr_block = each.value.cidr_block
}

resource "aws_subnet" "private" {
  for_each = { for s in local.vpc_subnets : s.name => s }

  vpc_id     = aws_vpc.example[each.value.vpc_name].id
  cidr_block = cidrsubnet(each.value.cidr_block, each.value.subnet_bits, each.value.network_num)
  # ...
}

If the names "prod" and "dev" are fixed and thus your module will assume they will always be specified, you can derive the subnet_bits values automatically in a way similar to what you described, like this:

variable "vpc_cidr" {
  type = object({
    # Force caller to provide "dev" and "prod" values, so
    # that it will match up with the attributes in
    # local.subnet_bits defined below.
    dev  = string
    prod = string
  })
  value = {
    dev = "10.10.0.0/24"
    prod = "10.10.0.0/16"
  }
}

variable "pri_subnet_count" {
  type    = number
  default = 1
}

locals {
  subnet_bits = {
    dev  = 3
    prod = 4
  }

  vpcs = {
    for name, cidr_block in var.vpc_cidr : name => {
      cidr_block  = cidr_block
      subnet_bits = local.subnet_bits[name]
    }
  }

  vpc_subnets = flatten([
    for name, vpc in local.vpcs : [
      for i in count(var.pri_subnet_count) : {
        name        = "${name}-${i}"
        vpc_name    = name
        cidr_block  = vpc.cidr_block
        subnet_bits = vpc.subnet_bits
        network_num = i + 2
      }
    ]
  ])
}

resource "aws_vpc" "example" {
  for_each = local.vpcs

  cidr_block = each.value.cidr_block
}

resource "aws_subnet" "private" {
  for_each = { for s in local.vpc_subnets : s.name => s }

  vpc_id     = aws_vpc.example[each.value.vpc_name].id
  cidr_block = cidrsubnet(each.value.cidr_block, each.value.subnet_bits, each.value.network_num + 2)
  # ...
}

The general pattern illustrated above is building the data structure local.vpc_subnets which contains one element for each subnet you want to create. That then allows repeating aws_subnet.private for each element, and gathers together all of the values required to populate the vpc_id and cidr_block arguments on the subnet.