# Loops in templates with yamlencode

**URL:** <https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684>\
**Category:** Terraform\
**Created:** [September 2, 2020, 1:30pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684 "2020-09-02T13:30:10Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 2, 2020, 1:30pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/1 "2020-09-02T13:30:10Z")

</div>

Hi @apparentlymart .

I have the original definition using a TF provider for launching K8s clusters

```auto

        infrastructure_config {
          aws {
            networks {
                vpc {
                  cidr = var.vnetcidr
                }
                dynamic "zones" {
                  for_each = var.subnets
                  content {
                    name = zones.value.name
                    workers = zones.value.workers
                    public = zones.value.public
                    internal = zones.value.internal
                  }
                }
            }
          }
        }

```

Which works well. However i trying to use a plain k8s yaml manfiest using the following provider

[github.com/gavinbunney/kubectl](http://github.com/gavinbunney/kubectl)

```auto
terraform {
  required_version = ">= 0.13.0"
  required_providers {
    kubectl = {
      source = "github.com/gavinbunney/kubectl"
      version = "1.5.1"
    }
    kubernetes = "~> 1.11.4"
  }
}

provider "kubectl" {
  config_path = "/path/k8s.kubeconfig"
  load_config_file = true
}

resource "kubectl_manifest" "gardener_shoot" {
   yaml_body = templatefile("${path.module}/templates/gardener-shoot.yaml.tmpl", {
          shoot_cluster_name = var.shoot_cluster_name,
          project_name = var.project_name,
        })

```

The template has the following for now.

```auto
${yamlencode({
            "infrastructureConfig": {
                "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",
                "kind": "InfrastructureConfig",
                "networks": {
                    "vpc": {
                        "cidr": vnetcidr,
                    },
                    "zones": [
                        {
                            "internal": "10.250.48.0/20",
                            "name": "eu-central-1a",
                            "public": "10.250.32.0/20",
                            "workers": "10.250.0.0/19"
                        }
                    ]
                }
            },
})}

```

How would i use a loop in the yamlencode tempalte if i have the following variables as input ?

```auto
variable "subnets" {
    type = list
    description = "Subnets"
    default = [
    {
      name = "eu-central-1a"
      workers = "10.250.0.0/26"
      public = "10.250.96.0/26"
      internal = "10.250.112.0/26"
    },
    {
      name = "eu-central-1b"
      workers = "10.250.0.64/26"
      public = "10.250.96.64/26"
      internal = "10.250.112.64/26"
    },
    {
      name = "eu-central-1c"
      workers = "10.250.0.128/26"
      public = "10.250.96.128/26"
      internal = "10.250.112.128/26"
    }
    ]
}

variable "zones" {
    type = list
    description = "Zones"
    default = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
}

```

Kevin

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [September 2, 2020, 5:56pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/2 "2020-09-02T17:56:14Z")

</div>

Assuming you’ve passed that list of subnets in the second argument to the `templatefile` function as `subnets`, I don’t think you really need any sort of repetition construct here because the structure of that variable already seems to be the right shape for the output you want:

```auto
${yamlencode({
            "infrastructureConfig": {
                "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",
                "kind": "InfrastructureConfig",
                "networks": {
                    "vpc": {
                        "cidr": vnetcidr,
                    },
                    "zones": subnets
                }
            },
})}

```

However, I’ll also show a version with a [`for` expression](https://www.terraform.io/docs/configuration/expressions.html#for-expressions), even though it’s redundant in this case, because this pattern will probably be useful to you in other situations where the shape of your desired YAML data structure doesn’t exactly match the shape of the input data:

```auto
${yamlencode({
            "infrastructureConfig": {
                "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",
                "kind": "InfrastructureConfig",
                "networks": {
                    "vpc": {
                        "cidr": vnetcidr,
                    },
                    "zones": [
                        for subnet in subnets : {
                            internal = subnet.internal
                            name = subnet.name
                            public = subnet.public
                            workers = subnet.workers
                        }
                    ]
                }
            },
})}

```

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 3, 2020, 12:24pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/3 "2020-09-03T12:24:01Z")

</div>

Hi @apparentlymart,

The 2nd snippet was what i needed. It worked. I have only one question. Is there a way to do something like a if condition in a template file ? I would like to selectively load a yaml definition when some variables are set to true ?

For eg

```auto
        if dns_enabled = "true"
        "dns": {
            "domain": "test.ondemand.com",
            "providers": [
                {
                    "domains": {
                        "include": [
                            "abc.domain1.com",
                            "xyz.domain1.com"
                        ]
                    },
                    "secretName": "secret_name",
                    "type": "aws-route53"
                }
            ]
        },
       if extensions_enabled = "true"
        "extensions": [
            {
                "type": "shoot-dns-service"
            },
            {
                "providerConfig": {
                    "apiVersion": "service.cert.extensions.test.cloud/v1alpha1",
                    "issuers": [
                        {
                            "email": "test@domain.com",
                            "name": "domain1.com",
                            "server": "https://acme-v02.api.letsencrypt.org/directory"
                        }
                    ]
                },
                "type": "shoot-cert-service"
            }
        ],
        */

```

Also how would i make the variables optional ? I only want them to be used when the dns\_enabled or extensions\_enabled is set to true. The remainder of the yaml stays the same.

Kevin

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [September 3, 2020, 4:50pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/4 "2020-09-03T16:50:36Z")

</div>

Hi @linuxbsdfreak,

I think the least complex answer to that would be to use [conditional expressions](https://www.terraform.io/docs/configuration/expressions.html#conditional-expressions) to set a different value for each of those attributes depending on the condition, although that approach doesn’t _exactly_ meet the requirements you stated because those attributes would still both be present, just potentially set to `null` when not needed:

```auto
  "dns": dns_enabled ? {
    "domain": "test.ondemand.com",
    # etc
  } : null,
  "extensions": extensions_enabled ? [
    {
      "type": "shoot-dns-service",
    },
    # etc
  ] : null,

```

Whether this solution will be acceptable will depend on whether the system you’re sending this YAML to will tolerate those attributes being present but being set to null, like this:

```auto
dns: null
extensions: null

```

If omitting them entirely is the requirement then unfortunately the problem gets a little harder because the Terraform language doesn’t have a built-in operator to conditionally omit an attribute entirely. It’s possible to do it with some clever use of [`merge`](https://www.terraform.io/docs/configuration/functions/merge.html) to combine together multiple objects with different properties set, which I think might be the best answer for you here if you have this requirement, but I’d encourage you to try the approach above first and see if that works, and then consider `merge` to be a more complicated fallback if the remote system rejects those `null` values with an error.

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 3, 2020, 6:57pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/5 "2020-09-03T18:57:13Z")

</div>

Hi @apparentlymart,

Thanks for the reply. I would try that tomorrow. I think the 2nd option with merge would be necessary, since the system accepting the yaml is a K8s custom resource definition and the null has to be removed. Do you have any example with merge and conditionals to accomplish it?

Kevin

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [September 4, 2020, 11:20pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/6 "2020-09-04T23:20:30Z")

</div>

Hi @linuxbsdfreak,

I can’t write out a full example right now because it’s almost the end of my work day, but the general pattern would be like the following, which hopefully you can adapt to the specific structure you are working with:

```auto
${yamlencode(merge(
  {
    # put the attributes that will be unconditional
    # in this object.
  },
  dns_enabled ? {
    "dns": {
      "domain": "test.ondemand.com",
    }
  } : {},
  extensions_enabled ? {
    "extensions": {
      "type": "shoot-dns-service",
    }
  } : {},
))}

```

The idea here is that each of the subsequent arguments to `merge` (after the first unconditional one) use a conditional expression to choose between either an object with one additional attribute to merge in, or an object that has no attributes at all and will thus cause `merge` to make no changes because there is nothing additional to merge in.

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 7, 2020, 11:13am UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/7 "2020-09-07T11:13:36Z")

</div>

Hi @apparentlymart,

Thanks for the reply. I am have the following template defintion

```auto
${yamlencode(merge(
    "apiVersion": "core.gardener.cloud/v1beta1",
    "kind": "Shoot",
    "metadata": {
        "name": shoot_cluster_name,
        "namespace": project_name,
    },
    "timeouts": {
        "create": create_timeout,
        "update": update_timeout,
        "delete": delete_timeout,
    }
    "spec": {
        "addons": {
            "kubernetesDashboard": {
                "authenticationMode": "token",
                "enabled": dashboard_enabled,
            },
            "nginxIngress": {
                "enabled": nginx_enabled,
                "externalTrafficPolicy": "Cluster",
            }
        },
        "cloudProfileName": target_profile,
        "hibernation": {
            "enabled": false,
        },
        "kubernetes": {
            "allowPrivilegedContainers": true,
            "version": kubernetes_version,
        },
        "maintenance": {
            "autoUpdate": {
                "kubernetesVersion": maintenance_k8s_version_enabled,
                "machineImageVersion": maintenance_machine_image_version_enabled,
            },
            "timeWindow": {
                "begin": "210000+0000",
                "end": "220000+0000",
            }
        },
        "networking": {
            "nodes": networking_nodes,
            "pods": networking_pods,
            "services": networking_services,
            "type": networking_type,
        },
        "provider": {
            "infrastructureConfig": {
                "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",
                "kind": "InfrastructureConfig",
                "networks": {
                    "vpc": {
                        "cidr": vnetcidr,
                    },
                    "zones": [
                        for subnet in subnets : {
                            internal = subnet.internal
                            name = subnet.name
                            public = subnet.public
                            workers = subnet.workers
                        }
                    ]
                }
            },
            "type": cloud_provider,
            "workers": [
                {
                    "machine": {
                        "image":{
                            "name": machine_image_name,
                            "version": machine_image_version,
                        },
                        "type": machine_type,
                    },
                    "maxSurge": worker_max_surge,
                    "maxUnavailable": worker_max_unavailable,
                    "maximum": worker_maximum,
                    "minimum": worker_minimum,
                    "name": worker_name,
                    "volume": {
                        "size": disk_size,
                        "type": disk_type,
                    },
                    "zones": zones,
                }
            ]
        },
        "purpose": "evaluation",
        "region": location,
        "secretBindingName": target_secret,
    },
    gardener_dns_management? {
    "spec": {
        "dns": {
            "domain": "testcluster.ondemand.com",
            "providers": [
                {
                    "domains": {
                        "include": [
                            "domain1.ondemand.com",
                            "domain12.ondemand.com",
                        ]
                    },
                    "secretName": "mysecret",
                    "type": "aws-route53",
                }
            ]
        },
        "extensions": [
            {
                "type": "shoot-dns-service",
            }
      ]
     },
    } : {},
    gardener_dns_ssl_management ? {
    "spec": {
      "dns": {
            "domain": "testcluster.ondemand.com",
            "providers": [
                {
                    "domains": {
                        "include": [
                            "domain1.ondemand.com",
                            "domain12.ondemand.com",
                        ],
                    },
                    "secretName": "my-secret",
                    "type": "aws-route53",
                }
            ]
      },
      "extensions": [
          {
                "type": "shoot-dns-service",
          },
          {
                "providerConfig": {
                    "apiVersion": "service.cert.extensions.gardener.cloud/v1alpha1",
                    "issuers": [
                        {
                            "email": "test@example.com",
                            "name": "ondemand.com",
                            "server": "https://acme-v02.api.letsencrypt.org/directory",
                        }
                    ],
                },
                "type": "shoot-cert-service",
          }
       ]
     },
   } : {},
))}

```

I added the following in the variables

variables.tf

```auto
variable "gardener_dns_management" {
    type = bool
    default = true
}

variable "gardener_dns_ssl_management" {
    type = bool
    default = false
}

```

In main.tf

```auto
resource "kubectl_manifest" "gardener_shoot" {
   yaml_body = templatefile("${path.module}/templates/gardener-shoot.yaml_wip.tmpl", {
          shoot_cluster_name = var.shoot_cluster_name,
          project_name = var.project_name,
          create_timeout = var.create_timeout,
          update_timeout = var.update_timeout,
          delete_timeout = var.delete_timeout,
          dashboard_enabled = var.dashboard_enabled,
          nginx_enabled = var.nginx_enabled,
          target_profile = var.target_profile,
          kubernetes_version = var.kubernetes_version,
          maintenance_k8s_version_enabled = var.maintenance_k8s_version_enabled,
          maintenance_machine_image_version_enabled = var.maintenance_machine_image_version_enabled,
          networking_nodes = var.networking_nodes,
          networking_pods = var.networking_pods,
          networking_services = var.networking_services,
          networking_type = var.networking_type,
          vnetcidr = var.vnetcidr,
          cloud_provider = var.cloud_provider,
          machine_image_name = var.machine_image_name,
          machine_image_version = var.machine_image_version,
          machine_type = var.machine_type,
          worker_max_surge = var.worker_max_surge,
          worker_max_unavailable = var.worker_max_unavailable,
          worker_maximum = var.worker_maximum,
          worker_minimum = var.worker_minimum,
          worker_name = var.worker_name,
          disk_size = var.disk_size,
          disk_type = var.disk_type,
          location = var.location,
          target_secret = var.target_secret,
          zones = var.zones,
          subnets = var.subnets,
          gardener_dns_management = var.gardener_dns_management,
          gardener_dns_ssl_management = var.gardener_dns_ssl_management,
        })
}

```

I get an error with merge

```auto
 on main.tf line 18, in resource "kubectl_manifest" "gardener_shoot":
  18: yaml_body = templatefile("${path.module}/templates/gardener-shoot.yaml_wip.tmpl", {
  19: shoot_cluster_name = var.shoot_cluster_name,
  20: project_name = var.project_name,
  21: create_timeout = var.create_timeout,
  22: update_timeout = var.update_timeout,
  23: delete_timeout = var.delete_timeout,
  24: dashboard_enabled = var.dashboard_enabled,
  25: nginx_enabled = var.nginx_enabled,
  26: target_profile = var.target_profile,
  27: kubernetes_version = var.kubernetes_version,
  28: maintenance_k8s_version_enabled = var.maintenance_k8s_version_enabled,
  29: maintenance_machine_image_version_enabled = var.maintenance_machine_image_version_enabled,
  30: networking_nodes = var.networking_nodes,
  31: networking_pods = var.networking_pods,
  32: networking_services = var.networking_services,
  33: networking_type = var.networking_type,
  34: vnetcidr = var.vnetcidr,
  35: cloud_provider = var.cloud_provider,
  36: machine_image_name = var.machine_image_name,
  37: machine_image_version = var.machine_image_version,
  38: machine_type = var.machine_type,
  39: worker_max_surge = var.worker_max_surge,
  40: worker_max_unavailable = var.worker_max_unavailable,
  41: worker_maximum = var.worker_maximum,
  42: worker_minimum = var.worker_minimum,
  43: worker_name = var.worker_name,
  44: disk_size = var.disk_size,
  45: disk_type = var.disk_type,
  46: location = var.location,
  47: target_secret = var.target_secret,
  48: zones = var.zones,
  49: subnets = var.subnets,
  50: gardener_dns_management = var.gardener_dns_management,
  51: gardener_dns_ssl_management = var.gardener_dns_ssl_management,
  52: })
    |----------------
    | path.module is "."

Call to function "templatefile" failed:
./templates/gardener-shoot.yaml_wip.tmpl:2,17-18: Missing argument separator;
A comma is required to separate each function argument from the next..

```

As you see i would like to add the relevant snippet under the `spec` section of the yaml if the boolean value is set.

What am i doing incorrect ?

Kevin

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 8, 2020, 5:52pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/8 "2020-09-08T17:52:12Z")

</div>

Hi @apparentlymart,

I tried to debug the template along with the merge information online. I had no success.

Kevin

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [September 8, 2020, 5:56pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/9 "2020-09-08T17:56:26Z")

</div>

It looks like you are missing the braces around the first argument to `merge`.

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 8, 2020, 7:23pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/10 "2020-09-08T19:23:50Z")

</div>

Hi @apparentlymart,

Hmmm ok so ${yamlencode(merge( … ))} is incorrect. What kind of brackets have i missed? Could you please provide me a Sample snippet to understand ?

Kevin

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 9, 2020, 7:22am UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/11 "2020-09-09T07:22:37Z")

</div>

Hi @apparentlymart,

Thanks for the hint. I went a little ahead but not satisfied with the merge . It is not doing as you had mentioned. TF is trying to remove the attributes that would be unconditional in the object. For eg

```auto
${yamlencode(merge({
    "apiVersion": "core.gardener.cloud/v1beta1",
    "kind": "Shoot",
    "metadata": {
        "name": shoot_cluster_name,
        "namespace": project_name,
    },
    "timeouts": {
        "create": create_timeout,
        "update": update_timeout,
        "delete": delete_timeout,
    }
    "spec": {
        "addons": {
            "kubernetesDashboard": {
                "authenticationMode": "token",
                "enabled": dashboard_enabled,
            },
            "nginxIngress": {
                "enabled": nginx_enabled,
                "externalTrafficPolicy": "Cluster",
            }
        },
        "cloudProfileName": target_profile,
        "hibernation": {
            "enabled": false,
        },
        "kubernetes": {
            "allowPrivilegedContainers": true,
            "version": kubernetes_version,
        },
        "maintenance": {
            "autoUpdate": {
                "kubernetesVersion": maintenance_k8s_version_enabled,
                "machineImageVersion": maintenance_machine_image_version_enabled,
            },
            "timeWindow": {
                "begin": "210000+0000",
                "end": "220000+0000",
            }
        },
        "networking": {
            "nodes": networking_nodes,
            "pods": networking_pods,
            "services": networking_services,
            "type": networking_type,
        },
        "provider": {
            "infrastructureConfig": {
                "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",
                "kind": "InfrastructureConfig",
                "networks": {
                    "vpc": {
                        "cidr": vnetcidr,
                    },
                    "zones": [
                        for subnet in subnets : {
                            internal = subnet.internal
                            name = subnet.name
                            public = subnet.public
                            workers = subnet.workers
                        }
                    ]
                }
            },
            "type": cloud_provider,
            "workers": [
                {
                    "machine": {
                        "image":{
                            "name": machine_image_name,
                            "version": machine_image_version,
                        },
                        "type": machine_type,
                    },
                    "maxSurge": worker_max_surge,
                    "maxUnavailable": worker_max_unavailable,
                    "maximum": worker_maximum,
                    "minimum": worker_minimum,
                    "name": worker_name,
                    "volume": {
                        "size": disk_size,
                        "type": disk_type,
                    },
                    "zones": zones,
                }
            ]
        },
        "purpose": "evaluation",
        "region": location,
        "secretBindingName": target_secret,
    }},
    gardener_dns_management ? {
    "spec": {
        "dns": {
            "domain" : "${shoot_cluster_name}.${element(split("-",project_name),1)}.${gardenerdomain}",
            "providers": [
                {
                    "domains": {
                        "include": included_dns_domains,
                    },
                    "secretName": dns_secret_name,
                    "type": dns_type,
                }
            ]
        },
        "extensions": [
            {
                "type": "shoot-dns-service",
            }
      ]
     },
    } : {},
    gardener_dns_ssl_management ? {
    "spec": {
      "dns": {
            "domain" : "${shoot_cluster_name}.${element(split("-",project_name),1)}.${gardenerdomain}",
            "providers": [
                {
                    "domains": {
                        "include": included_dns_domains,
                    },
                    "secretName": dns_secret_name,
                    "type": dns_type,
                }
            ]
      },
      "extensions": [
          {
                "type": "shoot-dns-service",
          },
          {
                "providerConfig": {
                    "apiVersion": "service.cert.extensions.gardener.cloud/v1alpha1",
                    "issuers": [
                        {
                            "email": email_id,
                            "name": subdomain,
                            "server": "https://acme-v02.api.letsencrypt.org/directory",
                        }
                    ],
                },
                "type": "shoot-cert-service"
          }
       ]
     },
   } : {},
))}

```

The output is as follows

```auto
~ yaml_body = <<~EOT
            "apiVersion": "core.gardener.cloud/v1beta1"
            "kind": "Shoot"
            "metadata":
              "name": "testcluster"
              "namespace": "garden-abap"
            "spec":
          - "addons":
          - "kubernetesDashboard":
          - "authenticationMode": "token"
          - "enabled": true
          - "nginxIngress":
          - "enabled": true
          - "externalTrafficPolicy": "Cluster"
          - "cloudProfileName": "aws"
          - "hibernation":
          - "enabled": false
          - "kubernetes":
          - "allowPrivilegedContainers": true
          - "version": "1.18.8"
          - "maintenance":
          - "autoUpdate":
          - "kubernetesVersion": true
          - "machineImageVersion": true
          - "timeWindow":
          - "begin": "210000+0000"
          - "end": "220000+0000"
          - "networking":
          - "nodes": "10.250.0.0/16"
          - "pods": "100.96.0.0/11"
          - "services": "100.64.0.0/13"
          - "type": "calico"
          - "provider":
          - "infrastructureConfig":
          - "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1"
          - "kind": "InfrastructureConfig"
          - "networks":
          - "vpc":
          - "cidr": "10.250.0.0/16"
          - "zones":
          - - "internal": "10.250.112.0/26"
          - "name": "eu-central-1a"
          - "public": "10.250.96.0/26"
          - "workers": "10.250.0.0/26"
          - - "internal": "10.250.112.64/26"
          - "name": "eu-central-1b"
          - "public": "10.250.96.64/26"
          - "workers": "10.250.0.64/26"
          - - "internal": "10.250.112.128/26"
          - "name": "eu-central-1c"
          - "public": "10.250.96.128/26"
          - "workers": "10.250.0.128/26"
          - "type": "aws"
          - "workers":
          - - "machine":
          - "image":
          - "name": "linux"
          - "version": "27.1.0"
          - "type": "m5.large"
          - "maxSurge": 2
          - "maxUnavailable": 2
          - "maximum": 2
          - "minimum": 1
          - "name": "workertest"
          - "volume":
          - "size": "30Gi"
          - "type": "gp2"
          - "zones":
          - - "eu-central-1a"
          - - "eu-central-1b"
          - - "eu-central-1c"
          - "purpose": "evaluation"
          - "region": "eu-central-1"
          - "secretBindingName": "mysecret"
          + "dns":
          + "domain": "mydomain"
          + "providers":
          + - "domains":
          + "include":
          + - "domain1.ondemand.com"
          + - "domain2.ondemand.com"
          + "secretName": "mysecret"
          + "type": "aws-route53"
          + "extensions":
          + - "type": "shoot-dns-service"
          + - "type": "shoot-cert-service"
          + - "providerConfig":
          + "apiVersion": "service.cert.extensions.gardener.cloud/v1alpha1"
          + "issuers":
          + - "email": "test@ondeman.com"
          + "name": "subdomain.com"
          + "server": "https://acme-v02.api.letsencrypt.org/directory"
            "timeouts":
              "create": "30m0s"
              "delete": "20m0s"
              "update": "30m0s"

```

Curious what i am missing with the merge? I expected that it would merge the common part along with the conditional part when i set via the boolean variable.

Kevin

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [September 9, 2020, 4:54pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/12 "2020-09-09T16:54:55Z")

</div>

`merge` only works for one level of mapping at a time, so your conditional override of `spec` overrides the entire `spec` value.

If you want to conditionally add elements to the `spec` object then you’ll need to put this `merge` pattern as the expression for `spec` itself instead of for the top-level object. Then the presence of the `dns` and `extensions` nested attributes can be what is conditional, rather than the presence of the `spec` object as a whole.

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 9, 2020, 5:35pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/13 "2020-09-09T17:35:29Z")

</div>

Hi @apparentlymart,

Thanks for the feedback. Could you give me small sample snippet to solve my use case? I never worked with merge in depth in terraform . Thanks once again.

Kevin

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 10, 2020, 11:57am UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/14 "2020-09-10T11:57:34Z")

</div>

Hi @apparentlymart,

I tried as per your suggestions by moving the merge pattern under spec.

```auto
${yamlencode({
    "apiVersion": "core.gardener.cloud/v1beta1",
    "kind": "Shoot",
    "metadata": {
        "name": shoot_cluster_name,
        "namespace": project_name,
    },
    "timeouts": {
        "create": create_timeout,
        "update": update_timeout,
        "delete": delete_timeout,
    },
    merge({
    "spec": {
        "addons": {
            "kubernetesDashboard": {
                "authenticationMode": "token",
                "enabled": dashboard_enabled,
            },
            "nginxIngress": {
                "enabled": nginx_enabled,
                "externalTrafficPolicy": "Cluster",
            }
        },
        "cloudProfileName": target_profile,
        "hibernation": {
            "enabled": false,
        },
        "kubernetes": {
            "allowPrivilegedContainers": true,
            "version": kubernetes_version,
        },
        "maintenance": {
            "autoUpdate": {
                "kubernetesVersion": maintenance_k8s_version_enabled,
                "machineImageVersion": maintenance_machine_image_version_enabled,
            },
            "timeWindow": {
                "begin": "210000+0000",
                "end": "220000+0000",
            }
        },
        "networking": {
            "nodes": networking_nodes,
            "pods": networking_pods,
            "services": networking_services,
            "type": networking_type,
        },
        "provider": {
            "infrastructureConfig": {
                "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1",
                "kind": "InfrastructureConfig",
                "networks": {
                    "vpc": {
                        "cidr": vnetcidr,
                    },
                    "zones": [
                        for subnet in subnets : {
                            internal = subnet.internal
                            name = subnet.name
                            public = subnet.public
                            workers = subnet.workers
                        }
                    ]
                }
            },
            "type": cloud_provider,
            "workers": [
                {
                    "machine": {
                        "image":{
                            "name": machine_image_name,
                            "version": machine_image_version,
                        },
                        "type": machine_type,
                    },
                    "maxSurge": worker_max_surge,
                    "maxUnavailable": worker_max_unavailable,
                    "maximum": worker_maximum,
                    "minimum": worker_minimum,
                    "name": worker_name,
                    "volume": {
                        "size": disk_size,
                        "type": disk_type,
                    },
                    "zones": zones,
                }
            ]
        },
        "purpose": "evaluation",
        "region": location,
        "secretBindingName": target_secret,
    }
    gardener_dns_management ? {
    "spec": {
        "dns": {
            "domain" : "${shoot_cluster_name}.${element(split("-",project_name),1)}.${gardenerdomain}",
            "providers": [
                {
                    "domains": {
                        "include": included_dns_domains,
                    },
                    "secretName": dns_secret_name,
                    "type": dns_type,
                }
            ]
        },
        "extensions": [
            {
                "type": "shoot-dns-service",
            }
      ]
    },
    } : {}, ----> ERROR on this line. 
    gardener_dns_ssl_management ? {
    "spec": {
      "dns": {
            "domain" : "${shoot_cluster_name}.${element(split("-",project_name),1)}.${gardenerdomain}",
            "providers": [
                {
                    "domains": {
                        "include": included_dns_domains,
                    },
                    "secretName": dns_secret_name,
                    "type": dns_type,
                }
            ]
      },
      "extensions": [
          {
                "type": "shoot-dns-service",
          },
          {
                "providerConfig": {
                    "apiVersion": "service.cert.extensions.gardener.cloud/v1alpha1",
                    "issuers": [
                        {
                            "email": email_id,
                            "name": subdomain,
                            "server": "https://acme-v02.api.letsencrypt.org/directory",
                        }
                    ],
                },
                "type": "shoot-cert-service",
          }
       ]
    },
    } : {},
   })
})}

```

Error as follows

```auto
Call to function "templatefile" failed:
./templates/gardener-shoot.yaml_wip.tmpl:114,11-12: Missing attribute value;
Expected an attribute value, introduced by an equals sign ("=")..

```

I am not confident if i am doing it correct .

Kevin

---

<div class="post-metadata">

**Author:** ![apparentlymart](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/apparentlymart/32/702_2.png) [@apparentlymart](https://discuss.hashicorp.com/u/apparentlymart)\
**Post date:** [September 10, 2020, 5:52pm UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/15 "2020-09-10T17:52:50Z")

</div>

Hi @linuxbsdfreak,

It looks like you were almost there… just some things in a slightly different order. I find it hard to work with these long examples because it’s hard to replicate all of the settings you are including when I’m not familiar with your system, but here’s an abbreviated example that hopefully shows the relevant parts in a way you can incorporate into your larger template:

```auto
${yamlencode({
  "apiVersion": "core.gardener.cloud/v1beta1",
  "kind": "Shoot",
  # (and all of the other unconditional top-level attributes)
  "spec": merge(
    {
      "addons": { /* ... */ },
      "cloudProfileName": target_profile,
      # (and all of the other unconditional "spec" attributes)
    },
    gardener_dns_management || gardener_dns_ssl_management ? {
      "dns": {
        "domain" : "${shoot_cluster_name}.${element(split("-",project_name),1)}.${gardenerdomain}",
        # ...
      }
    } : {},
    gardener_dns_management ? {
      "extensions": [
        {
          "type": "shoot-dns-service",
        },
      ]
    } : {},
    gardener_dns_ssl_management ? {
      "extensions": [
        {
          "type": "shoot-dns-service"
        },
        {
          "type": "shoot-cert-service",
          # and "providerConfig"
        },
      ]
    } : {},
  ),
})}

```

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 11, 2020, 10:05am UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/16 "2020-09-11T10:05:04Z")

</div>

Hi @apparentlymart,

Thanks for the feedback. It works partially but the yaml data structure is incorrect in the terraform plan

```auto
${yamlencode({
  "apiVersion": "core.gardener.cloud/v1beta1",
  "kind": "Shoot",
  # (and all of the other unconditional top-level attributes)
  "spec": merge(
    {
      "addons": { /* ... */ },
      "cloudProfileName": target_profile,
      # (and all of the other unconditional "spec" attributes)
    },
    gardener_dns_management || gardener_dns_ssl_management ? {
      "dns": {
        "domain" : "${shoot_cluster_name}.${element(split("-",project_name),1)}.${gardenerdomain}",
        # ...
      }
    } : {},
    gardener_dns_management ? {
      "extensions": [
        {
          "type": "shoot-dns-service",
        },
      ]
    } : {},
    gardener_dns_ssl_management ? {
      "extensions": [
        {
          "type": "shoot-dns-service"
        },
        {
          "type": "shoot-cert-service",
          # and "providerConfig"
        },
      ]
    } : {},
  ),
})}

```

With the above snippet definition i get the following error

```auto
Call to function "templatefile" failed:
./templates/gardener-shoot.yaml_wip.tmpl:94,31-112,11: Inconsistent
conditional result types; The true and false result expressions must have
consistent types. The given expressions are object and object, respectively.,
and 1 other diagnostic(s).

```

