Iterate through a nested map when using a templatefile

Hi All,

I am trying to iterate through a nested map within a templatefile provided from the input variables.
I have seen one other post on this but it doesnt quite make sense to me in my scenario.

I basically have a yaml templatefile which is has the declaration for deploying an azure policy that appends NSG rules to all NSGs and i want to deploy these policies on a per subscription basis (this is my root map) but then for each subscription i want to be able to dynamically specify some of the NSG rule details in particular the list of IP addresses for the source or destination and this is my nested map.

below is the actual templatefile:

%{ for rule, value in subscriptions }
  subscription_policy_assignment:
    nsg-rule-tcp-inbound:
      display_name: "NSG rule 1"
      description: "This policy ensures that there is a rule on all nsgs."
      enforce: true
      policy_definition_name: common-nsg-rule
      subscription_id: ${value.subscription_id}
      identity: {}
      parameters:
        name: Allow-Tcp-Inbound
        priority: 4000
        direction: Inbound
        access: Allow
        protocol: "Tcp"
        sourcePortRange: 
          - "*"
        destinationPortRange: 
          - "5986"
          - "443"
          - "1433"
        sourceAddressPrefix:
        %{ for ranges in value.source_ranges ~}
          - ${ranges}
        %{ endfor ~}
        destinationAddressPrefix: 
          - "10.0.96.0/19"
          - "10.0.128.0/18"
        effect: Append
%{ endfor ~}

and below is the input variables i am supplying:

subscriptions:
  pr:
    subscription_id: "xxx"
    source_ranges:
      - "10.0.6.0/23"
      - "10.0.70.0/23"

everything appears correct to me but it does not work so not sure whether its because this is not possible or i have done something wrong

the error i get is:

│ Error: Error in function call
│ 
│   on main.tf line 17, in locals:
│   17:   policies  = yamldecode(templatefile("./policies.tpl", local.envs[var.env].input_data))
│     ├────────────────
│     │ while calling yamldecode(src)
│     │ local.envs is object with 2 attributes
│     │ var.env is "prod"
│ 
│ Call to function "yamldecode" failed: on line 454, column 17: did not find
│ expected key.

Hi @fsaleem-smt,

I suggest that you follow the advice in Generating JSON or YAML from a template to avoid this problem.

Generating YAML or JSON by string concatenation is hard to do correctly, so you should use Terraform’s yamlencode function to generate YAML instead so that Terraform can be the one to worry about generating valid YAML syntax, and you only need to worry about providing a suitable data structure to encode.

Hi @apparentlymart,

sorry could you elaborate a bit more, so currently i am creating a template in yaml format and decoding it to provide to the module, in addition being a yaml template file i am also providing the vars to it from a yaml file.

do you mean i should use yamlencode to create the vars file for the templatefile function or get rid of the templatefile function altogether?

the reason i am using the templatefile function is so that i can deploy the same config multiple times for different environments with minimal config change.

thanks

faisal

The main point of that advice is to use yamlencode whenever you need to generate YAML, and to never do that using string concatenation in templates.

Beyond that, it doesn’t really matter whether you choose to call yamlencode from inside your template file or if you just remove the template and put the yamlencode call inside your main module code, but in this case where you are immediately decoding the YAML again anyway it would be redundant to do that if you weren’t using a template file.

So in this particular case, I would either write the relevant data structure directly into the .tf file without encoding it at all, or to call yamlencode from inside the template so that the template rendering result will just be the yamlencode result.