Nested for loop help on list of list of data

Can anybody help me ; im struggling to apply the split manifests as its a list of kubectl_file_documents[*].manifests … I somehow need to use a nested loop but am strugging to figure it out.

Thanks in advance

## complete example...

data "http" "this" {
  # for each url load it into response_body
  for_each = toset(["https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloaks.k8s.keycloak.org-v1.yml",
                  "https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml"])
  url      = each.key
}

data "kubectl_file_documents" "this" {
  # handle multi resource yamls ( split into multiple objects )
  for_each = data.http.this
  content = each.value.response_body
}

resource "kubectl_manifest" "this" {
  # kubectl_manifest will only deploy one resource normally so if you have a file with multiple we need
  # to instead treat each resouce as a manifest and walk them one by 
  for_each = data.kubectl_file_documents.this[ "*" /* i know cannot use wild care here its just to show its a list clearly */ ].manifests 
  yaml_body          = each.value
}

This an example of the data data.kubectl_file_documents.this
Sorry its so long

x = [
  {
    "https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml" = {
      "content" = <<-EOT
      # Generated by Fabric8 CRDGenerator, manual edits might get overwritten!
      apiVersion: apiextensions.k8s.io/v1
      kind: CustomResourceDefinition
      metadata:
        name: keycloakrealmimports.k8s.keycloak.org
      EOT
      "documents" = tolist([
        <<-EOT
        # Generated by Fabric8 CRDGenerator, manual edits might get overwritten!
        apiVersion: apiextensions.k8s.io/v1
        kind: CustomResourceDefinition
        metadata:
          name: keycloakrealmimports.k8s.keycloak.org
        EOT,
      ])
      "id" = "c81d707e127bae22b058fd6193af1337a21014ab40a0d1583b9438c97d865ff7"
      "manifests" = tomap({
        "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/keycloakrealmimports.k8s.keycloak.org" = <<-EOT
        apiVersion: apiextensions.k8s.io/v1
        kind: CustomResourceDefinition
        metadata:
          name: keycloakrealmimports.k8s.keycloak.org
        
        EOT
      })
    }
    "https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/kubernetes.yml" = {
      "content" = <<-EOT
      ---
      apiVersion: v1
      kind: ServiceAccount
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRole
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: ClusterRole    
      ---    
      apiVersion: apps/v1
      kind: Deployment
      EOT
      "documents" = tolist([
        <<-EOT
        ---
        apiVersion: v1
        kind: ServiceAccount
      
        EOT,
        <<-EOT
        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRole
      
  
        EOT
      })
    }
  },
]

Hi @stecullum,

Firstly - Thank you so much for providing a complete example!

Based upon the data structure of kubectl_file_documents.this (Output from terraform console)

> type(data.kubectl_file_documents.this)
object({
    https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml: object({
        content: string,
        documents: list(string),
        id: string,
        manifests: map(string),
    }),
    https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloaks.k8s.keycloak.org-v1.yml: object({
        content: string,
        documents: list(string),
        id: string,
        manifests: map(string),
    }),
})

This should create a separate kubectl_manifest resource for each manifest in each file.

## proposed (complete) solution

data "http" "this" {
  # for each url load it into response_body
  for_each = toset(["https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloaks.k8s.keycloak.org-v1.yml",
                  "https://raw.githubusercontent.com/keycloak/keycloak-k8s-resources/24.0.5/kubernetes/keycloakrealmimports.k8s.keycloak.org-v1.yml"])
  url      = each.key
}

data "kubectl_file_documents" "this" {
  # handle multi resource yamls ( split into multiple objects )
  for_each = data.http.this
  content = each.value.response_body
}

# Create a kubectl_manifest for each manifest in each file
# Used a local to help with debugging, but the merge(...) could just
# be used in the for_each

locals {
  manifests = merge([
    for file, data in data.kubectl_file_documents.this : {
      for manifest in data.manifests : "${file}-${manifest}" => { #create a map with composite key from file and manifest
        content = manifest
        id = "${file}-${manifest}"
      }
    }
  ]...)
}

resource "kubectl_manifest" "this" {
# Create a resource for each manifest in each file
  for_each = local.manifests
  yaml_body          = each.value.content
}

I’ve not been able to test it all the way through to an apply, but the plan output looks sensible (if a bit verbose with all of the YAML content!)

Hope that Helps

Happy Terraforming

Thanks …that makes sense.
Appreciate the effort you took

This topic was automatically closed 62 days after the last reply. New replies are no longer allowed.