TransWikia.com

How to produce a map from the cartesian product of two lists to use with for_each on a resource with Terraform

DevOps Asked on December 9, 2021

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 ?

3 Answers

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
}

Answered by Joseph on December 9, 2021

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.

Answered by Sébastien Baillet on December 9, 2021

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!

Answered by Denouche on December 9, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP