When you operate across multiple cloud accounts or multiple regions at the same time, Terraform provider aliases are unavoidable. This article compactly covers design, modularization, and credential switching, all aligned with the official specification.
Content follows the official Terraform documentation (https://developer.hashicorp.com/terraform/language/providers), and version-specific behavior is treated carefully. The second half of the article covers the points most often tested on the exam, along with a sample question.
In Terraform, the alias meta-argument lets you have multiple configurations of the same provider (e.g., different regions or credentials). A configuration without an alias is the default configuration, while one with an alias becomes a separately named configuration. On the resource or data source side, you can explicitly select which configuration to use via the provider meta-argument.
Inside a module, the default provider configuration is implicitly inherited from the parent. To make the child use an aliased configuration, you pass it explicitly via the providers map on the module block. This is the core pattern for multi-account / multi-region operations.
Minimal alias example (using AWS)
terraform {
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
provider "aws" {
region = "ap-northeast-1" # default configuration
}
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
resource "aws_s3_bucket" "tokyo" {
bucket = "example-tokyo-bucket-unique"
}
resource "aws_s3_bucket" "oregon" {
provider = aws.usw2
bucket = "example-oregon-bucket-unique"
}
In practice, three patterns are common: 1) define multiple aliases in the root and use them directly, 2) modularize and inject multiple configurations via the providers map, or 3) split into separate stacks (directories) and separate the state as well. Choose based on scale and isolation requirements (compliance / blast radius).
When spanning multiple accounts (e.g., dev/prod) and multiple regions (e.g., ap-northeast-1 / us-west-2), it is safer to separate state along account boundaries. Region differences can be handled in the same state, but consider isolation based on business requirements once you account for change conflicts and concurrent runs.
| Pattern | Scope | Pros | Caveats / Risks |
|---|---|---|---|
| Root-level (multiple aliases) | Small to medium, single team | Simple; low learning curve | Tends toward a monolith; blast radius grows |
| Module + providers map | Medium to large, split teams | Explicit dependencies; easy to reuse and test | Easy to forget passing providers and fall back to the implicit default |
| Split stacks (state separated too) | Strict isolation (accounts / environments) | Minimal blast radius; least-privilege | Cross-cutting changes are harder; operations need extra care |
Relationship between multi-account / multi-region and aliases
Example: injecting multiple providers into a module
module "multi_region" {
source = "./modules/multi_region"
providers = {
aws = aws # default (ap-northeast-1)
aws.usw2 = aws.usw2 # other region (us-west-2)
}
}
A child module implicitly inherits the parent's default configuration, but aliased configurations are not implicitly inherited. When the child uses an alias, you must map the provider names the child references (e.g., aws, aws.usw2) to concrete parent instances (such as aws or aws.usw2) via the providers map on the module block.
The key is the name used inside the child module, and the value is the concrete instance defined in the parent. The value can be an expression, so conditional branching is possible, but you cannot create the alias itself dynamically (the provider block does not support for_each).
Example: using a separate region in a child module
# Parent side
module "net" {
source = "./modules/net"
providers = {
aws = aws # default: ap-northeast-1
aws.usw2 = aws.usw2 # additional: us-west-2
}
}
# Child side (./modules/net/main.tf)
terraform {
required_providers {
aws = { source = "hashicorp/aws" }
}
}
resource "aws_vpc" "primary" {
cidr_block = "10.0.0.0/16" # default configuration (ap-northeast-1)
}
resource "aws_subnet" "west" {
provider = aws.usw2 # aliased configuration (us-west-2)
cidr_block = "10.1.0.0/24"
vpc_id = "vpc-xxxxxxxx"
}
Switch credentials and privileges per alias. With AWS there are multiple paths — profile, assume_role, environment variables — evaluated in a specific order of precedence. Following each provider's official documentation, it is safest to write only the intended method into your configuration.
When strong account-level isolation is required, separate not just aliases but also state per environment / account, and separate the execution role used from CI. You can switch regions via variables, but keeping alias names static makes migrations easier.
Example: separate credentials per alias (AWS)
provider "aws" {
alias = "dev"
region = var.dev_region
profile = "dev"
}
provider "aws" {
alias = "prod"
region = var.prod_region
assume_role {
role_arn = var.prod_role_arn
session_name = "tf-prod"
}
}
resource "aws_kms_key" "dev" {
provider = aws.dev
description = "dev key"
}
resource "aws_kms_key" "prod" {
provider = aws.prod
description = "prod key"
}
State holds a reference to which provider configuration manages each resource. Be careful when changing alias names themselves — renaming alone, even with an identical API target, becomes a breeding ground for unexpected diffs. Choose alias names that will remain stable over the long term.
For import (the Terraform 1.5+ import block) and other migrations, you can also explicitly specify which alias configuration to use. plan/apply will use the appropriate configuration based on the provider meta-argument referenced by each resource.
Specifying an alias in the import block (1.5+)
import {
to = aws_s3_bucket.oregon
id = "example-oregon-bucket-unique"
provider = aws.usw2
}
Both the Associate and Professional exams frequently ask about where aliases are defined, how to specify them on resources and modules, and the scope of implicit inheritance. In particular, forgetting to pass an alias to a module — causing the child to fall back to the default configuration and operate on an unintended region or account — is a common question pattern.
The provider argument can take an expression, but you cannot generate the alias itself dynamically. For sprawling region rollouts, supplement with directory splitting or templating.
A common mistake and the correct specification
# Incorrect (child uses aws.usw2 but parent does not pass it)
module "m" {
source = "./m"
# No providers map -> child sees only the default aws
}
# Correct
module "m" {
source = "./m"
providers = {
aws.usw2 = aws.usw2
}
}
Associate / Pro
問題 1
A child module creates AWS resources in both us-west-2 and ap-northeast-1. Inside the child, some resources explicitly use aws.usw2. Which parent-module configuration is correct?
正解: A
If the child references aws.usw2, the parent's module.providers must map the key aws.usw2 to the parent's concrete aws.usw2 instance. The default aws alone will not resolve the child's aws.usw2 reference, and typos in the key name (such as aws.use2) are not acceptable either.
Can I create provider aliases dynamically (e.g., with for_each or variables)?
No. The provider block does not support count or for_each. Define as many provider blocks as you need statically, and switch between them inside modules using the providers map.
Is the providers map required when a child module only uses the default configuration?
Not required. The parent's default configuration is inherited implicitly. You only need to pass the providers map explicitly when the child uses an aliased configuration.
What happens if I rename an alias later?
The provider configuration key referenced by state will change, which causes drift. Even if the underlying target is identical, unexpected side effects can occur, so the recommended policy is to avoid renaming. If you absolutely must rename, plan a phased migration and carefully review the plan diff and blast radius.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...