A lot of us have encountered this before: I need to create multiple instances of a resource,
and we don't want to shoot ourselves in the foot with count
, so we want to use for_each.
We define our input as a map of object, each key representing a resource instance.
The key of the map will be the key of the resource in for_each
.
variable "vpc_id" {
type = string
}
variable "subnets" {
type = map(object({
cidr_block = string
}))
}
resource "aws_subnet" subnet {
for_each = var.subnets
vpc_id = var.vpc_id
cidr_block = each.value.cidr_block
tags = {
Name = each.key
}
}
Now this looks great, but now we need to provide those values in a tfvars.
vpc_id = "1231231323"
subnets = {
public = {
cidr_block = "10.0.0.0/24"
}
private = {
cidr_block = "10.0.1.0/24"
}
}
Now we realize we have a problem on our hands:
- our users want to define a subnet list
- our users are confused that they have to provide a map instead
- our users are confused as to what the map key is
Instead of a map, we could also use a list, introducing a new field into the list items called subnet_name
.
variable "subnets" {
type = list(object({
subnet_name = string
cidr_block = string
}))
}
}
And so that we don't have to use count
, but still use for_each
, we can transform that list into
a map, keyed by the subnet_name
.
locals {
subnet_map = {
for item in var.subnets:
item.subnet_name => item
}
}
}
You can now define your subnet
variable like this:
subnets = [
{
subnet_name = "public"
cidr_block = "10.0.0.0/24"
},
{
subnet_name = "private"
cidr_block = "10.0.1.0/24"
}
]
- subnet definition is now a list
- resource creation still uses
for_each
- there's no confusion that a
subnet_name
needs to be provided - Maps/objects have stable keys
For better interfaces, don't use maps, use keyed lists instead!