Real estates span several regions and accounts. One configuration can talk to all of them through provider configurations with aliases.
flowchart LR P1["provider aws (default, ap-southeast-1)"] --> M1["module.app_sg"] P2["provider aws.us (us-east-1)"] --> M2["module.cdn_cert"] P3["provider aws.prod (assume_role prod)"] --> M3["module.app_prod"] CI["CI role (OIDC)"] -->|"sts:AssumeRole"| P3 %% step P1: The default provider configuration has no alias and is used when nothing else is set. %% step P2: An alias such as aws.us adds a second configuration, here for resources that must live in us-east-1. %% step M2: Modules get a specific configuration through the providers argument of the module block. %% step P3: An alias can also assume a role in another account, so one pipeline reaches several accounts. %% step CI: The pipeline itself holds only one identity and assumes roles from there.
Aliases
provider "aws" {
region = "ap-southeast-1"
}
provider "aws" {
alias = "us"
region = "us-east-1"
}
provider "aws" {
alias = "prod"
region = "ap-southeast-1"
assume_role {
role_arn = "arn:aws:iam::123456789012:role/terraform-deployer"
}
}
resource "aws_acm_certificate" "cdn" {
provider = aws.us # CloudFront certificates must be in us-east-1
domain_name = "shop.example.com"
}
Passing providers into modules
module "app_prod" {
source = "./modules/app"
providers = {
aws = aws.prod
}
}
A module that needs two configurations declares them:
# modules/cdn/versions.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [aws.us]
}
}
}
Modules should never define provider blocks themselves (topic 1.1); they receive them.
Big runs: -target and -parallelism
| Option | Use | Risk |
|---|---|---|
-target=ADDRESS |
Emergency fix of one resource, or bootstrapping in steps | Plans an incomplete graph; state and code can drift apart. Follow with a full plan |
-parallelism=N (default 10) |
Lower it when an API rate-limits you; raise it carefully for huge states | Too high → throttling errors |
-refresh=false |
Quick look at a huge plan | Plan can be wrong |
| Split states (2.4) | The real fix for slow plans | Needs planning |
Terraform itself warns when targeting is used: it is not a workflow, it is an escape hatch.
Many accounts: a pattern
- One state per account per layer (
prod/network,prod/app). - CI has one OIDC identity; each layer's provider assumes a deployer role in its account.
- Guard rails at the organisation level (SCPs, Azure Policy) limit what even that role can do.
Traps: forgetting provider = aws.us silently creates the resource in the default region; and removing a provider alias while resources in state still use it fails the plan until you move or destroy those resources first.