Loop for the same line in terraform template

Hello,

I have the following map (of map) variable:

kubernetes_servers = {
 	etcd-1 = {
 		ip_address = "10.88.88.222"
	}
 	etcd-2 = {
 		ip_address = "10.88.88.223"
	}
 	etcd-3 = {
 		ip_address = "10.88.88.224"
	}
 	kube-controlplane-1 = {
 		ip_address = "10.88.88.225"
	}
 	kube-controlplane-2 = {
 		ip_address = "10.88.88.226"
	}
 	kube-worker-1 = {
 		ip_address = "10.88.88.227"
	}
}

What I need to achieve is eventually this:

initial-cluster: etcd-1=https://10.88.88.222:2380,etcd2=https://10.88.88.223:2380,etcd3=https://10.88.88.224:2380

What I tried doing was to split the variables into two lists, so that it’s accepted by the terraform template as such:

kubeadm_config_file = templatefile("${path.module}/files/kubernetes/kubeadm-config.tpl", {
            etcd_names = [ for name, v in var.kubernetes_servers : "${name}" if startswith(name, "etcd-") ]
            etcd_ips = [ for name, v in var.kubernetes_servers : "${v["ip_address"]}" if startswith(name, "etcd-") ]
                         })

But with this solution I have two big issues: two variables instead of one, so I’m not sure how I’m supposed to use a loop where I’d use the values of both variables for the same iteration (name and ip) and the second issue is that I need to do this all in one line.

Maybe it made more sense to do it directly in hcl and the insert the string as such into the template, if it’s even possible?

Ok, I’ve got it. I did learn something from the help I got here and from the documentation eventually :slight_smile:

node = join("", [ for name, v in var.kubernetes_servers : "${name}=https://${v["ip_address"]}" if startswith(name, "etcd-") ])

This simply generates the whole line as is :slight_smile:

Hi @lethargosapatheia,

If you are happy with handling part of the “templating” in the call instead of inside the template then indeed that seems like a nice pragmatic approach.

I think it should be possible to implement this inside the template too, but it’s a matter of opinion whether the result is better or worse than doing it the way you did it.

The template-based approach I would consider is to do the filtering and projection out in the call but still do the final string templating instead the template.

templatefile("...", {
  etcd_nodes = {
    for name, node in var.kubernetes_servers :
    name => node.ip_address
    if startswith(name, "etcd-")
  }
})

Then inside the template file something like this:

initial-cluster: ${
  join("," [
    for name, addr in etcd_nodes :
    "${name}=https://${addr}:2380"
  ])
}

As noted above, I’m not sure this is actually better, just a bit different: it puts the concern about the node list syntax inside the template instead, but at the expense of doing the job with two for expressions rather than only one.

1 Like