Skip to content

Instantly share code, notes, and snippets.

@reegnz
Last active June 5, 2020 18:16
Show Gist options
  • Save reegnz/c2883a010aa1e8cc93803864b74812c5 to your computer and use it in GitHub Desktop.
Save reegnz/c2883a010aa1e8cc93803864b74812c5 to your computer and use it in GitHub Desktop.
Terraform interface design: list vs map

Terraform interface design: lists vs maps

The core problem

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

A better alternative

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"
  }
]

Advantages

  • 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment