Chapter 7: Providers in Depth
Topic 42

Provider Configuration and Aliases

ProvidersConfig

A provider block configures how Terraform talks to a platform. For AWS that means the region it operates in, where it gets credentials, and the tags it stamps on everything it creates. One block is enough for a single-region, single-account project — but real estates are neither.

Aliases are how one configuration uses the same provider several ways at once: two regions, or two accounts, in a single apply. Every multi-region disaster-recovery setup and every cross-account Terraform run is built on this one mechanism. Get aliases right and the rest of multi-target Terraform is just plumbing.

One config, several provider instances
aws (default)
No alias — region us-east-1. Every resource without a provider argument lands here.
aws.us_west
Same credentials, region us-west-2. A resource opts in with provider = aws.us_west.
aws.audit
assume_role into another account — manages the audit account from this run.

The Basic Provider Block

The default provider block names the platform and sets its region. The interesting argument is default_tags: every resource this provider creates inherits these tags automatically, so you stamp ownership, cost center, and environment once instead of repeating a tag map on a hundred resources. The credentials are deliberately absent — they come from the environment, a named profile, or an assumed role, never from the block itself.

provider.tf — region plus org-wide default tags
provider "aws" {
  region = "us-east-1"

  default_tags {
    tags = {
      Environment = "prod"
      ManagedBy   = "terraform"
      Team        = "platform"
    }
  }
}

Aliased Providers

Give a second provider block an alias and you have a named, independent configuration of the same provider. A resource opts into it with the provider meta-argument; without that line, the resource lands on the default provider. The bucket below is created in us-west-2 only because it points at aws.us_west — drop that one line and it silently goes to us-east-1 instead.

main.tf — a default region and an aliased second region
provider "aws" {
  region = "us-east-1"
}

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

resource "aws_s3_bucket" "dr_logs" {
  provider = aws.us_west
  bucket   = "my-app-dr-logs-7f3a"
}

Passing Providers Into Modules

A module uses the default provider unless you hand it a different one. The providers argument in the module block maps the module's provider name to the aliased configuration you want it to use, so a single module call can deploy into a specific region or account. This is how a reusable "regional stack" module gets pointed at us-west-2 without the module itself knowing which region it runs in.

main.tf — handing an aliased provider to a module
module "dr_stack" {
  source = "./modules/regional-stack"

  providers = {
    aws = aws.us_west
  }
}

Default Tags and assume_role

Two provider-level settings carry most of the operational weight. default_tags applies a tag map to every resource, which is how a whole organization gets consistent cost and ownership tags without trusting each author to remember them. assume_role tells the provider to assume an IAM role before doing anything, which is the foundation of cross-account management: a provider in your tooling account assumes a role in a workload account and manages its resources from there.

provider.tf — assuming a role in another account
provider "aws" {
  alias  = "workload"
  region = "us-east-1"

  assume_role {
    role_arn     = "arn:aws:iam::222233334444:role/TerraformExec"
    session_name = "terraform-prod"
  }
}

One caveat on tags: default_tags covers tags Terraform sets, but if a resource sets the same tag key directly, the resource's value cleanly overrides the provider default for that key (it shows up in tags_all) — there is no perpetual diff. The genuine trap is a tag an outside system mutates out of band, which surfaces as drift, so decide where each tag comes from and keep it in one place.

One Version, Many Configurations

A common misconception is that aliases let you run two different versions of the AWS provider side by side. They do not. A configuration resolves exactly one version of the AWS provider for the whole run; aliases give you multiple configurations of that one version — different regions, accounts, or assumed roles — not different binaries. If you genuinely need two provider versions, that is a state-splitting problem, not an alias problem.

Common Mistakes
  • Defining an aliased provider but forgetting provider = aws.alias on the resource — it silently uses the default provider, so the resource lands in the wrong region or account with no error.
  • Putting access_key and secret_key directly in the provider block, committing permanent credentials to git instead of using the environment, a profile, or an assumed role.
  • Calling a module that operates in a specific region without passing the aliased provider, so the module uses the default provider and deploys into the wrong place.
  • Assuming a resource cannot override a default tag — in fact a tag set on the resource wins over default_tags for the same key, so the override is silent, not a conflict.
  • Assuming an alias can pin a second provider version side by side — one configuration resolves a single AWS provider version, and aliases only vary its configuration.
Best Practices
  • Set default_tags at the provider level for consistent org-wide tagging, so ownership and cost tags exist on every resource without per-resource effort.
  • Keep credentials out of the provider block entirely; rely on the environment, named profiles, or assume_role so nothing secret reaches version control.
  • Pass aliased providers explicitly with the providers argument into any module that must run in a specific region or account.
  • Name aliases for their purpose — us_west, audit_account — so every provider = aws.audit_account line reads as documentation.
  • Use one assume_role provider per target account rather than juggling multiple credential profiles, keeping cross-account intent visible in the config.
Comparable tools CloudFormation StackSets run a template per region and account Pulumi configures explicit provider instances Ansible uses inventory and connection vars per target

Knowledge Check

What does a provider alias let you do?

  • Use the same provider in multiple configurations — two regions or accounts — within one apply
  • Run two different versions of the AWS provider side by side in one configuration
  • Cache the provider's downloaded plugin binaries across configurations so that a subsequent init runs faster
  • Encrypt the static credentials stored directly in the provider block

You define an aliased aws.us_west provider but a resource has no provider argument. What happens?

  • It uses the default provider, landing in the default region rather than us-west-2
  • Terraform halts with an error because the provider alias is ambiguous
  • It is created in both the default and aliased regions to be safe
  • Terraform picks one alias at random from among all the provider blocks defined in the configuration

How does a module use a non-default provider configuration?

  • The caller passes it with the providers argument in the module block
  • The module declares its own credentials in a nested provider block
  • It silently inherits whichever provider alias was defined last in the file
  • Modules always run on the default provider and cannot be retargeted

What is the effect of default_tags on the provider?

  • Every resource the provider creates inherits the tag map automatically
  • It records the tags only in the state file, not on the real resources themselves
  • It overrides any tag a resource sets directly, with no conflict possible
  • It applies tags only to resources that omit their own tags block

You got correct