💭 The Problem
I found myself committing a cardinal sin in Terraform — repeating configuration across multiple projects.
When deploying Azure resources, we often need region- or environment-specific values. For example, virtual networks may need different routes or DNS servers depending on the location.
I was managing these using lookup tables in local variables, like this:
locals {
firewall_ip = {
"westeurope" = "10.0.0.1"
"northeurope" = "10.0.0.2"
}
}
This worked fine… until it didn’t.
Every time a region or IP changed, I had to update the same table in multiple projects — a maintenance nightmare.
💡 The Realisation
What I really needed was a global lookup table — a shared, central source of truth I could query across all Terraform projects.
The solution turned out to be beautifully simple:
✅ A centralised shared configuration Terraform module.
🧱 The Shared Configuration Module
All my Terraform modules live in Azure DevOps Git Repos, so I can reference them directly rather than duplicating code.
My shared configuration module only needs one file — outputs.tf — that defines all the shared values:
output "firewall_ips" {
description = "Firewall IP Addresses By Region"
value = {
prod = {
"westeurope" = "10.27.0.4"
"centralus" = "10.28.0.4"
"australiaeast" = "10.29.0.4"
}
dev {
"westeurope" = "10.27.1.4"
"centralus" = "10.28.1.4"
"australiaeast" = "10.29.1.4"
}
`
}
}
output "dns_servers" {
description = "DNS Servers by Regions"
value = {
"westeurope" = ["10.27.1.1", "10.27.1.2"]
"centralus" = ["10.28.1.1", "10.28.1.2"]
"australiaeast" = ["10.29.1.1", "10.29.1.2"]
}
}
This module simply outputs all the shared configuration values I want to reference elsewhere.
🪄 Calling the module
In any Terraform project, I reference it like so:
module "shared_configuration"
source = "gitssh://git@ssh.dev.azure.com/v3/example/Terraform-Modules/tf_module_shared_configuration"
}
Then I use a locals.tf file to do my lookups:
locals {
# Get region-specific values from the shared module
firewall-ip = module.shared_configuration.firewall_ips[var.environment][var.location]
dns-servers = module.shared_configuration.dns_servers[var.location]
}
Here’s what’s happening:
• firewall_ip dynamically selects the right address for the target environment and region.
• dns_servers fetches region-specific DNS values.
Whenever I reference local.firewall_ip or local.dns_servers in my Terraform code, I’m automatically pulling the correct config for that deployment.
🔁 The Benefit
Now, when I need to add a region or update a value, I just modify the shared module once.
All projects referencing it get the change instantly.
No duplication.
No missed updates.
No drift between environments.
⚙️ Summary
By creating a shared configuration Terraform module, you can:
✅ Keep environment and region settings consistent
✅ Avoid copy/paste duplication
✅ Simplify maintenance and updates
✅ Scale your Terraform codebase cleanly
Sometimes the best solutions are the simplest ones — we just need to stop and think about the problem first.
Top comments (1)
Really like this pattern of a shared config module as a single source of truth for region/env values. Using outputs plus locals for lookups is a clean way to kill duplication and config drift. Simple approach, but very effective in larger Terraform codebases.