Best Practices
Best Practices
May 23, 2025

When Should You Use Terraform Locals? Some Real-World Use Cases

By
Jack Roper

Last updated: May 23, 2025

In this article, we will dive into Terraform local values (sometimes referred to as locals). We will discuss what Terraform local values are, how they differ from variables, and when to use them with some examples.

What are Terraform local values?

Locals are useful to use when you want to give the result of an expression and then re-use that result throughout your configuration.

They can help simplify the configuration by enabling the values to be set once and then referenced throughout your configuration, rather than calling the same expression multiple times in the configuration.

Terraform locals should be used with caution, as using them too often or unnecessarily can cause the configuration to become harder to follow as instead of displaying the actual value or expression used, this is ‘hidden’ and needs to be tracked through the code by any future contributors. As such they should be used in moderation.

How do locals differ from variables and hard-coded values in Terraform?

Locals values are not set by the user input or values in .tfvars files, instead, they are set ‘locally’ to the configuration (hence the name). Instead of hardcoding values, local values can produce a more meaningful or readable result. The ability to change values that are likely to change in the future is the key benefit of using Terraform locals. Unlike variable values, local values can use dynamic expressions and resource arguments.

These values can be stored centrally and locally to the configuration. If the configuration is well organized, they should be easily found by a contributor. In my experience, if your module contains the use of local values, I recommend putting these in a separate file called locals.tf so they can be easily referenced. This is not a hard requirement however, they can be used anywhere in your Terraform configuration files.

Locals values do not change between Terraform runs or stages in the lifecycle, such as plan, apply or destroy.

Example — Using locals to name resources

Locals can be useful when naming resources. Consider you want to enforce a standard naming convention across your resources, with a defined prefix.

In the below example, we define name_prefix in our locals block, that adds “JacksDemo-” plus the contents of the environment variable, e.g. “JacksDemo-Prod”. This is then used to name an application gateway, in combination with the var.application_gateway_name.


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

module "appgateway" {
  application_gateway_name    = "${local.name_prefix}-${var.application_gateway_name}"
  ...
}

Example — Using locals to set resource tags

Consider we have a mandatory set of tags that need to be set on every resource, as well as a set of tags that apply just to the resource itself. Using locals, we can set the mandatory tags, and then merge them with the resource-specific tags, to give us the final required set of tags. This affords the user the flexibility to add their own tags, whilst also mandating a required set.

In the below example, mandatory tags include the cost_center and environment which will then be merged with those tags defined in the resource_tags variable.

You can use the merge function to combine local values with variable values.


locals {
    mandatory_tags = {
      cost_center = var.cost_center,
      environment = var.environment
    }
 }
 
 tags = merge(var.resource_tags, local.mandatory_tags)

Example — Using locals with Azure Application Gateway

In this example, we will use locals to configure an Azure Application Gateway.


locals {

  gateway_ip_configuration = [
    {
      name      = var.gateway_ip_configuration_name
      subnet_id = module.vnet.subnet01_id
    }
  ]

  ssl_certificate = [
    {
      name     = var.ssl_certificate_name
      data     = data.azurerm_key_vault_secret.certificate.value
      password = var.ssl_certificate_password
    }
  ]

  authentication_certificate = [
    {
      name = var.authentication_certificate_name
      data = data.azurerm_key_vault_secret.authentication_certificate.value
    }
  ]
    
  mandatory_tags = {
    cost_center = var.cost_center,
    environment = var.environment
  }

  name_prefix = "JacksDemo-${var.environment}"
}
  
###### Current Config ######
data "azurerm_client_config" "current" {}

###### Key Vault Certificate ######
data "azurerm_key_vault_secret" "certificate" {
  name         = var.ssl_certificate_name
  key_vault_id = module.kv.kv_id

  depends_on = [module.kv]
}

###### Key Vault authentication_certificate - For HTTPS HTTP Settings ######
data "azurerm_key_vault_secret" "authentication_certificate" {
  name         = var.authentication_certificate_name
  key_vault_id = module.kv.kv_id

  depends_on = [module.kv]
}
  
### Application Gateway ###
module "appgateway" {
  source                      = "./../appgwv2"
  application_gateway_name    = "${local.name_prefix}-${var.application_gateway_name}"
  resource_group_name         = var.rg_name
  location                    = var.rg_location
  frontend_port               = var.frontend_port
  gateway_ip_configuration    = local.gateway_ip_configuration
  autoscale_configuration     = var.autoscale_configuration
  sku                         = var.agw_sku
  ssl_certificate             = local.ssl_certificate
  authentication_certificate  = local.authentication_certificate
  subnetId                    = module.vnet.subnet01_id
  frontendPrivateIpConfigName = var.frontendPrivateIpConfigName
  agw_private_ip_addr         = var.agw_private_ip_addr
  backend_address_pool        = var.backend_address_pool
  backend_http_settings       = var.backend_http_settings
  http_listener               = var.http_listener
  probe                       = var.probe
  url_path_map                = var.url_path_map
  rewrite_rule_set            = var.rewrite_rule_set
  request_routing_rule        = var.request_routing_rule
  redirect_configuration      = var.redirect_configuration
  tags                        = merge(var.resource_tags, local.mandatory_tags)
  depends_on                  = [module.vnet, module.kv]
}