For the TF plan to work i need to add the following under the  
gardener\_dns\_management and gardener\_dns\_ssl\_management conditional section

```auto
"spec": {

}

```

However the dns or extensions do not get appended under the original spec definitions  
Example output

```auto
      ~ yaml_body = <<~EOT
            "apiVersion": "core.gardener.cloud/v1beta1"
            "kind": "Shoot"
            "metadata":
              "name": "testcluster"
              "namespace": "test-garden"
            "spec":
              "addons":
                "kubernetesDashboard":
                  "authenticationMode": "token"
                  "enabled": true
                "nginxIngress":
                  "enabled": true
                  "externalTrafficPolicy": "Cluster"
              "cloudProfileName": "aws"
              "hibernation":
                "enabled": false
              "kubernetes":
                "allowPrivilegedContainers": true
                "version": "1.18.8"
              "maintenance":
                "autoUpdate":
                  "kubernetesVersion": true
                  "machineImageVersion": true
                "timeWindow":
                  "begin": "210000+0000"
                  "end": "220000+0000"
              "networking":
                "nodes": "10.250.0.0/16"
                "pods": "100.96.0.0/11"
                "services": "100.64.0.0/13"
                "type": "calico"
              "provider":
                "infrastructureConfig":
                  "apiVersion": "aws.provider.extensions.gardener.cloud/v1alpha1"
                  "kind": "InfrastructureConfig"
                  "networks":
                    "vpc":
                      "cidr": "10.250.0.0/16"
                    "zones":
                    - "internal": "10.250.112.0/26"
                      "name": "eu-central-1a"
                      "public": "10.250.96.0/26"
                      "workers": "10.250.0.0/26"
                    - "internal": "10.250.112.64/26"
                      "name": "eu-central-1b"
                      "public": "10.250.96.64/26"
                      "workers": "10.250.0.64/26"
                    - "internal": "10.250.112.128/26"
                      "name": "eu-central-1c"
                      "public": "10.250.96.128/26"
                      "workers": "10.250.0.128/26"
                "type": "aws"
                "workers":
                - "machine":
                    "image":
                      "name": "linux"
                      "version": "27.1.0"
                    "type": "m5.large"
                  "maxSurge": 2
                  "maxUnavailable": 2
                  "maximum": 2
                  "minimum": 1
                  "name": "workertest"
                  "volume":
                    "size": "30Gi"
                    "type": "gp2"
                  "zones":
                  - "eu-central-1a"
                  - "eu-central-1b"
                  - "eu-central-1c"
              "purpose": "evaluation"
              "region": "eu-central-1"
              "secretBindingName": "test-gardener"
          + "spec": --> NOT NEEDED
          + "dns": --> SHOULD be aligned direct under the secretBindingName
          + "domain": "testcluster.ondemand.com"
          + "providers":
          + - "domains":
          + "include":
          + - "domain1.k8s-testcluster-eu10.example.com"
          + - "domain1-support.k8s-testcluster-eu10.example.com"
          + "secretName": "mysecret"
          + "type": "aws-route53"
          + "extensions":
          + - "type": "shoot-dns-service"
            "timeouts":
              "create": "30m0s"
              "delete": "20m0s"
              "update": "30m0s"

```

I would like to have the **dns** direct under the **secretBindingName** key and without the **spec**.

Kevin

---

<div class="post-metadata">

**Author:** ![linuxbsdfreak](https://sea2.discourse-cdn.com/hashicorp/user_avatar/discuss.hashicorp.com/linuxbsdfreak/32/9430_2.png) [@linuxbsdfreak](https://discuss.hashicorp.com/u/linuxbsdfreak)\
**Post date:** [September 18, 2020, 11:06am UTC](https://discuss.hashicorp.com/t/loops-in-templates-with-yamlencode/13684/17 "2020-09-18T11:06:48Z")

</div>

Hi @apparentlymart,

Do you have any clue how i can fix that with merge ?

Kevin
