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

Terraform Provider Configuration and Aliases Done Right

Multi-region and multi-account Terraform lives and dies on provider aliases. Here's how to configure providers, pass them into modules, and avoid the errors that block every apply.

  • #terraform
  • #providers
  • #aliases
  • #multi-region
  • #modules
  • #aws

The first time you need a resource in a second AWS region — an ACM certificate in us-east-1 for a CloudFront distribution while everything else lives in eu-west-1 — Terraform’s provider model stops being invisible. Suddenly you’re staring at Provider configuration not present or Multiple provider configurations, the apply won’t run, and the docs assume you already understand aliases. Let me save you the afternoon.

One provider block, one configuration

By default a provider block configures a single instance, and every resource of that type uses it implicitly:

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

resource "aws_s3_bucket" "data" {
  bucket = "acme-data"   # uses the eu-west-1 provider, no ceremony
}

This is the case that “just works” and is why most people never think about providers. The complexity starts the moment you need two configurations of the same provider at once.

Aliases: two configurations, same provider

When you need a second region or a second account, you give one of the provider configurations an alias:

provider "aws" {
  region = "eu-west-1"   # default, no alias
}

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

Now resources must opt into the aliased provider explicitly with provider = aws.us_east:

resource "aws_acm_certificate" "cdn" {
  provider          = aws.us_east
  domain_name       = "www.example.com"
  validation_method = "DNS"
}

Resources without an explicit provider argument keep using the default (unaliased) configuration. That’s the whole mental model: the unaliased block is the default; aliased blocks are opt-in by name.

Passing providers into modules

Here’s where most people get stuck. Modules don’t automatically see your aliased providers. If a module needs to create resources in two regions, you have to pass the providers in.

First, the module declares which provider configurations it expects, using configuration_aliases:

# modules/cdn/providers.tf
terraform {
  required_providers {
    aws = {
      source                = "hashicorp/aws"
      configuration_aliases = [aws.us_east]
    }
  }
}

Inside the module, resources reference aws (the default the caller passes) or aws.us_east (the certificate region) as needed.

Then the caller wires its real providers to the module’s expected names with a providers map:

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

  providers = {
    aws         = aws            # caller default -> module default
    aws.us_east = aws.us_east    # caller alias   -> module alias
  }
}

The left side is the name inside the module; the right side is the configuration in the caller. Get this mapping right and the cross-region module just works. Get it wrong and you get provider configuration not present, which is Terraform’s way of saying “the module asked for a provider you didn’t pass.”

The multi-account pattern

The same machinery handles multi-account, which is where it earns its keep. Each account gets a provider with an assume_role:

provider "aws" {
  alias  = "network"
  region = "eu-west-1"
  assume_role {
    role_arn = "arn:aws:iam::111111111111:role/terraform"
  }
}

provider "aws" {
  alias  = "workload"
  region = "eu-west-1"
  assume_role {
    role_arn = "arn:aws:iam::222222222222:role/terraform"
  }
}

Now a single configuration can create a Transit Gateway in the network account and attach a VPC in the workload account, passing each module the provider it needs. This is the backbone of most hub-and-spoke AWS setups.

Errors you’ll actually hit, and what they mean

A field guide to the provider errors that block applies:

  • Provider configuration not present — a module needs a provider configuration that’s been removed, or a resource references an alias you never defined. Often shows up after a refactor; check that every provider = aws.x has a matching block and that module providers = {} maps are complete.
  • Multiple provider configurations — you defined the same alias twice, or a default and an alias collide. Each alias value must be unique per provider.
  • Module does not declare a provider named... — you passed a provider in the providers map that the module’s configuration_aliases doesn’t list. Add it to the module’s required_providers, or stop passing it.
  • A provider config inside a module that uses for_each/count — Terraform forbids configuring providers inside modules that are themselves counted. Configure providers in the root and pass them down.

Don’t configure providers inside reusable modules

A rule worth internalizing: reusable modules should not contain provider blocks. A module that hardcodes a region or credentials can’t be reused, can’t be count/for_each-ed, and fights the caller. Instead, declare configuration_aliases for what the module needs and let the caller supply the how. Provider configuration is a root-module concern; modules consume providers, they don’t define them.

This separation is what makes a module portable across regions, accounts, and even between Terraform and OpenTofu without edits.

The takeaway

Provider aliases are how Terraform spans regions and accounts, and the whole system rests on two ideas: the unaliased provider is the default, and modules receive providers explicitly through a providers map that bridges caller names to module names. Keep provider configuration in your root modules, keep provider requirements in your reusable modules, and most of the cryptic errors disappear.

When a provider-map refactor touches cross-account infrastructure, a careful second review catches the mismapped alias before it fails the apply — that’s what our AI code review tooling is built for. For more multi-environment patterns, see the full Terraform guides.

Examples are illustrative. Validate provider wiring with terraform plan before applying changes that span regions or accounts.

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.