Unable to map the value when i have nested map variable

Two suggestions:

  • If you are going to post Terraform code here (especially complicated / long snippets), make sure to wrap it in a code block.
  • With these kinds of data structure problems, I like to make a really simple and self-contained terraform file with just the data structure and an output and / or a null resource. This will also let you experiment to see if starting with a slightly different data structure will help simplify things or not.

IMO, that can make things a lot easier / faster to play around with and try different things. This may not be exactly what you’re trying to do, but does this get closer or give you some ideas?

variable "ipam_scopes" {
  description = "Map of IPAM scopes, each with pools and CIDRs"
  type = map(object({
    description = string
    pools = map(object({
      description = string
      cidrs       = list(string)
      locale      = string
    }))
  }))
  default = {
    scope1 = {
      description = "Scope 1"
      pools = {
        test1 = {
          description = "Pool 1 in Scope 1"
          cidrs = [
            "192.168.0.0/24",
            "192.168.1.0/24"
          ]
          locale = "us-east-2"
        }
        test2 = {
          description = "Pool 2 in Scope 1"
          cidrs = [
            "10.0.0.0/24",
            "10.0.1.0/24"
          ]
          locale = "us-east-2"
        }
      }
    }
    scope2 = {
      description = "Scope 2"
      pools = {
        pool1 = {
          description = "Pool 1 in Scope 2"
          cidrs = [
            "192.168.0.0/24",
            "192.168.1.0/24"
          ]
          locale = "us-east-2"
        }
      }
    }
  }
}

output "test" {
  value = merge([
    for scope_name, scope_data in var.ipam_scopes : {
      for pool_name, pool_data in scope_data.pools :
      "${scope_name}_${pool_name}" => {
        scope_name  = scope_name
        pool_name   = pool_name
        description = pool_data.description
        cidrs       = pool_data.cidrs
        locale      = pool_data.locale
      }
    }
  # That spread operator (`...` ) is needed here if you're using `merge()`
  ]...)
}

If you put this as test.tf in an empty directory, and run terraform apply, you should get something like this:

Outputs:

test = {
  "scope1_test1" = {
    "cidrs" = tolist([
      "192.168.0.0/24",
      "192.168.1.0/24",
    ])
    "description" = "Pool 1 in Scope 1"
    "locale" = "us-east-2"
    "pool_name" = "test1"
    "scope_name" = "scope1"
  }
  "scope1_test2" = {
    "cidrs" = tolist([
      "10.0.0.0/24",
      "10.0.1.0/24",
    ])
    "description" = "Pool 2 in Scope 1"
    "locale" = "us-east-2"
    "pool_name" = "test2"
    "scope_name" = "scope1"
  }
  "scope2_pool1" = {
    "cidrs" = tolist([
      "192.168.0.0/24",
      "192.168.1.0/24",
    ])
    "description" = "Pool 1 in Scope 2"
    "locale" = "us-east-2"
    "pool_name" = "pool1"
    "scope_name" = "scope2"
  }
}

For extra credit, build in validation that the the CIDR ranges within the object are valid :crazy_face: