Terraform modules: creating multiple resources with modules and reference each other

7.5k Views Asked by At

I have defined a variable tenants which is a map:

variable tenants {
  type        = map
  default     = {
    tenant1 = {
      name      = "Tenant1"
    },
    tenant2 = {
      name    = "Tenant2"
    }
  }
}

In the root file, I call a module tenant as follows:

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

  for_each     =    var.tenants
  tenant = each.value.name
}

The child module tenant looks as follows:

resource "mso_tenant" "tenant" {
  name            = var.tenant
}

And I have defined an output in the child module with:

output "mso_tenant" {
  value = mso_tenant.tenant

In the output file under root, I do the following to print all tenants:

output "tenant_names" {
    value       = { for p in sort(keys(var.tenants)) : p => module.tenant[p].mso_tenant.name }
}

So far so good, all the above works.

Now I have another variable called schemas, also a map. A tenant can have multiple schema's. So I have defined the following

variable schemas {
  type        = map
  default     = {
    schema1 = {
      name          = "Schema1",
      template_name = "Template1",
      tenant = <refer to tenant1>      <==== refer to tenant module
    },
    schema2 = {
      name          = "Schema2"
      template_name = "Template2",
      tenant = <refer to tenant2>      <==== refer to tenant module
    },
    schema3 = {
      name          = "Schema3"
      template_name = "Template3",
      tenant = <refer to tenant1>      <==== refer to tenant module
    },
  }
}

In the main.tf file under root I want to do the following:

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

  for_each     =    var.schemas
  name            = each.value.name
  template_name   = each.value.template_name
  tenant_id       = each.value.tenant
}

How could I reference the respective tenants in either the schema variable or else directly in the schema module?

Update:

Tried solution 1:

In variables file, I passed the tenant as follows:

    schema1 = {
      name          = "Schema1",
      template_name = "Template1",
      tenant        = module.tenant["tenant1"].mso_tenant
    }

Error: Variables not allowed

Tried solution 2:

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

  for_each     =    var.tenants
  tenant       =    each.value.name
}

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

  for_each     =    var.schemas
  name            = each.value.name
  template_name   = each.value.template_name
  tenant_id       = module.tenant[each.value.tenant].mso_tenant
}

Resulting in following error:

  on main.tf line 30, in module "schema":
  30:   tenant_id       = module.tenant[each.value.tenant].mso_tenant
    |----------------
    | each.value is object with 2 attributes

This object does not have an attribute named "tenant".
1

There are 1 best solutions below

7
On BEST ANSWER

If you want to refer to the tenant resources of tenant1 you can use module.tenant["tenant1"].mso_tenant and assign this directly to the schema or in the input variables of the second module.

As you are using module for_each module.tenant is a map of objects keyed by the tenant_id. Each object consists of all the outputs of your tenant child module.

REFACTORED:

root variable definitions:

variable tenants {
  type = map
  default = {
    tenant1 = {
      name = "Tenant1"
    },
    tenant2 = {
      name = "Tenant2"
    }
  }
}

variable schemas {
  type = map
  default = {
    schema1 = {
      name          = "Schema1",
      template_name = "Template1",
      tenant_id     = "tenant1"
    },
    schema2 = {
      name          = "Schema2"
      template_name = "Template2",
      tenant_id     = "tenant2"
    },
    schema3 = {
      name          = "Schema3"
      template_name = "Template3",
      tenant_id     = "tenant1"
    },
  }
}

calling the modules like this in the root:

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

  for_each = var.tenants

  tenant = each.value.name
}

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

  for_each = var.schemas

  name          = each.value.name
  template_name = each.value.template_name
  tenant        = module.tenant[each.value.tenant_id].mso_tenant
}

root output definitions could look like:

output "tenant_names" {
  value = { for p in sort(keys(var.tenants)) : p => module.tenant[p].mso_tenant.name }
}

output "schemas" {
  value = module.schema
}

this leads to the following outputs:

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

schemas = {
  "schema1" = {
    "schema" = {
      "name" = "Schema1"
      "template_name" = "Template1"
      "tenant" = {
        "name" = "Tenant1"
      }
    }
  }
  "schema2" = {
    "schema" = {
      "name" = "Schema2"
      "template_name" = "Template2"
      "tenant" = {
        "name" = "Tenant2"
      }
    }
  }
  "schema3" = {
    "schema" = {
      "name" = "Schema3"
      "template_name" = "Template3"
      "tenant" = {
        "name" = "Tenant1"
      }
    }
  }
}
tenant_names = {
  "tenant1" = "Tenant1"
  "tenant2" = "Tenant2"
}

schema module used to simulate:

variable "name" {
  type = string
}

variable "template_name" {
  type = string
}

variable "tenant" {
  type = any
}

output "schema" {
  value = {
    name          = var.name
    template_name = var.template_name
    tenant        = var.tenant
  }
}

tenant module used to simulate:

variable "tenant" {
  type = string
}

locals {
  mso_tenant = {
    tenant = {
      name = var.tenant
    }
  }
}

output "mso_tenant" {
  value = local.mso_tenant.tenant
}

hope in the full context this is easier and more clear ;)