Terraform templating and loops

Alex Leonhardt
Mar 28, 2018 · 3 min read

You probably already know it’s not easily possible to loop through a variable of type slice/list within templates, in fact, there are no loop constructs within TF templates at all at this point in time.

There is hope though, according to their comment on this GH issue: https://github.com/hashicorp/terraform/issues/16628

So, say we want to generate a JSON file from a slice/array that, once finished, looks something like this :

We cannot simply iterate over an array to change the value of endpointX as we cannot loop through it.

What we’d expect to be able to do, in the template, is something like this

we’d have to handle that last , but let’s ignore that for now — the idea is simple, iterate over values in the array of map endpoints and use each key and value as variables within that loop to generate the json file. As we already know, this isn’t possible, and we won’t get this anytime soon.

Luckily, necessity is the mother of invention, and I got this to work with the existing loop constructs outside the template; although it’s not as “clean” as above, it’s usable and produces the desired result.

To make use of TFs built-in loop construct and generate the wanted json, we will need 2 template files, the first will look like this

// data.json.tmpl{
"name": "${name}",
"endpoint": "${endpoint}"
}

and the second template looks like this

// service.json.tmpl[
${value},
{"links": ${links}}
]

Maybe you already know where we’re heading with this, we now render the first template using the built-in loop constructs from TF using the template_file data resource

variable "endpoints" {
type = "list"
default = [
{ endpoint1 = "https://endpoint-1.example.com" },
{ endpoint2 = "https://endpoint-2.example.com" },
{ endpoint3 = "https://endpoint-3.example.com" },
]
}
data "template_file" "data_json" {
template = "${file("${path.module}/data.json.tmpl")}"
count = "${length(var.endpoints)}"
vars {
endpoint = "${element(values(var.endpoints[count.index]), 0)}"
name = "${element(keys(var.endpoints[count.index]), 0)}"
}
}

and then re-use the .rendered output as input to the 2nd template, which really just generates the list by pre- and post- fixing the $value variable with [] .

variable "links" {
type = "list"
default = [
"link1",
"link2",
"link3",
]
}
data "template_file" "service_json" {
template = "${file("${path.module}/service.json.tmpl")}"
vars {
value = "${join(",", data.template_file.data_json.*.rendered)}"
links = "${jsonencode(var.links)}"
}}

and finally we’re using the output module to show us what we’ve done, this is also how you’d use the result as input to any other TF module that accepts json.

output "json" {
value = "${data.template_file.service_json.rendered}"
}

and the output

$ tf apply
data.template_file.data_json[2]: Refreshing state...
data.template_file.data_json[0]: Refreshing state...
data.template_file.data_json[1]: Refreshing state...
data.template_file.service_json: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.Outputs:json = [
{
"name": "endpoint1",
"endpoint": "https://endpoint-1.example.com"
},{
"name": "endpoint2",
"endpoint": "https://endpoint-2.example.com"
},{
"name": "endpoint3",
"endpoint": "https://endpoint-3.example.com"
},
{"links": ["link1","link2","link3"]}
]

You might have noticed the jsonencode in the last part, this helps to convert any structure to valid json and it becomes valid input to the template as json in essence is just text

variable "links" {
type = "list"
default = [
"link1",
"link2",
"link3",
]
}
data "template_file" "service_json" {
[...]
links = "${jsonencode(var.links)}"
}}

{ ovni }

it’s out of this world

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store