What are Terraform Locals
All programming languages have a way to express and store 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.” In this post, we'll examine how to define local values, how they differ from input variables, and common uses for locals.
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. The following code creates a local value called environment
and assigns it the string value development
.
locals {
environment = "development"
}
Locals can be assigned any valid Terraform data type, such as a string
, list
, map
, or object
. The actual values can come from input variables, resource attributes, or other local values defined in the configuration.
locals {
environment = "development"
server_list = ["web", "app", "db"]
subnet_map = {
web = var.web_subnet
app = var.app_subnet
db = var.db_subnet
}
}
A locals
block is also useful for composing expressions for values that will be used elsewhere in the configuration, including in both ternary and for
expressions. The following example sets the DNS value to the load balancer fqdn
if a load balancer is being created, and to the fqdn
of a virtual machine otherwise.
locals {
dns_entry = var.create_load_balancer ?
azurerm_public_ip.lb.fqdn : azurerm_public_ip.vm.fqdn
}
A for
expression could be used to create a local value for naming resources. The code below would add a naming prefix input variable to the beginning of each server in the list.
locals {
server_list = ["web", "app", "db"]
server_names = [ for svr in local.server_list : "${var.prefix}-${svr}" ]
}
The locals
block can appear multiple times in a Terraform configuration, so long as each local value has a unique name within the configuration.
Some organizations prefer to define all their locals in a single locals
block stored in a dedicated locals.tf file, while others will use multiple locals blocks, placing each close to the resources and data sources referencing it.
The value stored in a local is referenced with the local keyword followed by the name of the local. The following code makes use of the server_names
and environment
locals in an Azure VM.
resource "azurerm_linux_virtual_machine" "web" {
name = local.server_names[0]
#...
tags = {
environment = local.environment
}
}
Locals defined in a Terraform module are only available within the scope of that module. For instance, the local value named environment
that is defined in the root module cannot be referenced directly by a child module.
Additionally, locals defined in the child module are not directly available to the parent module. Locals follow the same scoping principles as input variables, resources, and data sources.
Terraform Locals vs. Input Variables
While Terraform input variables and local values might sound very similar, there are some key differences to keep in mind when selecting which construct to use.
Input Variables
- Dynamic values defined at run time
- Values cannot come from other configuration sources
- Default value can be overridden
Local Values
- Defined by an internal expression
- Can be assigned any valid Terraform expression
- Can be assigned static values
In most other programming languages, input variables would instead be called “parameters” and local values would simply be called “variables.”
If you are coming from another programming language, that may be a helpful mental framework to apply when thinking about whether to use an input variable or local value.
Generally, if you need to be able to change the value of a placeholder at runtime, you should use an input variable. If you simply need a reusable expression or need to perform data transformation, you should use a local value.
Examples of Terraform Locals
Let's walk through a few common examples of how and when Terraform locals might be used.
1. Reusing a Value
Locals allow you to define a value once and reuse it throughout the configuration. For instance, we can use a local value to define a set of common tags for your resources based on input variables.
locals {
common_tags = {
environment = var.environment
project = var.project
billing = var.billingcode
}
}
resource "azurerm_resource_group" "main" {
...
tags = local.common_tags
}
By defining the common tags in a locals
block, it is simple to add a new tag to all resources with having to update every tags
argument in every resource block.
Another common use case is establishing a standard naming convention for all resources in a configuration. The Cloud Posse terraform-null-label module makes extensive use of locals for exactly that purpose.
2. Data Transformation
Locals can also be used to transform data before it is passed on in the configuration. For instance, you could use a local variable and a for expression to update the values in a list.
locals {
env_config_list = [ for item in var.config_list : "${local.environment}-${item}"
}
Even if the local value isn't going to be used multiple times, performing the data transformation in a locals
block can help make other configuration blocks easier to read. Locals can also be used as an intermediary step in data transformation, to simplify code updates for future maintainers.
3. Constant Values
Unlike input variables, locals don't accept input values, so they can be used to set static constants in the configuration that can only be updated by altering the code itself. For instance, the code below establishes default ports for a web application.
locals {
web_app_ports = ["8080", "8443"]
}
If someone wishes to alter the port list, they will need to go through the code change process, rather than changing the input variable values submitted during a Terraform run. Using locals helps code maintainers view all the configuration values in a single place, while not allowing consumers of the configuration to change values easily.
Conclusion
Locals in Terraform configuration files construct reusable values to be referenced throughout a configuration. Values can be assigned to locals from input variables, resources, data sources, and other related local values.
Local values are a core component of Terraform and are also supported on the new open-source project OpenTofu. OpenTofu is intended to be a drop-in replacement for existing Terraform deployments, and its support of locals is one example of its interoperability. You can join OpenTofu’s Slack community and check out how to contribute.
Top comments (1)
Thank you for the article; I enjoyed it. I generally prefer to place local blocks close to the resources that are using them if the number of variables is limited. However, when local variables start to be used across multiple files or there are too many of them, I move them to their own file.
One situation where it may be necessary to use local variables is to avoid a circular dependency. For example, the following code would cause a circular dependency because resources a and b reference each other:
Using a local variable can break this dependency: