Devops

Terraform Locals in Detail: Clean, Reusable Configurations

April 7, 2026
Published
#Automation#Cloud#DevOps#Infrastructure as Code#Terraform

There’s a moment in every Terraform project where things start to feel… repetitive. The same tags, naming conventions, or computed values appear across multiple resources. That’s usually the point where locals stop being optional and start becoming essential.

Terraform locals are often described as “just variables,” but that undersells them. They’re more like a lightweight computation layer inside your configuration—perfect for shaping data, avoiding duplication, and making your code easier to reason about.

What Are Terraform Locals, Really?

At a glance, locals are defined using a locals block and referenced with local.<name>. Unlike input variables, they aren’t passed in—they’re computed within the module.

Here’s a simple example:

TEXT
1locals {
2  project_name = "billing-service"
3  environment  = "prod"
4  full_name    = "${local.project_name}-${local.environment}"
5}

You can then use them anywhere:

TEXT
1resource "aws_s3_bucket" "app_bucket" {
2  bucket = local.full_name
3}

This might seem small, but it scales quickly. Imagine updating naming logic in one place instead of across 20 resources.

Why Not Just Use Variables?

This is a common question. Variables and locals solve different problems:

  • Variables are inputs to your module (external configuration)
  • Locals are internal transformations (derived values)

Think of locals as the “processing layer” between raw inputs and resource definitions.

Building Smarter Configurations with Locals

Here’s where things get interesting—locals aren’t limited to static values. You can use expressions, conditionals, loops, and functions.

Example: Dynamic Tagging Strategy

JSON
1variable "environment" {
2  type = string
3}
4
5locals {
6  common_tags = {
7    Project     = "billing"
8    Environment = var.environment
9  }
10
11  extra_tags = var.environment == "prod" ? {
12    Critical = "true"
13  } : {}
14
15  all_tags = merge(local.common_tags, local.extra_tags)
16}

Then apply it:

TEXT
1resource "aws_instance" "app" {
2  ami           = "ami-123456"
3  instance_type = "t3.micro"
4
5  tags = local.all_tags
6}

This keeps your tagging logic centralized and expressive.

Reducing Duplication Without Losing Clarity

A common mistake developers make is overusing locals for everything. The goal isn’t to hide logic—it’s to clarify intent.

Bad pattern:

TEXT
1locals {
2  a = "us-east-1"
3  b = local.a
4  c = local.b
5}

Good pattern:

TEXT
1locals {
2  region = "us-east-1"
3}

Use locals to make code more readable, not more abstract than necessary.

Transforming Complex Data

Locals shine when working with structured data like maps and lists.

Example: Flattening a Nested Structure

JSON
1variable "services" {
2  default = {
3    api = {
4      ports = [80, 443]
5    }
6    worker = {
7      ports = [8080]
8    }
9  }
10}
11
12locals {
13  service_ports = flatten([
14    for name, svc in var.services : [
15      for port in svc.ports : {
16        name = name
17        port = port
18      }
19    ]
20  ])
21}

This gives you a clean list of service-port pairs, which can be used in for_each loops.

Locals + for_each = Cleaner Resources

Instead of embedding complex expressions directly inside resources, move them into locals.

For example:

TEXT
1locals {
2  instance_configs = {
3    web = { instance_type = "t3.micro" }
4    api = { instance_type = "t3.small" }
5  }
6}
7
8resource "aws_instance" "nodes" {
9  for_each = local.instance_configs
10
11  ami           = "ami-123456"
12  instance_type = each.value.instance_type
13
14  tags = {
15    Name = each.key
16  }
17}

This keeps your resource block clean and focused.

A Few Subtle Gotchas

Locals are powerful, but there are a few nuances worth knowing:

  • They are immutable: once defined, you can’t override them
  • Evaluation is lazy: Terraform computes them only when needed
  • No dependency cycles: locals cannot reference each other circularly

Also, locals are scoped to a module. You can’t access them from outside—use outputs if you need to expose values.

When Should You Reach for Locals?

Some practical use cases:

  • Standardizing naming conventions across resources
  • Combining multiple variables into a single computed value
  • Building reusable tag maps
  • Preprocessing data for for_each or count
  • Simplifying long expressions inside resources

If you find yourself repeating logic or writing unreadable inline expressions, locals are usually the fix.

A Pattern That Scales Well

In larger Terraform modules, it’s common to group locals logically:

TEXT
1locals {
2  naming = {
3    prefix = "billing"
4    suffix = var.environment
5  }
6
7  computed_names = {
8    bucket = "${local.naming.prefix}-${local.naming.suffix}-bucket"
9    db     = "${local.naming.prefix}-${local.naming.suffix}-db"
10  }
11}

This approach makes your configuration easier to scan and maintain.

Final Thoughts

Terraform locals aren’t flashy, but they’re one of the most effective tools for keeping infrastructure code clean and maintainable. They help you express intent, reduce duplication, and centralize logic without introducing unnecessary complexity.

If your Terraform files are starting to feel cluttered or repetitive, adding a thoughtful locals block is often the simplest way to bring order back.

Comments

Leave a comment on this article with your name, email, and message.

Loading comments...

Similar Articles

More posts from the same category you may want to read next.

Share: