Using outputs from object maps in other modules

Evening,

I’ve been creating some modules for my company that essentially will create any number of the resource based on the nested map you provide. Example below:

rgp_config = {
  rgp_1 = {
    name           = "rgp"
    location       = "uksouth"
    location_short = "uks"
    project_name   = "example"
  }
  rgp_2 = {
    name           = "rgp"
    location       = "ukwest"
    location_short = "ukw"
    project_name   = "example"
  }
}

They work fine on their own and do what I need them to do. The problem I’m having is when I want to use the modules together I get issues with using the outputs from one module in another.

For example - I create two resource groups and then want to output the name of both to the AKS-Cluster module I’ve created. When I add the module output into the variable I want in the root main.tf I get the following error:

│ Error: Incorrect attribute value type
│ 
│   on .terraform/modules/create_k8s_clusters/Prepared-Modules/Completed-Modules/AKS-Cluster/main.tf line 6, in resource "azurerm_kubernetes_cluster" "aks":
│    6:   resource_group_name     = var.resource_group_name
│     ├────────────────
│     │ var.resource_group_name is object with 2 attributes
│ 
│ Inappropriate value for attribute "resource_group_name": string required.

I’m well aware it’s returning two attributes but I want to know how I can split these out without using a method like for_each because if I want to combine more modules then I will run into the same issue and won’t be able to add another for_each statement.

Here is the example of my main.tf (working):

module "create_resource_group" {
  source = "./modules/resource-group"

  rgp_config = var.rgp_config
}

module "create_k8s_clusters" {
  source   = "./modules/aks"
  for_each = module.create_resource_group.rgp_name

  resource_group_name = each.value
  aks_config          = var.aks_config
  default_tags        = var.default_tags

  depends_on = [module.create_resource_group]

}

Main.tf that produce the error above:

module "create_resource_group" {
  source = "./modules/resource-group"

  rgp_config = var.rgp_config
}

module "create_k8s_clusters" {
  source   = "./modules/aks"

  resource_group_name = module.create_resource_group.rgp_name
  aks_config          = var.aks_config
  default_tags        = var.default_tags

  depends_on = [module.create_resource_group]

}

This really depends on what you are trying to do, which depends on what you are wanting your module to do.

You have two options:

  1. Have a module that accepts multiple resource group names and then internally loops through that list to do whatever you are needing

  2. Have the module accept a single resource group name and then create multiple instances of that module.

For (1) you’d have for_each or count on the resources within the module, while for (2) you’d have the for_each or count on the module itself.

From your description it sounds like you are wanting option (2), but I’m not quite understanding why you are trying not to use for_each?

HI Stuart,

It’s not that I’m trying not use for_each but more I’m limiting the amount of outputs that can be used in a module if I have to use for_each for every output. If that makes sense?

I just want a way to output from another module that is using for_each, to the current module, for example AKS module above.

Sorry I don’t really understand. for_each is for making multiple versions of a single resource or module, so you’d need to setup the variable/local that you specify to reflect what is needed.

So when I use the output of the resource group, I need to use for each as it’s an object map and will just simple return the whole object. Then if I want to use another output from a different module, that is also using an object map, I can’t because I would need to use for_each again to loop over the map

Could you give a more concrete example of what you mean?

Are you talking about the case of creating multiple resources using for_each and then wanting to make a second set of resources/modules using some of the data from those resources (so create 10 of X and then create 10 of Y using the info from X)?

If you then create 10 of resource Z what are you thinking might be needed? Another 10 of Y using the info from Z?

Ok an example for you:

Say I have the following modules:

  • Create Resource Group
  • Create AKS Cluster
  • Create Network

Each of these modules take their inputs from a object map and then iterate over the map based on its contents, see below:

rgp_config = {
  rgp_1 = {
    name           = "rgp"
    location       = "uksouth"
    location_short = "uks"
    project_name   = "mattp"
  }
  rgp_2 = {
    name           = "rgp"
    location       = "ukwest"
    location_short = "ukw"
    project_name   = "mattp"
  }
}

So when the above is inputted into the Resource Group cluster, it will create two resource groups based on the map inputted.

The AKS cluster will need to take outputs from both the resource group and create network module. Currently if I just use the following to output the resource group names it will error because it will return the whole object, not the individual attributes.

module "create_resource_group" {
  source = "./modules/resource-group"

  rgp_config = var.rgp_config
}

module "create_k8s_clusters" {
  source   = "./modules/aks"

  resource_group_name = module.create_resource_group.rgp_name <---- HERE
  aks_config          = var.aks_config
  default_tags        = var.default_tags

  depends_on = [module.create_resource_group]

}

So to combat this I used a for_each at module level to pull the individual values out of the object and use them as inputs for the cluster creation.

What my problem now is when I need to then use network outputs, that also come from a object map similar to the create resource group module, I can’t use for_each now because I already have one for the resource groups and it will error the same way because it will return the whole object, not just the attributes.

Again if you’re still not clear, please let me know :slight_smile:

You need to be thinking overall what needs creating. So you listed three things - resource group, AKS cluster & network. You also listed a variable containing two entries.

So the thing to ask yourself is for those two entries in the variable what should the overall result be? Are you expecting 2 resource groups, 2 clusters & 2 networks?

If so, that’s pretty easy - each of the three resources/modules would have a for_each that references that rgp_config variable (or maybe a variant of it created via a for block). If you need to reference information from one resource in another you can easily do that using each.key, as you would be creating two copies of each resource called “rgp_1” and “rgp_2” (the keys in your rgp_config map).