Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 10 min read

Passing Aliased Providers Into Terraform Modules the Right Way

Implicit provider inheritance breaks the moment a module needs two regions or accounts. Here's how to wire aliased providers explicitly with configuration_aliases and provider maps.

  • #terraform
  • #ai
  • #providers
  • #modules
  • #multi-region

Single-region Terraform modules feel effortless because of a convenience you probably never thought about: child modules silently inherit the default provider from the root. You never wire anything; it just works. Then someone asks for a cross-region replica, or a DNS record in a separate account, and that convenience turns into an error you can’t make sense of — because a child module has no way to ask for a specific aliased provider unless you tell it how.

The fix is to make provider plumbing explicit. It’s more verbose, but it’s the only thing that scales past one region or one account.

Why inheritance stops being enough

A root module can define multiple aliased providers:

provider "aws" {
  region = "us-east-1"
}

provider "aws" {
  alias  = "replica"
  region = "eu-west-1"
}

The default aws provider is inherited by child modules automatically. The aws.replica provider is not — aliased providers are never inherited implicitly. So the moment a module needs to create something in eu-west-1, you have to pass that provider in, and the module has to declare that it expects it.

Declaring the contract with configuration_aliases

Inside the child module, you declare which providers it consumes using configuration_aliases:

# modules/replicated-bucket/versions.tf
terraform {
  required_providers {
    aws = {
      source                = "hashicorp/aws"
      version               = ">= 5.0"
      configuration_aliases = [aws.primary, aws.replica]
    }
  }
}

This turns “this module happens to use whatever it inherited” into “this module requires exactly two AWS providers, named primary and replica.” That’s a contract a reviewer — and Terraform itself — can check. The module then references them explicitly:

resource "aws_s3_bucket" "source" {
  provider = aws.primary
  bucket   = "${var.name}-source"
}

resource "aws_s3_bucket" "replica" {
  provider = aws.replica
  bucket   = "${var.name}-replica"
}

Wiring the call site with a providers map

At the module block, you supply a providers = {} map. The names on the left are the module’s internal expectations; the values on the right are your configured providers:

module "bucket" {
  source = "./modules/replicated-bucket"
  name   = "app-data"

  providers = {
    aws.primary = aws
    aws.replica = aws.replica
  }
}

Read that map as “the module’s aws.primary is fed by my default aws; its aws.replica is fed by my aws.replica.” Once you pass any provider explicitly, there’s no automatic inheritance for that module — you’re now responsible for every provider it needs.

The nested hop everyone forgets

The part that catches people is forwarding aliases through more than one level. If module A calls module B, and B also needs both providers, A has to pass them down again:

# inside modules/replicated-bucket/main.tf (module A)
module "lifecycle_rules" {            # module B
  source = "./modules/lifecycle"

  providers = {
    aws.primary = aws.primary
    aws.replica = aws.replica
  }
}

There’s no re-inheritance once you’ve gone explicit. Every hop in the tree has to forward the aliases, and B must declare its own configuration_aliases. Miss a hop and terraform validate fails with “provider configuration not present” — annoying, but at least it fails loudly at plan time rather than silently creating resources in the wrong place.

Let AI map the flow, then validate

Tracing which alias each module level needs across a deep tree is exactly the kind of bookkeeping AI is good at — provided you check its work with terraform validate. A useful prompt:

Here’s my module hierarchy: root calls network, which calls peering. The root has aws (us-east-1) and aws.peer (us-west-2). The peering module needs both. Show the configuration_aliases for each module and the providers = {} map at every call site, including the nested hop from network to peering.

A good response gives you:

peering declares configuration_aliases = [aws.this, aws.peer]. The root passes { aws.this = aws, aws.peer = aws.peer } to network; network forwards { aws.this = aws.this, aws.peer = aws.peer } to peering. Run terraform validate to confirm every provider resolves. Note: moving a resource onto a different aliased provider can re-target its region and force replacement — check the plan.

That last note is why you verify rather than trust. Our aliased provider passing prompt is structured to produce the flow map, the contracts, and the call-site maps together, and our module passed-provider config prompt covers the single-level case in more depth.

A few rules that keep it sane

  • Declare every alias a module uses. If a module references aws.replica, it must list it in configuration_aliases. No exceptions.
  • Never rely on inheritance for aliased providers. Only the default provider is inherited; aliases must always be passed.
  • Forward through every hop. Deep trees need the map repeated at each module block.
  • Watch the plan when re-pointing resources. Changing which provider a resource uses can move it to a new region or account and recreate it.

Explicit provider wiring is more typing, but it makes a module’s dependencies legible and lets the same module serve one region or five without rewrites. For the wider set of provider, region, and account patterns, browse our Terraform category.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.