Skip to main content

The Module providers Meta-Argument

In a module call block, the optional providers meta-argument specifies which provider configurations from the parent module will be available inside the child module.

Code Block
# The default "aws" configuration is used for AWS resources in the root
# module where no explicit provider instance is selected.
provider "aws" {
region = "us-west-1"
}

# An alternate configuration is also defined for a different
# region, using the alias "usw2".
provider "aws" {
alias = "usw2"
region = "us-west-2"
}

# An example child module is instantiated with the alternate configuration,
# so any AWS resources it defines will use the us-west-2 region.
module "example" {
source = "./example"
providers = {
aws = aws.usw2
}
}

Each module in an OpenTofu configuration has its own separate namespace of provider configurations, but a child module's namespace is populated with configurations from the root module, either inheriting the default provider configurations automatically or explicitly passing them from the parent using the providers argument.

Default Behavior: Inherit Default Providers

If the child module does not declare any configuration aliases, the providers argument is optional. If you omit it, a child module inherits all of the default provider configurations from its parent module. (Default provider configurations are any that don't use the alias argument.)

If you specify a providers argument, it cancels this default behavior, and the child module will only have access to the provider configurations you specify.

Usage and Behavior

The providers argument uses a map-like syntax delimited by braces ({, }). In the given mapping:

  • The keys are the provider configuration addresses that will be used inside the child module.
  • The values are provider instance addresses from the parent module.

Both parts use provider instance reference syntax, which for alternative provider configurations appears as <PROVIDER NAME>.<ALIAS>.

Within a child module, resources are assigned to provider configurations as normal — either OpenTofu chooses a default based on the name of the resource type, or the resource specifies an alternate configuration with the provider argument. If the module receives a providers map when it's called, the provider configuration names used within the module are effectively remapped to refer the specified configurations from the parent module.

When to Specify Providers

There are two main reasons to use the providers argument:

  • Using different default provider configurations for a child module.
  • Configuring a module that requires multiple configurations of the same provider.

Changing Default Provider Configurations

Most re-usable modules only use default provider configurations, which they can automatically inherit from their caller when providers is omitted.

However, in OpenTofu configurations that use multiple configurations of the same provider, you might want some child modules to use the default provider configuration and other ones to use an alternate. (This usually happens when using one configuration to manage resources in multiple different regions of the same cloud provider.)

By using the providers argument (like in the code example above), you can accommodate this without needing to edit the child module. Although the code within the child module always refers to the default provider configuration, the actual configuration of that default can be different for each instance.

Modules With Alternate Provider Configurations

In rare cases, a single re-usable module might require multiple configurations of the same provider. For example, a module that configures connectivity between networks in two AWS regions is likely to need both a source and a destination region. In that case, the root module may look something like this:

Code Block
provider "aws" {
alias = "usw1"
region = "us-west-1"
}

provider "aws" {
alias = "usw2"
region = "us-west-2"
}

module "tunnel" {
source = "./tunnel"
providers = {
aws.src = aws.usw1
aws.dst = aws.usw2
}
}

Non-default provider configurations are never automatically inherited, so any module that works like this will always need a providers argument. The documentation for the module should specify all of the provider configuration names it needs.

Module instances with differing provider instances

When you write a provider block using the for_each meta-argument the provider configuration dynamically declares zero or more provider instances.

If you also write a module block that uses for_each you can set its provider configuration addresses to refer to dynamically-chosen instances of a multi-instance provider configuration, which allows instantiating a module once per provider instance.

For example, you might instantiate a module for each of a number of different AWS regions, declaring foundational infrastructure across all of the regions you use, with the module itself using only one default provider configuration that differs for each module instance:

Code Block
variable "aws_regions" {
type = map(object({
vpc_cidr_block = string
}))
}

provider "aws" {
alias = "by_region"
for_each = var.aws_regions

region = each.key
}

module "per_region" {
source = "./per-region"
# This expression filters var.aws_regions to include only
# the elements whose value is not null. Refer to the
# warning in the text below for more information.
for_each = {
for region, config in var.aws_regions : region => config
if config != null
}
providers = {
aws = aws.by_region[each.key]
}

region_name = each.key
vpc_cidr_block = each.value.vpc_cidr_block
}

The module in ./per-region should be written so that all of its AWS resources are bound to that module's default configuration for the AWS provider. The providers argument in the module block ensures that each instance of the module has its default configuration for the AWS provider bound to a different instance of aws.by_region.

All instances of the module must refer to instances of the same provider configuration: only the expression in brackets (each.key in the above example) can vary between the instances of the module.

More Information for Module Developers

For more details and guidance about working with providers inside a re-usable child module, see Module Development: Providers Within Modules.