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.