Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2020-07-23T15:07:59.000Z
Answers: 2020-07-23T15:07:59.000Z
Answers: Sébastien Baillet
Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2020-07-26T11:58:08.097Z
Answers: 2020-08-09T05:56:53.117Z
Answers: 2020-08-09T05:56:53.117Z
Answers: Joseph
Answers: <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>
  
Answers: CC BY-SA 4.0
Answers: 2020-07-23T13:06:36.667Z
Answers: 2020-07-23T13:06:36.667Z
Answers: Denouche
Body: <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>
  
Tags: <terraform>
Title: How to produce a map from the cartesian product of two lists to use with for_each on a resource with Terraform
UserDisplayName: Antoine
ThreadText: # 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
  }
  
  ```
  
  
  ---
  
  