Terraform

Terraform Locals: What Are They, How to Use Them [Examples]

Terraform locals

Terraform is not just another open source tool to manage infrastructure. One can define whole infrastructure components and implement the same using a specific coding language called Hashicorp Code Language (HCL). It has a full coding structure within itself, along with its own syntax and terminologies.

For this post, I will focus on a specific, important part of the language. All coding languages have a fundamental concept of ‘variables’ and ‘scopes’. Terraform also has its own version of this called Terraform Locals. There is also a concept of variables on Terraform which can be used to assign dynamic values, but we won’t be covering that in this post. I will instead explain locals and why (and how) they should be used in your Terraform scripts. 

What Are Terraform Locals?

Terraform Locals are named values which can be assigned and used in your code. It mainly serves the purpose of reducing duplication within the Terraform code. When you use Locals in the code, since you are reducing duplication of the same value, you also increase the readability of the code. But how does it differ from a Terraform variable, you may ask. There are a few differences which can be pointed out.  

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 doesn’t 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 that throughout the code instead of using the expression itself at multiple places. 

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. Here is an example of local declaration in a Terraform script:

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

How to Use Terraform Locals?

Now, let’s move on to see how to use a Terraform local. When you use a Terraform local in the code, there are two parts to it:  

  • First, declare the local along and assign a value
  • Then, use the local name anywhere in the code where that value is needed

Let’s look at an example for assigning a local:  

locals {
  bucket_name = "mytest"
  env         = "dev"
}

Here we are assigning two local values. There can be many locals defined within the locals section. These locals are assigned the values to the right. Now, the locals’ name can be used across the code.   

The values assigned to the locals are not limited to just strings or constants. Expression outputs can also be assigned:

locals {
  instance_ids = concat(aws_instance.ec1.*.id, aws_instance.ec3.*.id)
}

Locals can be assigned maps as values. Maps are nothing but a list of key value pairs. Here is an example of assigning a map to the local:

locals {
  env_tags = {
    envname = "dev"
    envteam = "devteam"
  }
}

One thing to note here is that the local name defined has to be unique across the code, because this is the name with which it will be referenced in various places.

Now that we have seen how to declare a local, let’s see how to use one. Since the local has been defined with a specific name, the local can be referenced anywhere with the expression 

local.<declared_name>

Make sure to take into account the fact that the declare block for local is “locals”, and the reference usage is “local”. I have seen this mistake happen many times, so this is something to be careful of.

Now let’s see an example of the usage. I will use the same local defined above for the s3 bucket name, to deploy an s3 bucket:

resource "aws_s3_bucket" "my_test_bucket" {
  bucket = local.bucket_name
  acl    = "private"
 
  tags = {
    Name        = local.bucket_name
    Environment = local.env
  }
}

Here it can be seen that the bucket name has been built using the local name which was defined earlier in an example. When deploying, the local expression will be replaced with the actual value from the declaration block.  

Instead of being used as a static value block as above, it can also be used as an expression to replace a part of a longer text. Here is an example where a local is being used to specify a part of the bucket name:

resource "aws_s3_bucket" "my_test_bucket" {
  bucket = "${local.bucket_name}-newbucket"
  acl    = "private"
 
  tags = {
    Name        = local.bucket_name
    Environment = local.env
  }
}

Combine Terraform Local with Variable

Terraform local is very similar to a variable in a coding language. The difference being that the local is just scoped to the present script which it is a part of. But a local can also be combined with a Terraform variable and used overall in a Terraform script.  

The way it is combined is that the variable which is declared in the script, like in the variables.tf file, can be used to assign a value to the local. Let’s look at an example.

Here 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 is needed to be made more dynamic based on input variable values.

Code Examples for Terraform Local

Here are some examples of using Terraform locals in multiple scenarios.  

The following script declares a Terraform local to define default tag values for Terraform resources. This value is used throughout the script. Here, an IAM role is being created with the tag local used:

locals {
  resource_tags = {
    project_name = "mytest",
    category     = "devresource"
  }
}
 
resource "aws_iam_role" "myrole" {
  name = "my_role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "s3.amazonaws.com"
        }
      },
    ]
  })
 
  tags = local.resource_tags
}

If you want to know more about destroying resources from Terraform, see: How to Destroy Resources from Terraform: Tutorial and Examples

In this below example, the local value is being merged with a Terraform variable value. The merged value is used in the script. The output value is also defined to output the tag values:  

variable "res_tags" {
  type = map(string)
  default = {
    dept = "finance",
    type = "app"
  }
}
 
locals {
  all_tags = {
    env       = "dev",
    terraform = true
  }
  applied_tags = merge(var.res_tags, local.all_tags)
}
 
 
resource "aws_s3_bucket" "tagsbucket" {
  bucket = "tags-bucket"
  acl    = "private"
 
  tags = local.applied_tags
}
 
 
output "out_tags" {
  value = local.applied_tags
}

Key Points

This post should give you a good perspective of using Locals in Terraform. It is recommended to use Locals generously in the code to avoid repetitions in the code. Additionally, the locals will help make the code more readable. All the best with your Terraform explorations!

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.

Start free trial