Terraform local values (aka locals) enable the creation of expressions or values that can be easily referenced within the Terraform project or Terraform module. Using local values helps eliminate the duplication of hard coded values that are used multiple times throughout the Terraform project. The easy way to use local values is to create multiple locals for each expression or value needed. When the project requires using multiple locals that are related there a couple techniques to keep in mind to make the code easier to read, understand, and more organized.


When to use locals

Before defining local values (aka locals) in a Terraform project or module, the reasons to use local values needs to be defined first. How do you know if a local value is necessary?

There are a few simple rules to follow when determining whether to hard code values in the code or to create local values (locals) so they are reusable in the Terraform code:

  • If the value is going to be used multiple times within the code, then a local value will reduce duplication of the hard coded value.
  • If the value needs to be created programmatically (using string concatenation or other expression methods) then it’ll make it easier to maintain the code by separating the value expression from the code that uses it.
  • Sometimes it may be easier to consolidate the collection of configuration values in the code to a single place to make everything easier to maintain over time. Creating local values to define these values for later use will help simplify maintenance and increase human readability of the code.

Keep in mind that these are all pretty much the same reasons any programming keeps in mind when deciding whether to hard code values or create variable holding that same value.

Related: There are times when it’s most appropriate to parameterize a Terraform project or to enable passing input variable values to a Terraform module. Reference the following article for more information on defining and using input variables in Terraform: Use Terraform Input Variables to Parameterize Infrastructure Deployments

Organize locals with Name Separators

The technique for using name separators for naming local expressions and values is essentially using the same name prefix to name all the locals that are related. This keeps the names similar and easy to understand what purpose the expressions and values are for. When naming locals in this way, it is common to separate each part of the names with an underscore (_) character. Another lesser common separator character is the hyphen (-) character.

When using Terraform to provision cloud resources, like Microsoft Azure resources, a common locals value to define is the “location” or Azure Region (aka cloud region or datacenter.)

locals {
  location = "eastus"
}

When there are additional expressions or values that need to be defined for use in the Terraform project that relate to location then the names of those locals can be prefixed with the location local value name. The following is an example of this with a local value for defining an abbreviated location and a naming prefix for resources.

locals {
  location             = "eastus"
  location_abbr        = "E1"
  location_resource_name_prefix = "${local.location_abbr}-"
  # value of location_resource_name_prefix will be "E1-"
}

The name separator technique become even more useful with more complex local value names. An example of this would be when multiple cloud locations (or Azure Regions) are used when provisioning resources. This creates another level of hierarchy to the name separator and makes following a naming technique like this even more useful.

The following is an example of local expressions and values like the previous examples with multiple locations or Azure Regions added, and an additional “tag” value:

locals {
  primary_location             = "eastus"
  primary_location_abbr        = "E1"
  primary_location_resource_name_prefix = "${local.primary_location_abbr}-"
  primary_location_tags        = {
    source = "terraform"
  }

  secondary_location             = "centralus"
  secondary_location_abbr        = "C1"
  secondary_location_resource_name_prefix = "${local.secondary_location_abbr}-"
  secondary_location_tags        = {
    source = "terraform"
  }
}

Organize locals with Object Maps

When the Terraform code contains many local values (aka locals) it can get cumbersome to manage all those values. Organizing the local values on some type of grouping can really improve the overall management of all the values. While name separators do help, it can remain a bit cumbersome to rely solely on name separators for organizing the long list of local values within a Terraform project.

The use of local values with the value equal to an object map with nested values can really help to better organize all the values in a Terraform project. Object maps are like an object with properties in programming terms and can contain multiple nested levels of values within.

The following is an example of using a local values of type Object Map to better organize the many values within a Terraform project or module:

locals {
  # Primary Region
  primary = {
    location      = "eastus"
    location_abbr = "E1"
    resource_name_prefix = "E1-"
    tags = {
      source = "terraform"
    }
  }
  # Secondary Region
  secondary = {
    location      = "centralus"
    location_abbr = "C1"
    resource_name_prefix = "C1-"
    tags = {
      source = "terraform"
    }
  }
}

Notice the location_abbr value nested within each of the primary and secondary local object map values are not using an expression like in the name separator example. The reason for this is because the full object map value for any locals defined this way are not able to use expressions that reference nested object map values of the local object map value that defines it. This limitation appears to be caused by the Terraform interpreter parsing the language top to bottom, and it can’t use an expression that references a value that’s not defined yet. To overcome this limitation a local value outside the object map can be used for any values that need to be referenced multiple times within the object map to eliminate duplicated hard coded values.

The following is the same locals Object Map value example with additional local values to help eliminate duplicated hard coded values:

locals {
  # Primary Region
  primary_location_abbr = "E1"
  primary = {
    location      = "eastus"
    location_abbr = local.primary_location_abbr
    resource_name_prefix = "${local.primary_location_abbr}-"
    tags = {
      source = "terraform"
    }
  }
  # Secondary Region
  secondary_location_abbr = "C1"
  secondary = {
    location      = "centralus"
    location_abbr = local.secondary_location_abbr
    resource_name_prefix = "${local.secondary_location_abbr}-"
    tags = {
      source = "terraform"
    }
  }
}

Referencing local values that are defined using object maps is done by using a period (.) to access the values at each nested level of the object map. Just like how the local values are referenced using a period on the local keyword itself. In programming terms this is like accessing any nested object values in most other programming languages.

The following is an example of referencing some of the local object map values within Terraform resource or module code blocks:

module "primary_my_module" {
  source = "./my-module"
  location = local.primary.location
  name     = "${local.primary.resource_name_prefix}resource-name"
}

resource azurerm_resource_group "primary_resource_group" {
  location = local.primary.location
  name     = "${local.primary.resource_name_prefix}resource-group"
  tags     = local.primary.tags
}

Wrap Up

The use of local values (aka locals) in Terraform projects and modules help to write cleaner infrastructure code. It also helps to reduce the number of duplicated values and expressions. However, proper organization of local values becomes increasingly important the larger and more complicated the Terraform code base becomes. Utilizing techniques of organization local values will help reduce the complexity and ease the maintenance of Terraform code over time.

Happy Terraforming!

Microsoft MVP

Chris Pietschmann is a Microsoft MVP (Azure & IoT) and HashiCorp Ambassador (2021) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to Fortune 100. He is also a Microsoft Certified Azure Solutions Architect and developer, a Microsoft Certified Trainer (MCT), and Cloud Advocate. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.