Terraform Locals As Named Module Scoped Values For Reuse And Clarity

Terraform locals are a core mechanism for expressing and storing values within the context of a code block. In the case of Terraform configurations, that functionality is delivered through Terraform local values. These allow you to define temporary values and then reference them elsewhere in the configuration. Local values, often called locals or Terraform local variables, can be used to store an expression that will be referenced multiple times, perform data transformation from other sources, or store static values to be used in the configuration.

Locals are one of three varieties of Terraform variables that can be used to request or public values, with the other two being input variables and output values. Locals are named, module-scoped values that let you assign an expression once and reuse it throughout your configuration, reducing duplication and making your code easier to read and maintain. If you have ever found yourself repeating the same tag block, name prefix, or computed value across multiple resources, locals are the solution. Unlike input variables, locals can be not overridden from outside the module, which makes them ideal for intermediate values derived from other resources, data sources, or expressions.

Locals are pure computed expressions inside a module, no provider calls, deterministic given inputs, used for reuse and clarity, not overrideable by callers. When you use locals in the code, since you are reducing duplication of the same value, you also increase the readability of the code. If you want to compare Terraform local to a general programming language construct, it will be equivalent to a local temporary variable declared within a function.

What Terraform Locals Are And Why They Exist

Terraform locals are named values that can be assigned and used in your code. They mainly serve the purpose of reducing duplication within the Terraform code. Terraform locals allow you to define values that are computed from expressions. You can use locals to simplify your infrastructure code and make it more readable.

Using Terraform locals means you define a value or expression once and reference it everywhere it is needed. If it changes, you update it in one place instead of hunting down every occurrence across your configuration. The real world consequence for an operator is that a single change to a naming convention or tag set propagates consistently across dozens of resources without manual search and replace, which reduces drift risk and human error.

Locals are often stored in a file called locals.tf. There is no strict requirement for this, however it could be considered a best practice for organizing your Terraform code, especially in larger projects to help keep your code readable, and maintainable. Centralizing locals in a dedicated file creates a clear contract for computed constants inside a module and allows team members to locate reusable values quickly.

How To Implement Terraform Locals

Defining local values in Terraform code is done using a locals block, with each local assigned a name and value in the format of a key-value pair.

locals { environment = "development" }

The following code creates a local value called environment and assigns it the string value development.

Locals can be assigned any valid Terraform data type, such as a string, a list, a map, or an object. Type flexibility means a local can hold a complex object built from multiple inputs and then be consumed by several resources without re-declaring the construction logic.

You can reference these locals later in your Terraform code by using the ${local.region} and ${local.ami} syntax. Locals are declared with the locals block plural but referenced with local.name singular. This is the most common source of confusion.

locals { region = "us-west-2" ami = data.aws_ami.ubuntu.id }

In this example, we define a local called region that has a value of us-west-2. We also define a local called ami that references the ID of an Ubuntu AMI from a data resource. You can reference these locals later in your Terraform code by using the ${local.region} and ${local.ami} syntax.

Syntax Rules And Scope Behavior

Locals are named, module-scoped values. Scope means a local is only accessible within the local module vs a Terraform variable, which can be scoped globally. Module scoping prevents accidental leakage of internal computation details to callers and enforces encapsulation.

Another thing to note is that a local in Terraform does not change its value once assigned. A variable value can be manipulated via expressions. This makes it easier to assign expression outputs to locals and use them throughout the code instead of using the expression itself at multiple places.

Locals can be assigned any valid Terraform data type. The table below summarizes the supported type categories.

Type Category Example Use In Locals Impact
string environment = "development" Enables consistent naming and environment tagging
list subnet_ids = [...] Allows iteration over collections without repetition
map tags = { Owner = "team" } Centralizes tag sets for multiple resources
object config = { a = 1, b = 2 } Encapsulates structured configuration for reuse

Locals fully support built-in functions, so you can use expressions like merge(), concat(), format(), or toset() directly in a local declaration and reference the result throughout your module.

locals { bucket_name = "${var.text1}-${var.text2}" }

When you use locals in the code, since you are reducing duplication of the same value, you also increase the readability of the code. If you want to compare Terraform local to a general programming language construct, it will be equivalent to a local temporary variable declared within a function.

How Locals Differ From Input Variables And Data Sources

How does Terraform local differ from a Terraform variable?

The first difference can be pointed towards the scope. A Local is only accessible within the local module vs a Terraform variable, which can be scoped globally.

Another thing to note is that a local in Terraform does not change its value once assigned. A variable value can be manipulated via expressions. This makes it easier to assign expression outputs to locals and use them throughout the code instead of using the expression itself at multiple places.

Locals are declared with the locals block plural but referenced with local.name singular. This is the most common source of confusion.

Input variables can be overridden from outside the module. Locals cannot be overridden from outside the module, which makes them ideal for intermediate values derived from other resources, data sources, or expressions.

Data resources in Terraform allow you to retrieve data from an external data source, such as an AWS EC2 instance, an AWS S3 bucket, or a DNS record. You can use data resources to obtain information that is required to provision infrastructure resources. Here is an example of how you can define a data resource in Terraform.