First, we define our locals block, containing gateway_ip_configuration , ssl_certificate and authentication_certificate.

Note these take a combination of values from variables, data sources, and modules, not something we would be able to define in a single variable or hardcode.

The relevant data source blocks are shown for completeness, they are basically pulling the certificate from an Azure Key Vault and applying it to the Application Gateway (you will also need a key vault access policy and managed identity to set this up in practice).

The local values we defined can then be passed into our appgateway module, on lines 59, 62, and 63. Note we are also using the naming prefix and mandatory_tags from the previous examples on lines 55 and 75.

Summary

Local values are a useful part of a Terraform configuration. They should be used only when necessary, and are best used when a repeated value is used throughout the configuration.

More Samples:

The following samples were added by Scalr, not the original author.

Simple Terraform Locals Examples

Here are some straightforward examples of Terraform locals:

1. Basic String Concatenation

Basic string concatenation in Terraform is the process of combining multiple strings together to form a single string. This is a fundamental technique that helps you create dynamic resource names, consistent naming patterns, and organized infrastructure.

String concatenation in Terraform is typically done using the string interpolation syntax with the ${} notation inside a string.


locals {
  # Simple name prefix combining project and environment
  name_prefix = "${var.project}-${var.environment}"
}

resource "aws_s3_bucket" "example" {
  bucket = "${local.name_prefix}-bucket"
}
2. Simple Map for Tags

A "Simple Map for Tags" in Terraform refers to using a local map variable to define common resource tags in one place, making them easy to apply consistently across multiple resources.


locals {
  # Common tags in one place
  tags = {
    Project     = var.project
    Environment = var.environment
    Owner       = "DevOps"
  }
}

resource "aws_instance" "example" {
  ami           = "ami-123456"
  instance_type = "t2.micro"
  tags          = local.tags
}
3. Basic Conditional Logic

Basic conditional logic in Terraform locals involves using "if-then-else" style expressions to make decisions about what values to use based on certain conditions.

Terraform's conditional expressions use the syntax condition ? true_value : false_value, which means:

  • If the condition is true, use the first value
  • If the condition is false, use the second value

This is also known as a "ternary operator" and lets you set different values based on conditions like environment type, region, or any other variable.


locals {
  # Simple boolean check
  is_production = var.environment == "prod"
  
  # Set value based on environment
  instance_size = local.is_production ? "t2.medium" : "t2.micro"
}

resource "aws_instance" "server" {
  instance_type = local.instance_size
  ami           = "ami-123456"
}
4. Simple List Creation

Simple list creation in Terraform locals involves defining and manipulating collections of values that can be used consistently throughout your configuration.

Lists in Terraform are ordered collections of values, similar to arrays in other programming languages. When used with locals, they allow you to:

  1. Define collections of related values in one place
  2. Manipulate those collections with built-in functions
  3. Reference list elements by their index position
  4. Iterate over lists using count or for_each

locals {
  # Create a list of availability zones
  azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
  
  # Get first two zones
  selected_azs = slice(local.azs, 0, 2)
}

resource "aws_subnet" "example" {
  count             = length(local.selected_azs)
  vpc_id            = aws_vpc.main.id
  availability_zone = local.selected_azs[count.index]
  cidr_block        = "10.0.${count.index}.0/24"
}
5. Simple Map Transformation

Simple map transformation is the process of creating a new map by modifying an existing map in straightforward ways. This usually involves iterating over the original map's elements (key-value pairs) and applying some logic to change the keys, the values, or both, or to filter out certain pairs.


locals {
  # Original map
  services = {
    web = { port = 80 }
    api = { port = 8080 }
  }
  
  # Add prefix to each key
  prefixed_services = {
    for name, config in local.services :
    "${var.project}-${name}" => config
  }
}
6. Basic String Manipulation

Basic string manipulation in Terraform involves using built-in functions to modify and transform string values within your configuration files. These manipulations are essential for constructing dynamic resource names, generating specific output formats, and managing data effectively.


locals {
  # Convert to lowercase
  environment = lower(var.environment)
  
  # Join strings with separator
  tags_csv = join(",", ["env=${var.environment}", "project=${var.project}"])
}
7. Default Value Assignment

locals {
  # Use provided region or default to us-east-1
  region = var.region != "" ? var.region : "us-east-1"
}

provider "aws" {
  region = local.region
}
8. Simple Numeric Calculation

Simple numeric calculations involve using operators and functions to perform these basic mathematical tasks directly within your code or configuration.


locals {
  # Calculate number of instances
  instance_count = var.environment == "prod" ? 3 : 1
  
  # Calculate disk size
  disk_size_gb = var.base_disk_size + (var.environment == "prod" ? 50 : 0)
}
Note: Check out our new Learning Center here for technical guides and how-tos.

Your costs = usage. Period.

A screenshot of the modules page in the Scalr Platform