<p>As in Terraform, a loop &quot;products&quot; a result (a map, a list, an object...), in this case, you have to avoid nested loop:</p>
<pre><code># Clusters
variable &quot;datadog_gke_clusters&quot; {
  type = list(object({
    name        = string
    environment = string
    url = object({
      private = string
      public  = string
    })
  }))
  default = [
    # NPRD
    {
      name        = &quot;cluster01&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.domain-priv.com&quot;
        public  = &quot;https://public.domain.com&quot;
      }
    },
    # PROD
    {
      name        = &quot;cluster02&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.domain-priv.com&quot;
        public  = &quot;https://public.domain.com&quot;
      }
    }
  ]
}

# Point of views
variable &quot;gke_slo&quot; {
  type = list(object({
    zone              = string
    monitor_threshold = number
  }))
  description = &quot;Response time threshold in ms to respond to a request.&quot;
  default = [
    {
      zone              = &quot;private&quot;
      monitor_threshold = 50
    },
    {
      zone              = &quot;public&quot;
      monitor_threshold = 100
    },
  ]
}

locals {
  result = {
    for tuple in setproduct(var.datadog_gke_clusters, var.gke_slo): &quot;${tuple[0].name}_${tuple[1].zone}&quot; =&gt; {
      name              = tuple[0].name
      environment       = tuple[0].environment
      url               = tuple[0].url
      zone              = tuple[1].zone
      monitor_threshold = tuple[1].monitor_threshold
    }
  }
}

output &quot;result&quot; {
  value = local.result
}
</code></pre>
<p>Here we use <code>setproduct</code> to produce a <code>list(list(tuple(cluster,pov))</code> and then we can iterate on the first list to produce the expected result.</p>

CC BY-SA 4.0
2020-07-23T15:07:59.000Z
2020-07-23T15:07:59.000Z
Sébastien Baillet
<p>Your loop is being nested.</p>
<p>Use this instead.</p>
<pre><code># Clusters
variable &quot;datadog_gke_clusters&quot; {
  type = list(object({
    name        = string
    environment = string
    url = object({
      private = string
      public  = string
    })
  }))
  default = [
    # NPRD
    {
      name        = &quot;cluster01&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.domain-priv.com&quot;
        public  = &quot;https://public.domain.com&quot;
      }
    },
    # PROD
    {
      name        = &quot;cluster02&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.domain-priv.com&quot;
        public  = &quot;https://public.domain.com&quot;
      }
    }
  ]
}

# Point of views
variable &quot;gke_slo&quot; {
  type = list(object({
    zone              = string
    monitor_threshold = number
  }))
  description = &quot;Response time threshold in ms to respond to a request.&quot;
  default = [
    {
      zone              = &quot;private&quot;
      monitor_threshold = 50
    },
    {
      zone              = &quot;public&quot;
      monitor_threshold = 100
    },
  ]
}

locals {
  result = {
    for tuple in setproduct(var.datadog_gke_clusters, var.gke_slo): &quot;${tuple[0].name}_${tuple[1].zone}&quot; =&gt; {
      name              = tuple[0].name
      environment       = tuple[0].environment
      url               = tuple[0].url
      zone              = tuple[1].zone
      monitor_threshold = tuple[1].monitor_threshold
    }
  }
}

output &quot;result&quot; {
  value = local.result
}
</code></pre>

CC BY-SA 4.0
2020-07-26T11:58:08.097Z
2020-08-09T05:56:53.117Z
2020-08-09T05:56:53.117Z
Joseph
<p>In 2 steps, you can first create your cartesian product in a variable:</p>
<pre><code>locals {
  datadog_gke_clusters_by_pov = flatten([
    for cluster in var.datadog_gke_clusters : [
      for pov in var.gke_slo_ : {
        cluster    = cluster
        pov        = pov
      }
    ]
  ])
}
</code></pre>
<p>And then use this new variable in your resource block:</p>
<pre><code>resource &quot;datadog_synthetics_test&quot; &quot;gke-monitoring&quot; {
  for_each = {
    for valuue in local.datadog_gke_clusters_by_pov : &quot;${valuue.cluster.name}-${valuue.pov.zone_name}&quot; =&gt; valuue
  }
...
# here you can use each.value.cluster.* and each.value.pov.*
}
</code></pre>
<p>Have a good day!</p>

