Automatically injecting module parameters

I’m thinking about ways to decrease the amount of boilerplate code when using modules.

There are certain parameters that ALL our modules require. Things like “environment” or “region”, so they can be used to name things created by the module.

Would it be possible for a module to access the caller’s environment? This sounds awful from a security perspective but is it possible at all?

Failing that, is there any mechanism that would allow for automatically injecting certain input parameters to every module?

Hi @gtirloni,

The closest thing is to group all of these “cross-cutting variables” into a single object variable so that it appears only as a single line in your module calls:

variable "context" {
  type = object({
    environment = string
    region      = string
  })
}

Then in the calling module:

variable "environment" {
  type = string
}

variable "region" {
  type = string
}

locals {
  shared_context = {
    environment = var.environment
    region      = var.region
  }
}

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

  context = local.shared_context
  # etc, etc
}

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

  context = local.shared_context
  # etc, etc
}

Terraform requires explicit passing so that the data flow is explicit between modules, due to the principle of favoring the reader over the writer unless some other need overrides that.

However, this approach (of grouping multiple values together into a single object by topic) also lends itself well to later refactoring to adopt the Module Composition patterns: if your idea of “environment” is described by a conventional object type then you might later refactor to make “the environment” be managed in a separate configuration from more specific bits of infrastructure and then use a data-only module to retrieve that environment data. If that module’s outputs match the environment object type you defined then you can use the module object as a whole to populate the variable:

module "environment_context" {
  # It's a "soft convention" to give data-only modules
  # a prefix like "join-", to avoid potential naming conflicts
  # with a module that _manages_ the environment.
  source = "./modules/join-environment"

  # whatever arguments are needed to choose an environment
}

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

  context = module.environment_context
  # etc, etc
}

Very interesting, thanks a lot! I’ll experiment with these ideas here.