Templatefile: nested iteration

I have a list(list(string)) variable in the form:

  vpn_clients = [
    #friendlyname    ipaddress          #psk                                              #pubkey  
    ["userA",         "",    "XXXX",   "YYY"],
    ["userB",         "",    "ZZZZ",   "TTT"],

And I want to iterate trough the whole object. In the template file I tried with:

%{ for A in vpn_users ~}
%{ for friendlyname, ip, pubkey, psk in A ~}
# ${friendlyname}
PublicKey = ${pubkey}
AllowedIPs = ${ip}
PresharedKey = ${psk}

%{ endfor ~}
%{ endfor ~}

But it seems to be incorrect:

Invalid ‘for’ directive; For directive requires ‘in’ keyword after names.

The documentation is not really helpful, I read:
string functions

Could you please address me to the right docs?

I don’t think the Terraform template for directive implements tuple/list unpacking.

Judging from the documentation at Strings and Templates - Configuration Language | Terraform | HashiCorp Developer, only a single name can be assigned in the for construct. You’d need to do the unpacking of your inner list elsewhere.

It seems like your intent here is to destructure the tuple (assign each of its elements to a separate symbol) rather than to iterate over it (declare one symbol and assign to it one element at a time, re-evaluating the body for each element).

Terraform templates do not have a destructuring assignment syntax, so to express this template you’ll need to refer to the tuple elements directly:

%{ for user in vpn_users ~}
# ${user[0]}
PublicKey = ${user[2]}
AllowedIPs = ${user[1]}
PresharedKey = ${user[3]}

%{ endfor ~}

Note that for this situation, where your template expects exactly four elements per item, the more appropriate data type for this variable would be list(tuple([string, string, string, string])), since your module won’t work properly if it doesn’t get exactly four strings. Using sequence-like types in this way is quite idiosyncratic, though.

For situations like this in Terraform it’s more typical to use an object type than a tuple type, since that then allows each of the attributes to have a descriptive name rather than just an index.

variable "vpn_clients" {
  type = list(object({
    friendly_name = string
    ip_address    = string
    public_key    = string
    preshared_key = string

Then your template would look like this, which I think is more readable but of course that’s a matter of personal taste:

%{ for user in vpn_users ~}
# ${user.friendly_name}
PublicKey = ${user.public_key}
AllowedIPs = ${user.ip_address}
PresharedKey = ${user.preshared_key}

%{ endfor ~}