Iterate using templatefile() in YAML

I currently am using a json file to iterate over list for resource creation using a for expression. A solution was provided in Iterating using templatefile() in JSON · Issue #33187 · hashicorp/terraform · GitHub. As this project has progressed I have pivoted to using yaml syntax instead for better readability and to be in-line with yaml files that we are ingesting from other sources. Is iteration possible with a yaml template file? Receiving “│ Invalid value for “vars” parameter: vars map does not contain key “index”…” error when attempting to loop through syslog servers.

resource "bigip_do" "ltm__tmpl_do" {
  timeout = 15
  do_json = templatefile("../templates/ltm_lab_DO.yaml.tf", {
    HOSTNAME                 = "ltm1-lab"
    NAME_SERVERS             = ["8.8.8.8", "8.8.4.4"]
    SYSLOG_SERVERS             = ["1.1.1.1", "2.2.2.2"]
    INT_VIPS_1_SELFIP_IP     = "10.169.21.10/24"
    INT_VIPS_2_SELFIP_IP     = "10.169.22.10/24"
    INT_VIPS_3_SELFIP_IP     = "10.169.23.10/24"
    EXT_VIPS_1_SELFIP_IP     = "10.169.24.10/24"
    EXT_VIPS_2_SELFIP_IP     = "10.169.25.10/24"
    EXT_VIPS_3_SELFIP_IP     = "10.169.26.10/24"
    TRAFFIC_SELFIP_IP        = "10.169.27.70/27"
    REMOTE_ACCESS_ALLOW_LIST = ["1.1.1.1/24", "2.2.2.2/24"]
    SNMP_ALLOW_LIST = [
      "127.0.0.0/8",
      "10.169.117.216",
      "1.1.1.1/24", 
      "2.2.2.2/24"
    ]
    VPC_CIDR_BLOCK = ["10.169.21.0/19", "10.179.32.0/20"]
  })
}

schemaVersion: 1.37.0
class: Device
async: true
Common: 
  class: Tenant
  System:
    class: System
    hostname: ${HOSTNAME}
    mgmtDhcpEnabled: false
    autoCheck: false
    autoPhonehome: true
  Provision:
    class: Provision
    ltm: dedicated
  NTP:
    class: NTP
    servers: ${NAME_SERVERS}
  DNS:
    class: DNS
    nameServers: ${NAME_SERVERS}
    search:
      - ops.sfdc.net
  EXT-VIPS-1-VLAN:
    class: VLAN
    tag: 102
    interfaces:
      - name: "1.2"
        tagged: false
  EXT-VIPS-2-VLAN:
    class: VLAN
    tag: 103
    interfaces:
      - name: "1.3"
        tagged: false
  EXT-VIPS-3-VLAN:
    class: VLAN
    tag: 104
    interfaces:
      - name: "1.4"
        tagged: false
  INT-VIPS-1-VLAN:
    class: VLAN
    tag: 105
    interfaces:
      - name: "1.5"
        tagged: false
  INT-VIPS-2-VLAN:
    class: VLAN
    tag: 106
    interfaces:
      - name: "1.6"
        tagged: false
  INT-VIPS-3-VLAN:
    class: VLAN
    tag: 107
    interfaces:
      - name: "1.7"
        tagged: false
  TRAFFIC-VLAN:
    class: VLAN
    tag: 101
    interfaces:
      - name: "1.1"
        tagged: false
  INT-VIPS-1-SELFIP:
    class: SelfIp
    address: INT_VIPS_1_SELFIP_IP
    vlan: INT-VIPS-1-VLAN
    trafficGroup: traffic-group-local-only
    allowService: none
  INT-VIPS-2-SELFIP:
    class: SelfIp
    address: INT_VIPS_2_SELFIP_IP
    vlan: INT-VIPS-2-VLAN
    trafficGroup: traffic-group-local-only
    allowService: none
  INT-VIPS-3-SELFIP:
    class: SelfIp
    address: INT_VIPS_3_SELFIP_IP
    vlan: INT-VIPS-3-VLAN
    trafficGroup: traffic-group-local-only
    allowService: none
  EXT-VIPS-1-SELFIP:
    class: SelfIp
    address: EXT_VIPS_1_SELFIP_IP
    vlan: EXT-VIPS-1-VLAN
    trafficGroup: traffic-group-local-only
    allowService: none
  EXT-VIPS-2-SELFIP:
    class: SelfIp
    address: EXT_VIPS_2_SELFIP_IP
    vlan: EXT-VIPS-2-VLAN
    trafficGroup: traffic-group-local-only
    allowService: none
  EXT-VIPS-3-SELFIP:
    class: SelfIp
    address: EXT_VIPS_3_SELFIP_IP
    vlan: EXT-VIPS-3-VLAN
    trafficGroup: traffic-group-local-only
    allowService: none
  TRAFFIC-SELFIP:
    class: SelfIp
    address: TRAFFIC_SELFIP_IP
    vlan: TRAFFIC-VLAN
    trafficGroup: traffic-group-local-only
    allowService: default
  Snmp:
    class: SnmpAgent
    allowList: ${SNMP_ALLOW_LIST}
  SSHD:
    class: SSHD
    allow: ${REMOTE_ACCESS_ALLOW_LIST}
  HTTPD:
    class: HTTPD
    allow: ${REMOTE_ACCESS_ALLOW_LIST}
{for index, syslog_server in SYSLOG_SERVERS : "remotesyslog${index + 1}" => {"host": syslog_server, "class": "SyslogRemoteServer"}}

Error:

│ Error: Invalid function argument
│ 
│   on ltm_lab.tf line 40, in locals:
│   40:   do_yaml = templatefile("../templates/ltm_lab_DO.yaml.tf", {
│   41:     HOSTNAME                 = "ltm1-lab"
│   42:     NAME_SERVERS             = ["8.8.8.8", "8.8.4.4"]
│   43:     SYSLOG_SERVERS             = ["1.1.1.1", "2.2.2.2"]
│   44:     INT_VIPS_1_SELFIP_IP     = "10.169.21.10/24"
│   45:     INT_VIPS_2_SELFIP_IP     = "10.169.22.10/24"
│   46:     INT_VIPS_3_SELFIP_IP     = "10.169.23.10/24"
│   47:     EXT_VIPS_1_SELFIP_IP     = "10.169.24.10/24"
│   48:     EXT_VIPS_2_SELFIP_IP     = "10.169.25.10/24"
│   49:     EXT_VIPS_3_SELFIP_IP     = "10.169.26.10/24"
│   50:     TRAFFIC_SELFIP_IP        = "10.169.27.70/27"
│   51:     REMOTE_ACCESS_ALLOW_LIST = ["1.1.1.1/24", "2.2.2.2/24"]
│   52:     SNMP_ALLOW_LIST = [
│   53:    	  "127.0.0.0/8",
│   54:       "10.169.117.216",
│   55:       "1.1.1.1/24", 
│   56:       "2.2.2.2/24"
│   57:     ]
│   58:     VPC_CIDR_BLOCK = ["10.169.21.0/19", "10.179.32.0/20"]
│   59:     })
│     ├────────────────
│     │ while calling templatefile(path, vars)
│ 
│ Invalid value for "vars" parameter: vars map does not contain key "index", referenced at ../templates/ltm_lab_DO.yaml.tf:116,65-70.
╵

This YAML content appears to be corrrupt. It appears to have little bits of Terraform code sprinkled through it.

I get the same result when that is removed. I used the merge function similar to how it was used in Iterating using templatefile() in JSON · Issue #33187 · hashicorp/terraform · GitHub to merge the maps.

Please review Guide to asking for help in this forum and provide the following missing information:

  • The full exact error message from Terraform, including all context lines it prints

  • A correct, uncorrupted, version of the template, so readers of the forum can reproduce the error. I can clearly see various characters are missing in what you have posted so far.

As @maxb says it would be helpful to see more about what problem you encountered so that folks can reproduce it locally to work on a fix.

But speaking more generally, I’d suggest trying to follow the advice in Generating JSON or YAML from a template in this situation. The result should be easier to debug if you are still seeing some problems after adopting that strategy.

I’ve updated the post with the correct code and also provided the completely error output.

I’m afraid I have to say, no you have not - because:

  1. This line is neither valid YAML nor valid Terraform template syntax:
  1. Your error message indicates a problem on line 258 of the template but you have only provided 116 lines of it.

You have, however provided the error message in full. The error is a pretty simple one: You have referenced a template input value called index within your template, but have not provided any input value named index. Hence, error.

But since you haven’t shown us the part of the template - that is specifically identified in the error message! (../templates/ltm_lab_DO.yaml.tf:258,65-70) - there’s not much more we can say without that.

I had to redact some configurations which is why the line number is identified as line 258. It is line 116.

  • It would have been nice if you had made that clear, given a specific line number was referenced.

  • My other point still stands:

I brought this up in my very first reply in this topic, yesterday, albeit not quite as explicitly then.

Ok, so not possible to do for expressions. Got it!

I’m not quite sure what you mean by that.

The problem throughout this conversation has been that the code you’ve presented has been sufficiently far away from valid syntax, that it hasn’t really explained clearly enough what you were trying to do.

I think I’ve finally realised your intent, by reading through the extra history in Iterating using templatefile() in JSON · Issue #33187 · hashicorp/terraform · GitHub, and I think your best way forward is to accept the advice offerered by @apparentlymart in this thread:

which is actually the same advice @apparentlymart gave you previously in Iterating using templatefile() in JSON · Issue #33187 · hashicorp/terraform · GitHub

If you prefer to have that produce YAML instead of JSON, just change jsonencode to yamlencode.

I must not be understanding how it is we are suppose to do for expressions in yaml. Json makes sense based on the conversation I had with apparentlymart. The doc, Generating JSON or YAML from a template , is a bit difficult to follow when it comes to more complex things. I’ve decided to use other resources to get me the results I was looking for. Thanks for the help!