How to Deploy Across Multiple AWS Regions Without Losing Your Mind
Day 14 of the 30-Day Terraform Challenge — and today I learned that Terraform isn't limited to one region or one account.
One provider config. Multiple regions. Multiple accounts. Same codebase.
Here's how it works.
What Is a Provider?
A provider is a plugin that translates Terraform code into API calls. The AWS provider knows how to create S3 buckets. The Kubernetes provider knows how to create pods. The random provider generates... random stuff.
When you run terraform init, Terraform downloads these plugins from the Terraform Registry. No manual installation. No hunting for binaries.
Pinning Provider Versions
Never leave your provider version blank. That's how things break unexpectedly.
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0" # Any 5.x, but not 6.0
}
}
}
The ~> 5.0 constraint means: use version 5.0 or higher, but less than 6.0. You get bug fixes and security patches, but no breaking changes.
After terraform init, check .terraform.lock.hcl. It records the exact version downloaded. Commit this file to Git. It ensures your whole team uses the same provider version.
The Default Provider
A basic provider config applies to every resource in your configuration:
provider "aws" {
region = "eu-north-1"
}
resource "aws_s3_bucket" "example" {
bucket = "my-bucket" # Deploys in eu-north-1
}
Terraform uses this default provider for every resource that doesn't specify otherwise.
Multiple Regions: Provider Aliases
Want resources in two regions? Define an alias:
# Default provider — primary region
provider "aws" {
region = "eu-north-1"
}
# Aliased provider — secondary region
provider "aws" {
alias = "ireland"
region = "eu-west-1"
}
# Aliased provider — tertiary region
provider "aws" {
alias = "frankfurt"
region = "eu-central-1"
}
Now you can deploy resources anywhere:
# Uses default provider (eu-north-1)
resource "aws_s3_bucket" "primary" {
bucket = "primary-bucket"
}
# Uses ireland alias
resource "aws_s3_bucket" "replica" {
provider = aws.ireland
bucket = "replica-bucket"
}
# Uses frankfurt alias
resource "aws_s3_bucket" "backup" {
provider = aws.frankfurt
bucket = "backup-bucket"
}
Terraform knows exactly which API endpoint to call for each resource.
S3 Cross-Region Replication Example
Here's a practical use case: replicate data across regions for disaster recovery.
# Primary bucket in eu-north-1
resource "aws_s3_bucket" "primary" {
bucket = "app-primary-data"
}
# Replica bucket in eu-west-1
resource "aws_s3_bucket" "replica" {
provider = aws.ireland
bucket = "app-replica-data"
}
# Replication configuration
resource "aws_s3_bucket_replication_configuration" "replication" {
role = aws_iam_role.replication.arn
bucket = aws_s3_bucket.primary.id
rule {
id = "replicate-all"
status = "Enabled"
destination {
bucket = aws_s3_bucket.replica.arn
storage_class = "STANDARD"
}
}
}
Now every file uploaded to the primary bucket is automatically replicated to Ireland. If eu-north-1 goes down, your data is safe.
Multiple AWS Accounts
For multi-account setups, use assume_role:
provider "aws" {
alias = "prod"
region = "eu-north-1"
assume_role {
role_arn = "arn:aws:iam::111111111111:role/TerraformDeployRole"
}
}
provider "aws" {
alias = "staging"
region = "eu-north-1"
assume_role {
role_arn = "arn:aws:iam::222222222222:role/TerraformDeployRole"
}
}
The IAM role in each account needs permissions to create the resources you're managing. Terraform assumes the role, performs the operations, then drops the credentials.
The Lock File Explained
After terraform init, you get .terraform.lock.hcl:
provider "registry.terraform.io/hashicorp/aws" {
version = "5.100.0" # Exact version installed
constraints = "~> 5.0" # Your version constraint
hashes = [
"h1:abc123def456...", # Checksum for verification
]
}
Why commit this file?
- Everyone uses the same provider version
- No "works on my machine" problems
- Hashes verify downloads haven't been tampered with
Chapter 7 Learnings
What happens during terraform init?
- Reads your
required_providersblock - Downloads provider binaries from the Terraform Registry
- Records exact versions in
.terraform.lock.hcl
version vs ~> version:
-
version = "5.0"— exact version only -
~> 5.0— any 5.x version, but not 6.0 (allows patches)
How Terraform chooses a provider: Uses the default provider unless you specify provider = alias in the resource.
What I Learned
Provider aliases unlock multi-region infrastructure. One configuration can now deploy globally.
The lock file is your friend. Commit it. It prevents version drift across your team.
Multi-account deployments are just aliases with assume_role. Same pattern, different accounts.
The Bottom Line
Terraform isn't limited to one region or one account. With provider aliases, you can deploy anywhere.
| Need | Solution |
|---|---|
| Multiple regions | Provider aliases |
| Multiple accounts |
assume_role + aliases |
| Version consistency | Pin versions + commit lock file |
One configuration. Global infrastructure.
P.S. The lock file seems like a small detail, but it's saved me from "but it worked on my laptop" conversations more times than I can count. Commit it. 🔒
Top comments (0)