DEV Community

Aakash Choudhary
Aakash Choudhary

Posted on

How to Build a Scalable Multi-Region Terraform Repo with Modules

Flow of values from variables.tf from root main.tf to resouce block

Have you ever faced a situation where you needed to deploy identical infrastructure resources across multiple regions in a cloud provider like AWS or Azure?

The best solution to this is multi-region Terraform setups. In this blog, we'll walk through how to structure a Terraform repository to handle resources like S3 buckets, IAM roles, etc., across two or more regions, with reusability, scalability, and simplicity.

What we want?

We'll create a Terraform repository template where:

  • Infrastructure can be replicated across multiple regions.

  • The module logic is centralized and reusable.

  • Region-specific customizations are managed cleanly.

We'll walk through an example of provisioning an S3 bucket in two AWS regions: us-west-1 and us-west-2.

Folder Structure

Here's the directory layout we'll be working with:

├── terraform/
│   ├── backend/
│   │   ├── us-west-1/
│   │   │   └── backend.tfvars
│   │   └── us-west-2/
│   │       └── backend.tfvars
│   ├── modules/
│   │   └── s3/
│   │       ├── main.tf
│   │       ├── variables.tf
│   │       └── output.tf
│   └── regions/
│       ├── us-west-1/
│       │   ├── main.tf
│       │   ├── variables.tf
│       │   ├── provider.tf
│       │   └── output.tf
│       └── us-west-2/
│           ├── main.tf
│           ├── variables.tf
│           ├── provider.tf
│           └── output.tf
├── .gitignore
└── README.md
Enter fullscreen mode Exit fullscreen mode

How to create re-usable terraform repository?

Let’s say we need to create an S3 bucket. Instead of writing this code for each region, we’ll write it once in a reusable module.

So in the modules/s3/variables.tf, the resource looks like this:

resource "aws_s3_bucket" "bucket" {
  bucket        = var.bucket_name
  tags          = var.tags
  force_destroy = true
}
Enter fullscreen mode Exit fullscreen mode

This module expects two input values, so we define them in modules/s3/variables.tf:

variable "bucket_name" {
  type        = string
  description = "Name for the S3 bucket"
}

variable "tags" {
  type        = map(string)
  description = "Tags for the resources to track cost for resources deployed"
}
Enter fullscreen mode Exit fullscreen mode

Conclusion: This module doesn’t contain any hardcoded values, which makes it reusable.

Using the Module per Region

Now, let’s use this module for each region without making changes to the module itself.

In regions/us-west-1/main.tf, we use the module like this:

module "s3" {
  source      = "../../modules/s3"
  bucket_name = var.bucket_name
  tags        = var.tags
}
Enter fullscreen mode Exit fullscreen mode

Where do bucket_name and tags come from?

This module expects two input values, so we define them in modules/s3/variables.tf:

We define them in that region’s variables.tf:

variable "root_var_bucket_name" {
  type        = string
  description = "Name for the S3 bucket"
}

variable "root_var_tags" {
  type        = map(string)
  description = "Tags for the resources to track cost for resources deployed"
}
Enter fullscreen mode Exit fullscreen mode

You can define the actual values in your automation pipeline or in a .tfvars file.

For backend setup, the file backend/us-west-1/backend.tfvars could look like:

bucket         = "my-terraform-state"
key            = "us-west-1/terraform.tfstate"
region         = "us-west-1"
dynamodb_table = "terraform-lock-table"
Enter fullscreen mode Exit fullscreen mode

What to do if i need to make any configuration change, but to any particular region?

Now imagine that you need to change the S3 bucket name — but only for us-west-1.

You don't need to touch the module or duplicate the code. Just go to:

terraform/regions/us-west-1/variables.tf

And update the value for bucket_name. When you run Terraform for that specific region, the module will automatically pick up the new value.

cd terraform/regions/us-west-1
terraform init -backend-config=../../backend/us-west-1/backend.tfvars
terraform plan -var="bucket_name=my-west-bucket" -var="tags={Environment=\"dev\"}"
terraform apply
Enter fullscreen mode Exit fullscreen mode

It doesn’t affect the setup in us-west-2 at all

Best Practices

  • Avoid hardcoding values inside modules — always pass variables.
  • Separate state per region to avoid conflicts.
  • Use identical variable names across regions to standardize configuration.
  • Centralize shared logic but isolate region-specific values.
  • Add CI/CD automation for managing multiple environments smoothly.

Summary

This structure makes it easy to:

  • Replicate infrastructure across regions
  • Reuse modules without code duplication
  • Make changes to individual regions independently

The same approach works not only for S3 buckets, but also IAM roles, DynamoDB, VPCs, and even across clouds like Azure or GCP.

Need more examples or want the full working repo on GitHub? Let me know in the comments!

Top comments (0)