TrademarkTrademark
Features
Documentation
  1. Learning Center
  2. Terraform tfvars: A Practical Guide to .tfvars Files and Variables

Article ยท part of a guide

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

What Terraform local values are, how they differ from variables, and when to use them.

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

Key takeaways

  1. Terraform locals assign a name to the result of an expression so it can be reused throughout a configuration instead of repeating the same expression.
  2. Unlike variables, local values are set within the configuration rather than by user input or tfvars files, and they can use dynamic expressions and resource arguments.
  3. Common uses for locals include enforcing naming conventions with a prefix, merging mandatory and resource-specific tags, and combining values from variables, data sources, and modules.
  4. Locals should be used in moderation because overusing them hides the underlying value and makes the configuration harder to follow.
  5. Locals organize expressions inside a module, but enforcing standards across many teams and workspaces is better handled with policy-as-code on a Terraform management platform.

Terraform local values, usually just called locals, are easy to reach for and easy to overuse. This post explains what they are, how they differ from variables, and when one pays off, with a few worked examples.

What are Terraform local values?

A local lets you give a name to the result of an expression and then reuse that result wherever you need it. You set the value once and reference it by name, instead of writing out the same expression in several places.

There's a cost to that, though. Locals are easy to overuse, and when you do, the configuration gets harder to follow. The reader no longer sees the actual value or expression at the point of use. It's 'hidden' behind the name, and anyone working on the code later has to chase it back to the locals block. Use them in moderation.

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

Local values aren't set by user input or values in .tfvars files. They're set 'locally' to the configuration, which is where the name comes from. Instead of hardcoding a value, a local can produce something more meaningful or readable. The main reason to reach for one is to keep values that are likely to change in one place. And unlike variables, locals can use dynamic expressions and resource arguments.

These values live alongside the configuration, and in a well-organized module a contributor should be able to find them quickly. In my experience, if a module uses local values, it's worth putting them in a separate file called locals.tf so they're easy to reference. That's a convention, not a rule. Locals can go anywhere in your Terraform configuration files.

Local values don't change between Terraform runs or stages in the lifecycle, such as plan, apply or destroy.

Example: Using locals to name resources

Naming resources is a good place to start. Say you want every resource to follow one naming convention with a fixed prefix.

In the example below, we define name_prefix in the locals block. It combines "JacksDemo-" with the contents of the environment variable, giving something like "JacksDemo-Prod". We then use that prefix to name an application gateway, together with 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

Say you have a set of tags that must go on every resource, plus a few tags that only apply to one resource. With locals, you define the mandatory tags once and then merge them with the resource-specific ones to get the final set. The user can still add their own tags, but the required set always comes along.

In the example below, the mandatory tags include cost_center and environment, which get merged with the 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
  }
}
 
resource "aws_instance" "example" {
  # ... other configuration ...
  tags = merge(var.resource_tags, local.mandatory_tags)
}

Example: Using locals with Azure Application Gateway

This example uses 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 data source blocks are shown for completeness. They're pulling the certificate from an Azure Key Vault and applying it to the Application Gateway (you'll 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 pull their weight in a Terraform configuration when you use them with some restraint. The clearest case for a local is a value or expression that shows up in several places, so naming it once keeps the code consistent and saves you from repeating yourself.

Locals keep values consistent within a single configuration, but enforcing standards like mandatory tags and naming conventions across many teams and workspaces is a governance problem better solved with policy-as-code on a dedicated Terraform management platform.

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 joins several strings into one. You'll use it to build dynamic resource names and keep naming patterns consistent.

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 tags map is a local map that defines common resource tags in one place, so they're 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

Conditional logic in locals uses "if-then-else" style expressions to pick a value based on a condition.

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

You can define a list in locals once and use it 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

A map transformation builds a new map from an existing one. You iterate over the original map's key-value pairs and change the keys, the values, or both, or 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

Terraform's built-in string functions modify and transform string values in your configuration. You'll use them to build dynamic resource names and produce specific output formats.

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

Locals can also do basic arithmetic with operators and functions, directly in your 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)
}

Where do locals stop and variables begin?

Locals organize expressions inside a module. Values that differ per environment still come from variables and tfvars files, which our tfvars guide covers. Once those values span dozens of workspaces, centrally scoped variables in a platform like Scalr (free up to 50 runs per month) replace copy-pasted tfvars files.

About the author

Jack Roper

DevOps engineer

Jack Roper is a DevOps engineer and technical writer specializing in Terraform and cloud infrastructure.

Part of this guide

6 sheets

Terraform tfvars: A Practical Guide to .tfvars Files and Variables

5 articles