Using a data resource, over using a fixed value or even an input, gives you an advantage when wanting to keep your infrastructure up-to-date.

The difference between data and locals in Terraform is that data sources fetch read-only information from providers or remote state during planning or apply, so values can change outside Terraform and require provider access. Locals are pure computed expressions inside a module, no provider calls, deterministic given inputs, used for reuse and clarity, not overrideable by callers.

Combining Locals With Variables For Dynamic Values

A variable is declared in the variables.tf file in a Terraform module:

variable "bucket_prefix" { type = string default = "mybucketname" }

This variable can be used as a default value to the local in the Terraform script:

locals { bucket_name = "${var.bucket_prefix}-bucket1" }

resource "aws_s3_bucket" "my_test_bucket" { bucket = local.bucket_name acl = "private" }

As you can see, a local can be easily combined with a Terraform variable to create complicated default value expressions for the Terraform local. This is very useful for scenarios where the local needs to be made more dynamic based on input variable values.

The impact of combining locals with variables is that the module interface remains stable through input variables while internal naming logic remains encapsulated in locals. Callers control the prefix, the module controls the suffix and formatting.

The table below contrasts locals and tfvars.

Aspect Locals Tfvars
Purpose Computed constants inside a module, derived from expressions and not overrideable by callers Provide values for input variables from outside the module
Override Not overrideable Enables environment specific overrides through Terraform variable precedence
Scope Module internal Cross module input
Separation Logic stays in code Clean separation of config from code

What is the difference between locals and tfvars? Locals are computed constants inside a module, derived from expressions and not overrideable by callers, useful for keeping logic DRY and readable. Tfvars files provide values for input variables from outside the module, enabling environment specific overrides through Terraform variable precedence and clean separation of config from code.

Practical Usage Patterns And Organization

You can also have multiple locals blocks defined in the same configuration or module, Terraform will handle them out for you, but you cannot have multiple local variables with the same name, even though they are in a different locals block.

Locals are often stored in a file called locals.tf. There is no strict requirement for this, however it could be considered a best practice for organizing your Terraform code, especially in larger projects to help keep your code readable, and maintainable.

The following script declares a Terraform local to define default tag values for Terraform resources. This value is used throughout the script.

The practical impact is that tag updates become a single edit point. When compliance requirements change, the operator edits the locals block once and all resources inherit the new tags on the next apply, avoiding inconsistent tagging.

A common pattern is name prefix reuse:

locals { name_prefix = "${var.project}-${var.environment}" }

Resources then reference local.name_prefix, guaranteeing consistent naming across services.

Common Errors And Constraints

Locals are declared with the locals block plural but referenced with local.name singular. This is the most common source of confusion.

Locals cannot be overridden from outside the module. Attempting to set a local via an input variable will fail because locals are module-scoped and non-overrideable.

You cannot have multiple local variables with the same name, even though they are in a different locals block. Terraform will error on duplicate definition, protecting against silent shadowing.

Locals are pure computed expressions inside a module, no provider calls. This means a local cannot directly call a provider API. Data must be fetched via data resources and then referenced into locals.

Frequently Asked Concepts

What is a local module in Terraform? A local module in Terraform is a reusable module loaded from your repo via a filesystem path, not the Registry or a remote VCS. Reference it with a path in the source field, for example source = "./modules/network". It enables reuse without versioning boundaries, so callers pick up changes immediately.

OpenTofu is an open-source version of Terraform that expands on Terraform's existing concepts and offerings. It is a viable alternative to HashiCorp's Terraform, being forked from Terraform version 1.5.6.

Terraform Management Made Easy: Spacelift effectively manages Terraform state, more complex workflows, supports policy as code, programmatic configuration, context sharing, drift detection, resource visualization and includes many more features.

Conclusion

Terraform locals provide a mechanism to assign an expression once and reuse it throughout a module, reducing duplication and improving readability. Locals are module-scoped, non-overrideable computed values that can hold strings, lists, maps, and objects, and they fully support built-in functions for transformation.

The impact of locals is felt in maintainability, consistency, and safety. By centralizing naming conventions, tag sets, and derived identifiers in a locals block, teams reduce the number of edit locations, limit drift, and keep internal logic encapsulated from callers. Combining locals with input variables allows dynamic yet controlled configuration, while the strict scope rules prevent accidental external mutation.

Locals coexist with input variables, output values, and data resources, each with distinct responsibilities. Variables provide external configurability, outputs expose results, data resources fetch external state, and locals provide internal reuse. Understanding the boundary between these constructs prevents misuse and supports a clean separation of code and configuration.

In larger projects, placing locals in a dedicated locals.tf file, avoiding duplicate names across multiple locals blocks, and referencing them with the correct local.name syntax preserves readability and prevents common errors. The module-scoped nature of locals ensures that intermediate computations remain deterministic and hidden from callers, which is the core design intent behind Terraform locals.

Sources

  1. How to Manage Terraform Locals
  2. Data and Locals
  3. Terraform Locals

Related Posts