Search code examples
terraformterraform-provider-kubernetes

How to iterate through nested list of objects in Terraform


I'm creating TF module, which downloads specified yaml files and then uses the yamls as a source for data field in k8s configmaps. I have working solution to create multiple configmaps with one data key-value pair(yaml file), but need also add support for multiple key-value pairs (yaml files) per one configmap.

main.tf

data "http" "config-map" {
  for_each = var.cloud-configmap

  url = format("https://%s", each.value.url)

  request_headers = {
    Accept = "text/plain"
  }
}

resource "kubernetes_config_map" "configmap" {
  for_each = var.cloud-configmap

  metadata {
    name = each.value.name
    namespace = each.value.namespace
  }

  data = {
    "${each.value.data-keyname}" = data.http.config-map[each.key].body
  }
}

variables.tf

variable "cloud-configmap" {
  type = map(object({
    url = string
    name = string
    namespace = string
    data-keyname = string
  }))
  default = {
    "cm1" = {
      url = "someurl.com/file1.yaml"
      name = "cm-name"
      namespace = "test"
      data-keyname = "file1.yml"
    },
    "cm2" = {
      url = "someurl.com/file2.yaml"
      name = "cm-name2"
      namespace = "default"
      data-keyname = "file2.yml"
    }
  }
}

This code till now is working, but I would like to change the variables.tf file to this:

variable "cloud-configmap" {
  type = map(object({
    name = string
    namespace = string
    cm-files = list(object({
      url = string
      data-keyname = string 
    }))
  }))
  default = {
    "cm1" = {
      name = "cm-name"
      namespace = "testnamespace"
      cm-files = [{
        url = "someurl.com/file1.yaml"
        data-keyname = "file1.yml"
      },
      {
        url = "someurl.com/file2.yaml"
        data-keyname = "file2.yml"
      }]
    },
    "cm2" = {
      name = "cm-name2"
      namespace = "default"
      cm-files = [{
        url = "someurl.com/file3.yaml"
        data-keyname = "file3.yml"
      },
      {
        url = "someurl.com/file4.yaml"
        data-keyname = "file4.yml"
      }]
    }
  }
}

And after this change, I don't know how to iterate through the nested list of objects cm-files to create configmap with multiple key-value pairs in data field. Any help or pointers would be much appreciated!


Solution

  • You'll be doing quite a bit with nested for loops in this one. Here's what I was able to come up with given your variable constraints.

    • Update your variables.tf to the desired state you have in your original question. This solution used that input as a requirement

    • Update your http resource to this. Note this will fail your initial terraform plan because these are dummy urls and Terraform attempts to make a request as it builds this resource. A good way to test how the urls look is the sample locals {} block I also have in this snippet. This locals block isn't needed, but illustrates how the URLs are created.

    data "http" "config-map" {
      for_each = toset(flatten([
        for cm in var.cloud-configmap : [
          for cm-file in cm.cm-files :
            cm-file.url
        ]
      ]))
    
      url = format("https://%s", each.key)
    
      request_headers = {
        Accept = "text/plain"
      }
    }
    
    # EXAMPLE showing how the URLs are created
    locals {
      urls = toset(flatten([
        for cm in var.cloud-configmap : [
          for cm-file in cm.cm-files :
            format("https://%s", cm-file.url)
        ]
      ]))
    }
    output "urls" { value = local.urls }
    
    • Update your kubernetes_config_map resource to this:
    resource "kubernetes_config_map" "configmap" {
      for_each = var.cloud-configmap
    
      metadata {
        name = each.value.name
        namespace = each.value.namespace
      }
    
      data = {
        for cm-file in each.value.cm-files :
          cm-file.url => cm-file.data-keyname
      }
    }
    

    Notice in each of these cases the use of for loops, and in the http case it's for_each being nested with for.