Variable keys in terraform maps

Update

The accepted answer describes how to do dynamic lookups in an existing map. For constructing maps with dynamic keys, in HCL2 (0.12), you can use a quoted interpolation expression in the key:

resource "aws_instance" "web" {
  count = "${var.ec2_instance_count}"
  
  ami = "${var.base_ami}"
  availability_zone = "${var.region_a}"
  instance_type = "${var.ec2_instance_size}"
  security_groups = ["sec1"]

  tags = {
    Name              = "${var.role} ${var.env}"
    role              = "${var.app_role}"
    "${var.app_role}" = "${var.env}"               # <------ like this
  }
}

And once issue #21566 is fixed, you can replace "${var.app_role}" with (var.app_role), which is the method described in the documentation.

(The same caveat as below applies here as well: if var.app_role contains one of those literal keys as its value, then it will replace it.)

Old answer

The accepted answer describes how to do dynamic lookups in an existing map. For constructing maps with dynamic keys, in HCL2 (0.12), you have two ways:

For expressions + merge

You can use for expressions to dynamically build a map from one or more variables for your keys, and then use that in combination with the merge function to build a new map with a mix of static and dynamic keys:

variable "app_role" {
  type = string
}

locals {
  tags = merge(
    {
      Name = "${var.role} ${var.env}"
      role = "${var.app_role}"
    },
    {
      for k in [var.app_role]: k => "${var.env}"
    }
  )
}

zipmap

Alternatively, you can use zipmap to construct it in one shot:

locals {
  tags = zipmap(
    [
       "Name",
       "role",
       var.app_role
    ],
    [
       "${var.role} ${var.env}",
       var.app_role,
       var.env
    ]
  )
}

You can then use this map in a resource:

resource "aws_instance" "web" {
  count = "${var.ec2_instance_count}"
  
  ami = "${var.base_ami}"
  availability_zone = "${var.region_a}"
  instance_type = "${var.ec2_instance_size}"
  security_groups = ["sec1"]

  tags = local.tags // or inline the above here
}

One caveat is that if var.app_role is equal to either "Name" or "role", then it'll overwrite your static value. You can avoid this by swapping the arguments in merge or reordering the lists in zipmap, though such a collision would more likely be a configuration error that should be caught & fixed before applying.


There's (now) a lookup function supported in the terraform interpolation syntax, that allows you to lookup dynamic keys in a map.

Using this, I can now do stuff like:

output "image_bucket_name" {
  value = "${lookup(var.image_bucket_names, var.environment, "No way this should happen")}"
}

where:

variable "image_bucket_names" {
  type = "map"

  default = {
    development = "bucket-dev"
    staging = "bucket-for-staging"
    preprod = "bucket-name-for-preprod"
    production = "bucket-for-production"
  }

}

and environment is a simple string variable.


The following works with terraform version 0.11.7. This solution uses the map function.

resource "aws_instance" "web" {
  ...
  tags = "${map(
    "Name", "${var.role} ${var_env}",
    "role", "${var.app_role}",
    "${var.app_role}", "${var_env}"
  )}"
}

I recently needed to set a tag key dynamically too, and managed to do it using zipmap:

locals {
  ec2_tag_keys = ["some/prefix/${var.some_var}", "another_tag"]
  ec2_tag_vals = ["some value", "another value"]
}

resource "aws_instance", "foo" {
  ...
  tags = "${zipmap(local.ec2_tag_keys, local.ec2_tag_vals)}"
}

It's a little clunky, but it works.

Tags:

Terraform