
September 10, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Terraform Provider Aliases for Multi-Cloud solve a narrow but painful problem: one codebase must create resources in more than one cloud account, region, or vendor in the same run. A single default aws or azurerm block is not enough when your app spans Mumbai and Frankfurt, or AWS plus Azure for failover. Aliases give each target its own authenticated provider instance while keeping related infrastructure in one plan. If you already treat infrastructure as code, see our Terraform infrastructure-as-code practical guide for the baseline patterns this article builds on.
alias argument—one per account, region, or cloud. Resources and modules reference them via provider = aws.eu or a providers map, so a single root module can manage AWS, Azure, and GCP together.What are Terraform Provider Aliases for Multi-Cloud?
A provider alias is a named duplicate of a provider configuration. Terraform allows only one default provider per type in a module. Any additional instance needs alias = "name".
In multi-cloud work, aliases usually map to one of these targets:
- A second AWS region (primary in
ap-south-1, DR ineu-west-1) - A separate AWS account (prod vs shared services)
- A different vendor entirely (
aws,azurerm,googlein one root module) - A partner or client subscription you manage from one repo
Aliases do not replace multi-cloud Terraform state design. They only tell Terraform which credentials and endpoint to use for a given resource. State still lives in one backend unless you split stacks on purpose.
On production systems I maintain, aliases show up most often during migration. A client keeps the old VPS while the new cloud stack comes online. Aliases let both environments coexist in one module until cutover. That pattern pairs well with controlled website and infrastructure migration where downtime must stay minimal.
How do you configure provider aliases in Terraform?
Start in the root module. Declare providers in required_providers, then add one block per alias. Pin versions—drift here breaks plans silently across clouds. Our provider version pinning guide covers that discipline.
Step 1: Declare providers and versions
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
google = {
source = "hashicorp/google"
version = "~> 6.0"
}
}
} Step 2: Configure default and aliased providers
# Default AWS — Mumbai primary
provider "aws" {
region = "ap-south-1"
default_tags {
tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
}
# Aliased AWS — Ireland DR region
provider "aws" {
alias = "dr"
region = "eu-west-1"
default_tags {
tags = {
Environment = "production"
Role = "dr"
ManagedBy = "terraform"
}
}
}
# Aliased Azure — backup target
provider "azurerm" {
alias = "backup"
features {}
subscription_id = var.azure_backup_subscription_id
}
# Aliased GCP — analytics project
provider "google" {
alias = "analytics"
project = var.gcp_analytics_project
region = "asia-south1"
} Step 3: Attach aliases to resources
Resources without a provider argument use the default. Everything else must name the alias explicitly.
resource "aws_s3_bucket" "primary_assets" {
bucket = "app-assets-ap-south-1"
}
resource "aws_s3_bucket" "dr_replica" {
provider = aws.dr
bucket = "app-assets-eu-west-1"
}
resource "azurerm_storage_account" "cold_backup" {
provider = azurerm.backup
name = "appbackupstore"
resource_group_name = azurerm_resource_group.backup.name
location = "southeastasia"
account_tier = "Standard"
account_replication_type = "GRS"
}
resource "google_storage_bucket" "logs" {
provider = google.analytics
name = "app-logs-asia-south1"
location = "ASIA-SOUTH1"
} Official reference: HashiCorp documents provider configuration and the alias meta-argument in the Terraform provider configuration docs. Treat that page as the source of truth when syntax changes between releases.
Authentication across clouds
Each aliased provider resolves credentials independently. On a CI runner, that usually means distinct environment variables or OIDC roles per account.
- AWS default:
AWS_ACCESS_KEY_ID/ IAM role for account A - AWS alias
dr:assume_roleblock inside the provider, or a named profile - Azure alias:
ARM_SUBSCRIPTION_ID,ARM_CLIENT_ID, tenant vars scoped to backup sub - GCP alias: service account JSON path or Workload Identity Federation
Never hard-code secrets in provider blocks. Use environment variables, Vault, or your pipeline secret store. Multi-cloud secret handling overlaps with our multi-cloud secrets management write-up.
When should you use provider aliases instead of workspaces or separate root modules?
Aliases, workspaces, and separate root modules all split configuration. They solve different problems. Pick the wrong one and you get brittle state or unclear ownership.
| Approach | Best for | State layout | Multi-cloud fit |
|---|---|---|---|
| Provider aliases | Same logical app, multiple regions/accounts/vendors in one deployment unit | Single state (or one state per stack that intentionally spans clouds) | Excellent — native multi-vendor support |
| Workspaces | Identical code, different env vars (dev/stage/prod) on one cloud | One backend, workspace-prefixed resources | Poor — one workspace still shares provider config unless you use vars heavily |
| Separate root modules | Independent lifecycles, different teams, strict blast-radius isolation | One state per root module | Good — clearest boundaries, more orchestration overhead |
| Terragrunt / stack wrappers | Many similar roots with shared remote state config | Multiple states, DRY backend config | Good at scale — see Terragrunt for DRY Terraform |
Use aliases when resources must be created together and reference each other. Example: primary RDS in AWS Mumbai plus read replica in AWS Ireland plus Azure Blob for backup—all in one apply so outputs wire cleanly.
Use separate roots when teams, approval paths, or blast radius differ. A networking team owning hub VPCs should not share state with an app team shipping Lambda functions. Our multi-cloud architecture guide walks through that split in more detail.
Workspaces alone cannot give you a second cloud vendor. They switch context inside one provider setup. Many engineers confuse the two and then wonder why terraform workspace select prod does not reach Azure.
For Nepal-based teams weighing cloud mix, cost and latency often push primary workloads to one region with DR abroad. Aliases keep that topology visible in one file instead of scattered shell scripts. Compare broader strategy in hybrid cloud vs multi-cloud before you commit to three vendors.
How do you pass aliased providers to Terraform modules?
Child modules do not inherit parent aliases automatically. You must declare configuration_aliases inside the module and map them in the parent's providers argument. This trips experienced engineers because default provider passing feels implicit until it breaks.
Inside the child module
# modules/dr-bucket/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [aws]
}
}
}
variable "bucket_name" {
type = string
}
resource "aws_s3_bucket" "this" {
bucket = var.bucket_name
} Note: the module never sets alias. It accepts whichever aws instance the parent injects.
Calling the module from root
module "dr_bucket" {
source = "./modules/dr-bucket"
providers = {
aws = aws.dr
}
bucket_name = "company-dr-assets"
} For multi-cloud modules that need both AWS and Azure, list both in configuration_aliases and map each key in the parent.
# modules/hybrid-backup/main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
configuration_aliases = [aws]
}
azurerm = {
source = "hashicorp/azurerm"
configuration_aliases = [azurerm]
}
}
} module "hybrid_backup" {
source = "./modules/hybrid-backup"
providers = {
aws = aws
azurerm = azurerm.backup
}
} Reusable modules belong in your org registry or a versioned git path. Patterns for that structure appear in Terraform modules for reusable infrastructure. Validate HCL with a JSON formatter when generating provider maps from scripts—bad escaping breaks plans fast.
HashiCorp documents the module providers meta-argument in the official module provider passing guide. Read it before abstracting a shared multi-cloud module library.
Implicit vs explicit default passing
If a module only uses the default provider and does not declare configuration_aliases, Terraform passes the parent's default automatically. The moment a module needs a non-default alias, implicit passing stops. Every child module in the chain must re-export the alias requirement upward.
Deep module trees get verbose. Some teams generate the providers map with Terragrunt or a thin wrapper. Others split stacks instead of nesting five modules deep. Both are valid; pick based on who owns reviews.
What are common mistakes with Terraform provider aliases in multi-cloud setups?
Most alias failures I see are not syntax errors. They are wrong assumptions about state, credentials, or module boundaries.
Forgetting the provider meta-argument on resources
A resource without provider = aws.dr lands in the default region. You think you built DR in Ireland; you built two buckets in Mumbai. Add lint rules or Checkov scans on Terraform to catch unannotated resources in multi-alias roots.
Mixing unrelated lifecycles in one state
Aliases encourage one big state file. That is fine for a tightly coupled app stack. It is dangerous when unrelated platforms share it. One bad terraform destroy can touch production AWS and a client Azure sub together. Split state when ownership or risk profiles differ. Follow safe Terraform state management practices either way.
Duplicated provider config instead of dynamic blocks
Teams copy-paste ten nearly identical provider "aws" blocks for ten regions. Use for_each on a map of regions where the provider supports it, or generate blocks with Terraform dynamic blocks. Provider blocks themselves cannot use for_each in older patterns, but Terraform 1.x allows provider "aws" with aliases via generated config in some wrapper tools. Keep the root readable.
Version skew across clouds
AWS provider 5.x and Azure 4.x release on different cadences. Pin each independently in required_providers. A blanket >= 1.0 constraint invites breaking schema changes mid-sprint.
Ignoring plan scope and blast radius
A plan touches every provider configured. CI must hold credentials for all of them even if the change looks AWS-only. Scope plans with -target only for emergencies—not daily workflow. Prefer smaller stacks instead.
Real-world pattern: app plus DR plus backup
On a booking platform I helped operate, primary compute stayed on one cloud region. DR replicas and object storage spanned a second region and vendor. Aliases let Terraform output the primary database endpoint and the DR replica hostname in one apply output block—useful for runbooks and production booking infrastructure that cannot afford silent drift.
Pair aliases with remote state locking. Multi-cloud plans run longer; concurrent applies corrupt state more painfully. Governance policies from multi-cloud policy as code should block public exposure regardless of which alias created a resource.
Variables, outputs, and workspace hygiene
Keep cloud-specific IDs in clearly named variables. Use outputs to expose only what downstream stacks need. Our Terraform variables and outputs guide applies unchanged—aliases do not replace disciplined naming.
For environment separation inside one cloud, combine aliases with Terraform workspaces and environments. Example: default AWS for app, aliased AWS for shared DNS, workspace selects prod vs staging tags. Do not expect workspaces alone to select Azure vs AWS.
When exploring OpenTofu as a fork, alias syntax stays compatible for most roots. Review OpenTofu vs Terraform before switching provider sources in regulated workloads.
Key Takeaways
- Define one provider block per cloud target; add
aliasfor every non-default instance. - Bind resources with
provider = aws.drand modules with an explicitprovidersmap. - Declare
configuration_aliasesin any module that accepts injected providers. - Prefer aliases for coordinated multi-cloud applies; split root modules when blast radius or ownership differs.
- Pin provider versions per vendor and scan plans in CI with credentials for every configured alias.
- Keep state boundaries intentional—aliases wire providers; they do not replace sound multi-cloud state design.
People Also Ask
Can one Terraform resource use multiple providers?
No. Each resource block binds to exactly one provider instance. Cross-cloud relationships use references between resources—output from an AWS resource fed into an Azure resource—not a multi-provider resource block. That one-resource-one-provider rule keeps dependency graphs predictable.
How many provider aliases can Terraform have?
There is no hard cap in Terraform core. Practical limits are plan time, credential management, and human readability. Beyond a handful of aliases, consider generated config or split stacks. Very large alias sets belong in Terragrunt or separate repos per domain.
Do provider aliases work with OpenTofu?
Yes for mainstream patterns. OpenTofu maintains compatibility with Terraform language constructs including alias, providers module maps, and configuration_aliases. Always run tofu validate after porting because provider registry paths may differ in air-gapped installs.
What is the difference between provider alias and assume_role?
An alias names a provider configuration inside Terraform. assume_role is an AWS provider argument that fetches temporary credentials for a different IAM role. You often combine them: an aliased provider "aws" block with its own assume_role targets a DR account while the default provider stays in the primary account.
Ship multi-cloud infrastructure without alias surprises
Terraform Provider Aliases for Multi-Cloud are the correct tool when one deployment unit must reach multiple regions, accounts, or vendors in a single plan. Configure named provider blocks, pin versions, wire modules explicitly, and split state when blast radius grows beyond one team. Start small—primary plus one DR alias—before you orchestrate three vendors from one root module. If you want help designing IaC for a migration or DR setup on Linux cloud infrastructure, or reviewing an existing Terraform layout before production cutover, contact us for a technical review.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

