Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable keys in terraform maps

Tags:

terraform

In Terraform, I'm trying to create a module with that includes a map with variable keys. I'm not sure if this is possible but I've tried the following without success.

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

and this:

tags {
   Name = "${var.role} ${var_env}"
}
tags."${var.role}" = "${var.env}"

Any ideas? Is this not possible with Terraform currently?

like image 488
JoeyP Avatar asked Feb 18 '16 20:02

JoeyP


People also ask

What is map variable Terraform?

terraform-map-variable.tfA variable can have a map type assigned explicitly, or it can be implicitly declared as a map by specifying a default value that is a map. The above demonstrates both. Then, replace the aws_instance with the following: resource "aws_instance" "example" { ami = var.

How do you pass a variable value in Terraform?

Using the -var Command-line Flag The -var flag is used to pass values for Input Variables to Terraform commands. This flag can be used with both of the Terraform plan and apply commands. The argument passed to the -var flag is a string surrounded by either single or double quotes.

What are variables in Terraform?

Variables in Terraform are a great way to define centrally controlled reusable values. The information in Terraform variables is saved independently from the deployment plans, which makes the values easy to read and edit from a single file.

What is each key in Terraform?

Basic Syntaxfor_each is a meta-argument defined by the Terraform language. It can be used with modules and with every resource type. The for_each meta-argument accepts a map or a set of strings, and creates an instance for each item in that map or set.


3 Answers

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.

like image 86
gsaslis Avatar answered Oct 22 '22 23:10

gsaslis


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.

like image 24
tavnab Avatar answered Oct 22 '22 22:10

tavnab


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}"
  )}"
}
like image 7
lander2k2 Avatar answered Oct 22 '22 23:10

lander2k2