CC BY-SA 4.0
2020-07-23T13:06:36.667Z
2020-07-23T13:06:36.667Z
Denouche
<p>I need to create a datadog synthetics monitors with the resource <code>datadog_synthetics_test</code></p>
<p>I need to monitor multiples cluster, with multiple point of view :</p>
<pre><code># Clusters
variable &quot;datadog_gke_clusters&quot; {
  default = [
    # NPRD
    {
      name        = &quot;cluster01&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.domain-priv.com&quot;
        public  = &quot;https://public.domain.com&quot;
      }
    },
    # PROD
    {
      name        = &quot;cluster02&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.domain-priv.com&quot;
        public  = &quot;https://public.domain.com&quot;
      }
    }
  ]
}

# Point of views
variable &quot;gke_slo&quot; {
  description = &quot;Response time threshold in ms to respond to a request.&quot;
  default = [
    {
      zone              = private
      monitor_threshold = 50
    },
    {
      zone              = public
      monitor_threshold = 100
    },
  ]
}
</code></pre>
<p><code>for_each</code> need a map or a list of strings</p>
<pre><code>resource &quot;datadog_synthetics_test&quot; &quot;gke-monitoring&quot; {
  for_each = ... # cartesian product of lists

  type    = &quot;api&quot;
  subtype = &quot;http&quot;
  ....
}
</code></pre>
<p>There is a function <code>[setproduct(sets...)][1]</code> which produce a list which combine values of each list, but not a map.</p>
<p>I would need to produce a map which look like this :</p>
<pre><code>  {
    &quot;cluster01-private&quot; = {
      name        = &quot;cluster01&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.cluster01.domain-priv.com&quot;
        public  = &quot;https://public.cluster01.domain.com&quot;
      }
      zone              = private
      monitor_threshold = 50
    },
    &quot;cluster01-public&quot; = {
      name        = &quot;cluster01&quot;
      environment = &quot;nprd&quot;
      url = {
        private = &quot;https://private.cluster01.domain-priv.com&quot;
        public  = &quot;https://public.cluster01.domain.com&quot;
      }
      zone              = public
      monitor_threshold = 100
    },
    # PROD
    .... etc
  }
</code></pre>
<p>How could I do that ?</p>

<terraform>
How to produce a map from the cartesian product of two lists to use with for_each on a resource with Terraform
Antoine
# How to produce a map from the cartesian product of two lists to use with for_each on a resource with Terraform

(Asked by: username_0 on 2020-07-23)

I need to create a datadog synthetics monitors with the resource datadog_synthetics_test
I need to monitor multiples cluster, with multiple point of view :
# Clusters
variable "datadog_gke_clusters" {
  default = [
    # NPRD
    {
      name        = "cluster01"
      environment = "nprd"
      url = {
        private = "https://private.domain-priv.com"
        public  = "https://public.domain.com"
      }
    },
    # PROD
    {
      name        = "cluster02"
      environment = "nprd"
      url = {
        private = "https://private.domain-priv.com"
        public  = "https://public.domain.com"
      }
    }
  ]
}

# Point of views
variable "gke_slo" {
  description = "Response time threshold in ms to respond to a request."
  default = [
    {
      zone              = private
      monitor_threshold = 50
    },
    {
      zone              = public
      monitor_threshold = 100
    },
  ]
}

for_each need a map or a list of strings
resource "datadog_synthetics_test" "gke-monitoring" {
  for_each = ... # cartesian product of lists

  type    = "api"
  subtype = "http"
  ....
}

There is a function [setproduct(sets...)][1] which produce a list which combine values of each list, but not a map.
I would need to produce a map which look like this :
  {
    "cluster01-private" = {
      name        = "cluster01"
      environment = "nprd"
      url = {
        private = "https://private.cluster01.domain-priv.com"
        public  = "https://public.cluster01.domain.com"
      }
      zone              = private
      monitor_threshold = 50
    },
    "cluster01-public" = {
      name        = "cluster01"
      environment = "nprd"
      url = {
        private = "https://private.cluster01.domain-priv.com"
        public  = "https://public.cluster01.domain.com"
      }
      zone              = public
      monitor_threshold = 100
    },
    # PROD
    .... etc
  }

How could I do that ?

**Tags:** terraform


---

## 3 answers

### Answer by username_3 on 2020-07-23. Score: 3 | Accepted answer

In 2 steps, you can first create your cartesian product in a variable:
locals {
  datadog_gke_clusters_by_pov = flatten([
    for cluster in var.datadog_gke_clusters : [
      for pov in var.gke_slo_ : {
        cluster    = cluster
        pov        = pov
      }
    ]
  ])
}

And then use this new variable in your resource block:
resource "datadog_synthetics_test" "gke-monitoring" {
  for_each = {
    for valuue in local.datadog_gke_clusters_by_pov : "${valuue.cluster.name}-${valuue.pov.zone_name}" => valuue
  }
...
# here you can use each.value.cluster.* and each.value.pov.*
}

Have a good day!


---

### Answer by username_1 on 2020-07-23. Score: 2

As in Terraform, a loop "products" a result (a map, a list, an object...), in this case, you have to avoid nested loop:
# Clusters
variable "datadog_gke_clusters" {
  type = list(object({
    name        = string
    environment = string
    url = object({
      private = string
      public  = string
    })
  }))
  default = [
    # NPRD
    {
      name        = "cluster01"
      environment = "nprd"
      url = {
        private = "https://private.domain-priv.com"
        public  = "https://public.domain.com"
      }
    },
    # PROD
    {
      name        = "cluster02"
      environment = "nprd"
      url = {
        private = "https://private.domain-priv.com"
        public  = "https://public.domain.com"
      }
    }
  ]
}

# Point of views
variable "gke_slo" {
  type = list(object({
    zone              = string
    monitor_threshold = number
  }))
  description = "Response time threshold in ms to respond to a request."
  default = [
    {
      zone              = "private"
      monitor_threshold = 50
    },
    {
      zone              = "public"
      monitor_threshold = 100
    },
  ]
}

locals {
  result = {
    for tuple in setproduct(var.datadog_gke_clusters, var.gke_slo): "${tuple[0].name}_${tuple[1].zone}" => {
      name              = tuple[0].name
      environment       = tuple[0].environment
      url               = tuple[0].url
      zone              = tuple[1].zone
      monitor_threshold = tuple[1].monitor_threshold
    }
  }
}

output "result" {
  value = local.result
}

Here we use setproduct to produce a list(list(tuple(cluster,pov)) and then we can iterate on the first list to produce the expected result.


---

### Answer by username_2 on 2020-07-26. Score: 0

Your loop is being nested.
Use this instead.
# Clusters
variable "datadog_gke_clusters" {
  type = list(object({
    name        = string
    environment = string
    url = object({
      private = string
      public  = string
    })
  }))
  default = [
    # NPRD
    {
      name        = "cluster01"
      environment = "nprd"
      url = {
        private = "https://private.domain-priv.com"
        public  = "https://public.domain.com"
      }
    },
    # PROD
    {
      name        = "cluster02"
      environment = "nprd"
      url = {
        private = "https://private.domain-priv.com"
        public  = "https://public.domain.com"
      }
    }
  ]
}

# Point of views
variable "gke_slo" {
  type = list(object({
    zone              = string
    monitor_threshold = number
  }))
  description = "Response time threshold in ms to respond to a request."
  default = [
    {
      zone              = "private"
      monitor_threshold = 50
    },
    {
      zone              = "public"
      monitor_threshold = 100
    },
  ]
}

locals {
  result = {
    for tuple in setproduct(var.datadog_gke_clusters, var.gke_slo): "${tuple[0].name}_${tuple[1].zone}" => {
      name              = tuple[0].name
      environment       = tuple[0].environment
      url               = tuple[0].url
      zone              = tuple[1].zone
      monitor_threshold = tuple[1].monitor_threshold
    }
  }
}

output "result" {
  value = local.result
}


---

