Grouping variables to pass into child modules

Hello,
I have a configuration that deploys VMware vCloud environment (3 db servers + 2 app cells). right now the configuration is written out in the long format:

  • variable definitions are declared at root and child
  • single .tfvars with all values passed in
  • within root module/main.tf => child is called all variables are passed as argument

This process works but looks very messy and repetitive code.
I am trying to make the root/main.tf look cleaner by using locals to prevent duplicate code within.

Question:
Are you able to do something like this to group variables and pass them into a child when calling a module?

[snipet - not full code]

# Locals to pass root variables to child modules
locals {
  vcda_universal_options_config = {
    vdatacenter_name                    = var.vdatacenter_name
    vmware_datastore_initial_deployment = var.vmware_datastore_initial_deployment
    vmware_compute_cluster_mgmtcl01     = var.vmware_compute_cluster_mgmtcl01
    vmware_esxi_mgmtcl01_esxi01         = var.vmware_esxi_mgmtcl01_esxi01
    vmware_ovf_fullpath_local           = var.vmware_ovf_fullpath_local
    vcda_ntp_server                     = var.vcda_ntp_server    
    vcda_domain_name                    = var.vcda_domain_name
    vcda_domain_searchpath              = var.vcda_domain_searchpath
    vcda_dns_servers                    = var.vcda_dns_servers
    vcda_nfs_mount_for_transfer         = var.vcda_nfs_mount_for_transfer
    vcda_installation_id                = var.vcda_installation_id
    vcda_unique_systemname              = var.vcda_unique_systemname
  }

  vcda_dbservers_options_config = {
    vcda_routes1         = var.vcda_dbservers_routes1
    vcda_default_gateway = var.vcda_dbservers_default_gateway    
  }
  
  vcda_db1_options_config = {
    vmware_vcda_vmname = var.vmware_vcda_db1_vmname
    vcda_eth0_ip       = var.vcda_db1_eth0_ip
    vcda_eth0_netmask  = var.vcda_db1_eth0_netmask
    vcda_eth1_ip       = var.vcda_db1_eth1_ip
    vcda_eth1_netmask  = var.vcda_db1_eth1_netmask
  }
}

# Declare modules
module "vcda_initial_db_master" {
   source = "./modules/vcda_initial_db_master"

   child_vars = [
     local.vcda_universal_options_config,
     local.vcda_dbservers_options_config,
     local.vcda_db1_options_config
   ]
}


module "vcda_initial_db_secondary" {
   source = "./modules/vcda_initial_db_secondary"
   
   depends_on = [
     module.timer_delay_for_db_master
   ]

   child_vars = [
     local.vcda_universal_options_config,
     local.vcda_dbservers_options_config,
     local.vcda_db2_options_config
   ]
}

So believe this is called “interpolation of objects”. (if I understand this correctly).

The short answer seems to be no.

There is an open feature request for some time now issue_353

Hi @tlb1galaxy,

Typically a child Terraform module should only need a subset of the data available in its caller, because the module would be focused on only one part of the problem. If you find that you need to pass the full set of variables from parent into child then that might suggest that the scope of your module is large, and it might be better to split it into smaller modules.

With that said, if you have a selection of settings that are thematically related, such as the DNS-related settings in your example, a typical design is to define some small object types representing those settings and then make all of the modules which interact with that component expect the settings in compatible object types, and then you can pass all of those values together rather than writing them separately each time.

For example, inside any module which needs the DNS-related settings:

variable "dns" {
  type = object({
    domain_name        = string
    domain_search_path = string
    dns_servers        = set(string)
  })
}

Then in the root module that calls the others:

locals {
  dns_settings = {
    domain_name       = var.vcda_domain_name
    domain_searchpath = var.vcda_domain_searchpath
    dns_servers       = var.vcda_dns_servers
  }
}

module "example" {
  source = "./modules/example"

  dns = local.dns_settings
}

I don’t know if the DNS settings are a good example of a reusable concept in your case, and I just used that to show a realistic example here. There’s more information on passing data between modules and resources in the documentation guide Module Composition.

Thanks for the explanation.
I originally thought about the scope of the modules. The reason I kept it as a larger module with multiple children, is really to do with the application itself. Deploying an entire vCloud solution makes more sense (logically) to keep them together.
But I get your point and from purely a code perspective, I would have broken this module apart (if vCloud liked having cells easily replaced/deployed without full knowledge of each other).

Have been busy with another project but have some time now to use this concept to make the code more efficient.