Terraform Locals

Bits Lovers
Written by Bits Lovers on
Terraform Locals

If you have spent any time writing Terraform, you know that your configurations can get messy fast. You end up repeating the same expressions, hard-coding the same values, and before long your code is hard to read and harder to maintain. Terraform locals are one of the simplest tools to fix that.

A good video if you are not familiar with how Terraform works:

Introduction to HashiCorp Terraform with Armon Dadgar

What are Terraform Locals?

Locals are named values you define inside a module. Think of them as temporary variables that exist only within that module. You give a name to an expression, and then you reference that name anywhere else in the same module instead of repeating the full expression.

The main reasons I use locals are to avoid duplicating the same expression in multiple places and to make my code more readable by replacing cryptic inline expressions with descriptive names.


Related posts:

How to use Gitlab to Deploy your Terraform Code.

Do you know How to use Terraform Template?


Terraform Locals vs Variables

This is a common source of confusion, so let me clear it up.

Input variables are like function parameters – they let callers pass values into your module. You set them from the command line, from .tfvars files, or from environment variables.

Locals are different. They are computed internally within the module and cannot be set from the outside. You also cannot change them through Terraform commands like plan, apply, or destroy. They are just named expressions, evaluated at plan time.

If you are coming from a general programming background, here is a mental model that has helped me:

  • Input variables are like function arguments.
  • Output values are like function return values.
  • Locals are like local variables inside a function body.

One important constraint: you can only access a local inside the module where it was declared. Other modules cannot see it.

How to Declare Terraform Locals

You declare locals in a locals block:

locals {
  service_name = "forum"
  owner        = "Community Team"
}

A single locals block can hold as many values as you want, and you can have multiple locals blocks in the same module. Terraform does not care how you organize them.

The expressions are not limited to static strings. They can reference variables, resource attributes, and other locals. For example:

locals {
  instance_ids = concat(aws_instance.blue[*].id, aws_instance.green[*].id)

  common_tags = {
    Service = local.service_name
    Owner   = local.owner
  }
}

Each local name must be unique across the module. The value can be any valid Terraform expression.

Important: You define locals in a locals block (plural), but you reference them as local.<name> (singular). Do not forget to drop the “s” when referencing.

Terraform Locals Example

Here is a practical example. Say you want to create one AWS Elastic IP per availability zone, but only when NAT gateways are enabled:

locals {
  count_nat_gateways = var.is_nat_gateway_enabled ? length(var.availability_zones) : 0
}

resource "aws_eip" "default" {
  count  = local.count_nat_gateways
  domain = "vpc"

  tags = merge(
    module.private_label.tags,
    {
      "Name" = format(
        "%s%s%s",
        module.private_label.id,
        var.delimiter,
        replace(
          element(var.availability_zones, count.index),
          "-",
          var.delimiter
        )
      )
    }
  )

  lifecycle {
    create_before_destroy = true
  }
}

Notice how combining a local with count keeps the resource block clean. The conditional logic is in one place, and the resource just references local.count_nat_gateways.

Note on the domain attribute: If you are using AWS Provider v5 or later, the old vpc = true argument is deprecated. Use domain = "vpc" instead. AWS retired EC2-Classic in August 2023, so all Elastic IPs are VPC-based now.

You can also assign a local value to another local. Just be aware that circular references are not allowed – a local cannot reference itself, directly or indirectly.

Using Locals with for Loops

Locals work well with for expressions. Here are two patterns I use frequently. Locals pair naturally with for_each expressions when building dynamic resource configurations.

Filtering a list:

locals {
  labels = [for l in local.label_order : local.id_context[l] if length(local.id_context[l]) > 0]
}

Transforming a map into a flat list:

locals {
  tags = merge(local.generated_tags, var.tags)

  tags_map = flatten([
    for key in keys(local.tags) : {
      key   = key
      value = local.tags[key]
    }
  ])
}

This second pattern is handy when you need to pass tags to a resource that expects a list of key-value pairs rather than a map.

When to Use Terraform Locals

Locals are great when you find yourself copying the same expression into three or more places. That is the sweet spot. You get a single source of truth and a descriptive name.

But do not overdo it. If you create a local for every single expression, your code becomes harder to follow. Someone reading it has to keep jumping between the resource blocks and the locals block to figure out what is actually going on.

My rule of thumb: use a local when a value appears in multiple places and is likely to change. If it is used once and is self-explanatory, leave it inline.

Conclusion

One last piece of advice: group related locals together in the same block, especially when they depend on each other. It makes the relationships easier to spot. For unrelated locals, use separate blocks and add a comment explaining what each block is for. If you want to dive deeper into how variables and locals interact, check out our post on Terraform variable types.

That covers the basics of Terraform locals. If you have questions or want to share how you use locals in your own projects, leave a comment below.

Boost your Terraform Skills:

How and When to use Terraform Modules?

Do you know what is Terraform Data and How to use it?

Learn How to use Terraform Template, to build dynamic configurations.

Create multiple copies of the same resource using Terraform Count.

Use Terraform Null Resource to run provisioners and local commands without a real resource.

Bits Lovers

Bits Lovers

Professional writer and blogger. Focus on Cloud Computing.

Comments

comments powered by Disqus