Using an environment variable as a map for common tags in a locals block

Viewed 3872

I've run into a specific problem while trying to automate a tagging process in terraform. I've set an environment variable that is essentially a list of all the tags we'd be using for all resources provisioned in the apply. It looks like this...

export TF_VAR_taglist='{JiraEpic = "ETOS-56","AssignedResearcherPri" = "Isaac",AssignedResearcherSec = "Matt"}'

After setting the environment variable I added a variable called "taglist" in the variables.tf file that grabs the aforementioned environment variable. It looks like this...

variable "taglist"{}

Lastly, I have another locals.tf file where i set a common_tags variable. Like so...

locals { common_tags ="${var.taglist}" }

When i terraform apply, the build fails while trying to map the tags properly. This is the error i receive...

Error: Incorrect attribute value type

  on kube_master_worker_nodes_ec2.tf line 9, in resource "aws_instance" "master":
   9:   tags = local.common_tags
    |----------------
    | local.common_tags is "{JiraEpic = \"ETOS-56\",AssignedResearcherPri = \"Isaac \",AssignedResearcherSec = \"Matt\"}"

Inappropriate value for attribute "tags": map of string required.

I then decided to define the type of the variable as map(string in the variables.tf file like this variable "taglist"{ type = map(string) } I had hoped that this would allow terraform to recognize this variable as a map of strings and not just a string literal, but I was wrong, and these are the errors I get when that definition is applied.

Error: Missing attribute separator

  on <value for var.taglist> line 1:
  (source code not available)

Expected a newline or comma to mark the beginning of the next attribute.


Error: No value for required variable

  on variables.tf line 11:
  11: variable "taglist"{

The root module input variable "taglist" is not set, and has no default value.
Use a -var or -var-file command line argument to provide a value for this
variable.

I'm really stuck on this, and I feel like I'm close. Can anyone provide some insight into this and how I should go about solving it?

2 Answers

Terraform uses the type constraint of a variable to decide how to interpret a string representation of its value. By default, Terraform will assume the value expects a primitive type such as a string or number, because that's the most typical case for variables set via the command line or environment variables.

Since your tag list is a list you need Terraform to interpret it as a map expression rather than as a string. You can tell Terraform to do that by telling Terraform which type of value you expect:

variable "taglist" {
  type = map(string)
}

You can read more about this in the Terraform documentation section Complex-typed Values.

You then need to make sure that the value in the environment variable is a valid object expression in order to avoid a syntax error. If you're setting the environment variable from the shell command line then you need to be mindful of escaping/quoting to ensure that Terraform will see the value with all of the quotes intact, and without any extra metacharacters.

The result is often hard to read clearly, which is why the Terraform documentation recommends using a .tfvars file to set complex-typed variables, instead of the -var command line option or environment variables. However, since you are using automation you might find it easier to generate a .tfvars.json file instead, which uses standard JSON format and is therefore easier to generate using JSON libraries available in most programming languages. Here's the .tfvars.json equivalent of the value you showed in your question:

{
  "taglist": {
    "JiraEpic": "ETOS-56",
    "AssignedResearcherPri": "Isaac",
    "AssignedResearcherSec": "Matt"
  }
}

Note that subjectively I'd find it pretty confusing to have a variable whose name ends in list when it actually expects a map. A more typical name for this variable would be just tags, though if it's useful to mention its type in order to distinguish it from other variables then I'd suggest tag_map instead, to make it less confusing.

I want to first thank Martin Atkins for giving me the idea of using colons instead of equal signs in my environment variable, because that was the ONLY issue. The variable was not properly represented as a JSON object so terraform was interpreting it as a string.

I changed

export TF_VAR_taglist='{JiraEpic = "ETOS-56","AssignedResearcherPri" = "Isaac",AssignedResearcherSec = "Matt"}'

to this

export TF_VAR_taglist='{"JiraEpic":"ETOS-56","AssignedResearcherPri":"Isaac", "AssignedResearcherSec":"Matt"}'

The build purrs like a kitten on catnip now.

Related