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.