'Template.tf and user_data.yaml.tpl- loop through a variable of type list

I am new to templates, I am trying to change terraform modules to flex as many “nameservers” as needed.

How can iterate through the values of variable?

Right now I am doing:

template.tf

variable "nameserver" {
  type = list(string)
}

nameservers = [
  "174.15.22.20",
  "174.15.12.21"
]

nameserver_1 = element(var.nameservers, 0) #nameserver_1=174.15.22.20
nameserver_2 = element(var.nameservers, 1) #nameserver_2=174.15.12.21

user_data.yaml.tpl

nameserver ${nameserver_1}
nameserver ${nameserver_2}

I want to do something like:

template.tf

vars = {
   count = length(var.nameserver)
   for nameserver in nameservers:
   nameserver_$(count.index)= ${element(var.nameserver, count.index)}
}

user_data.yaml.tpl

for nameserver in nameservers:
  nameserver  ${nameserver_[count.index]}

But I am unable to figure out the right way to do this in template.tf and user_data.yaml.tpl

Any help would be appreciated !



Solution 1:[1]

From what you've shown of template.tf I'm guessing that vars = { ... } declaration is inside a data "template_file" block. The template_file data source is primarily there for Terraform 0.11 compatibility and it only supports string values for the template variables, but since you are using Terraform 0.12 you can use the new templatefile function instead, which makes this easier by supporting values of any type.

From the template name you used I'm guessing that you intend to use this result to assign to user_data, in which case the syntax for doing that in templatefile would look something like this:

  user_data = templatefile("${path.module}/user_data.yaml.tpl", {
    nameservers = var.nameservers
  })

In your user_data.yaml.tpl file:

%{ for s in nameservers ~}
nameserver ${s}
%{ endfor ~}

The %{ ... } sequences here are Terraform template syntax. That same syntax is also available directly in the main configuration file, so for a template this small you might prefer to just write the template inline to keep things simpler:

  user_data = <<-EOT
    %{ for s in var.nameservers ~}
    nameserver ${s}
    %{ endfor ~}
  EOT

The template syntax is the same here, but because this is in the main .tf configuration file rather than in a separate template file we can just refer directly to var.nameservers here, rather than building a separate map of template variables.


The name you gave your template file seems to suggest that you are generating YAML, though the template you showed doesn't actually generate valid YAML. If you are intending the result to be YAML, you have some other options in Terraform that might be better depending on your goals:

Firstly, JSON is a subset of YAML, so you could ask Terraform to JSON-encode your data instead, and then the YAML parser in your instance (if it is YAML-spec-compliant) should be able to parse it:

  user_data = jsonencode({
    nameservers = var.nameservers
  })

An advantage of this approach is that you can let Terraform's jsonencode function worry about the JSON syntax, escaping, etc and you can just pass it the data structure you want to represent. Using templates instead might require you to handle quoting or escaping of values if they might contain significant punctuation.

Recent versions of Terraform also have a yamlencode function, but at the time of writing it's experimental and the exact details of how it formats its output might change in future releases. I would not recommend to use it as user_data right now because if the syntax details do change in a future version then that would cause your instance to be planned for replacement. In a future version of Terraform that output should be stabilized, once the team has enough feedback from real-world use to be confident that its YAML formatting decisions are acceptable for a broad set of use-cases.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Martin Atkins