Terraform
Simplify Terraform configuration with locals
Terraform local values (or "locals") assign a name to an expression or value. Using locals simplifies your Terraform configuration – since you can reference the local multiple times, you reduce duplication in your code. Locals can also help you write more readable configuration by using meaningful names rather than hard-coding values.
Unlike variables found in programming languages, Terraform's locals do not change values during or between Terraform runs such as plan, apply, or destroy. You can use locals to give a name to the result of any Terraform expression, and re-use that name throughout your configuration. Unlike input variables, locals are not set directly by users of your configuration.
In this tutorial, you will use Terraform to deploy a web application on AWS. The supporting infrastructure includes a VPC, load balancer, and EC2 instances. You will then use local values to reduce repetition in the configuration, and then combine local values with input variables to require a minimal set of resource tags while still allowing for user customization.
Prerequisites
You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.
Select the HCP Terraform tab to complete this tutorial using HCP Terraform.
This tutorial assumes that you are familiar with the Terraform workflow. If you are new to Terraform, complete the Get Started tutorials first.
In order to complete this tutorial, you will need the following:
- Terraform v1.1+ installed locally.
- An AWS account with local credentials configured for use with Terraform.
Note
Some of the infrastructure in this tutorial may not qualify for the AWS free tier. Destroy the infrastructure at the end of the guide to avoid unnecessary charges. We are not responsible for any charges that you incur.
Clone example repository
Clone the example repository for this tutorial. The configuration in this repository defines a web application, VPC, load balancer, and EC2 instances.
$ git clone https://github.com/hashicorp-education/learn-terraform-locals
Change to the repository directory.
$ cd learn-terraform-locals
Create infrastructure
Initialize this configuration.
$ terraform init
Initializing the backend...
##...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Now apply the configuration. Respond to the confirmation prompt with a yes
to create the example
infrastructure.
$ terraform apply
## ...
Apply complete! Resources: 40 added, 0 changed, 0 destroyed.
Outputs:
public_dns_name = "lb-GHB-my-project-dev-816845261.us-east-2.elb.amazonaws.com"
Tip
This tutorial shows the output for Terraform commands run with the Terraform CLI. If you are following the HCP Terraform workflow, the output may differ slightly but the results will be the same.
If you use HCP Terraform to provision your resources, your workspace now displays the list of all of the resources it manages.
Use locals to name resources
In the configuration's main.tf
file, several resource names consist of
interpolations of the resource type and the project
and environment
values
from the resource_tags
variable. Reduce duplication and simplify the
configuration by setting the shared part of each name
as a local
value to re-use across your configuration.
First, define the local name_suffix
by pasting the following snippet at the top of
main.tf
.
main.tf
locals {
name_suffix = "${var.resource_tags["project"]}-${var.resource_tags["environment"]}"
}
As in any Terraform configuration, the order of your resource definitions and values does not affect how Terraform interprets them. To make your configuration more readable, consider putting local definitions near the top of your files.
Now, update the name
attributes in the configuration to use this new local
value.
main.tf
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.66.0"
name = "vpc-${local.name_suffix}"
## ...
}
module "app_security_group" {
source = "terraform-aws-modules/security-group/aws//modules/web"
version = "3.17.0"
name = "web-sg-${local.name_suffix}"
## ...
}
module "lb_security_group" {
source = "terraform-aws-modules/security-group/aws//modules/web"
version = "3.17.0"
name = "lb-sg-${local.name_suffix}"
## ...
}
module "elb_http" {
source = "terraform-aws-modules/elb/aws"
version = "2.4.0"
# Ensure load balancer name is unique
name = "lb-${random_string.lb_id.result}-${local.name_suffix}"
## ...
}
Note that the load balancer name includes a random string to ensure that it is unique, which is a requirement for AWS load balancer names.
Apply the configuration to verify that the names' values have not changed.
$ terraform apply
random_string.lb_id: Refreshing state... [id=yeH]
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-02e45747200845db6]
module.vpc.aws_eip.nat[0]: Refreshing state... [id=eipalloc-09ebe519c3eec2991]
## ...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
public_dns_name = "lb-yeH-my-project-dev-1748354490.us-east-2.elb.amazonaws.com"
Combine variables with local values
Unlike variable values, local values can use dynamic expressions and resource
arguments. The resource_tags
map in variables.tf
defines the tags for the
local name_suffix
as defaults. A user could override the default value for
this map and omit the project_name
and environment
tags.
Many projects require that all resources are tagged in a certain way to track them. To enforce this requirement, use a local value in combination with variables so the tags assigned to resources include at least the project name and environment.
Tip
This tutorial uses local values to manage resource tags for educational purposes. Use AWS provider-level default tags to set global tags on your AWS resources.
Add the following new variable definitions to variables.tf
.
variables.tf
variable "project_name" {
description = "Name of the project."
type = string
default = "my-project"
}
variable "environment" {
description = "Name of the environment."
type = string
default = "dev"
}
Next, change the default value of the resource_tags
variable to an empty map.
variables.tf
variable "resource_tags" {
description = "Tags to set for all resources"
type = map(string)
default = { }
}
Add a new locals
block to main.tf
to create a map combining both required
tags and user defined tags.
main.tf
locals {
required_tags = {
project = var.project_name,
environment = var.environment
}
tags = merge(var.resource_tags, local.required_tags)
}
All of your configuration's local values can be defined in a single locals
block, or you can use multiple blocks.
Update the definition of the name_suffix
local to use the new variables for
project_name
and environment
.
main.tf
locals {
name_suffix = "${var.project_name}-${var.environment}"
}
Then, update the five references to tags in main.tf
to use the new local
value.
main.tf
tags = local.tags
## ... replace all five occurrences of `tags = var.resource_tags`
Finally, add an output named tags
to your outputs.tf
file. This output will
display the tags you used in this configuration. Based on your local value, the tags are a combination
of var.resource_tags
and local.required_tags
.
outputs.tf
output "tags" {
value = local.tags
}
Now, apply these changes to confirm that the tags for each resource to a
combination of the required_tags
local value and the resource_tags
variable.
Respond to the confirmation prompt with yes
. Once again, because none of the
values of changed, Terraform will show no changes to apply.
$ terraform apply
random_string.lb_id: Refreshing state... [id=yeH]
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-02e45747200845db6]
module.vpc.aws_eip.nat[1]: Refreshing state... [id=eipalloc-011888e01ac7b377a]
## ...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
public_dns_name = "lb-GHB-my-project-dev-816845261.us-east-2.elb.amazonaws.com"
tags = {
"environment" = "dev"
"project" = "my-project"
}
Next, run another apply, this time changing the environment from dev
to
prod
. Since this updates the local value with the tags map and the resource
name suffix, Terraform will have to recreate some of your resources. Respond to
the confirmation prompt with yes
.
$ terraform apply -var "environment=prod"
random_string.lb_id: Refreshing state... [id=yeH]
module.vpc.aws_vpc.this[0]: Refreshing state... [id=vpc-02e45747200845db6]
module.vpc.aws_eip.nat[1]: Refreshing state... [id=eipalloc-011888e01ac7b377a]
## ...
Plan: 17 to add, 15 to change, 17 to destroy.
Changes to Outputs:
~ public_dns_name = "lb-GHB-my-project-dev-816845261.us-east-2.elb.amazonaws.com" -> (known after apply)
~ tags = {
~ environment = "dev" -> "prod"
# (1 unchanged element hidden)
}
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
## ...
module.app_security_group.module.sg.aws_security_group.this_name_prefix[0]: Destruction complete after 1s
module.lb_security_group.module.sg.aws_security_group.this_name_prefix[0]: Destruction complete after 2s
Apply complete! Resources: 17 added, 15 changed, 17 destroyed.
Outputs:
public_dns_name = "lb-GHB-my-project-prod-1689308961.us-east-2.elb.amazonaws.com"
tags = {
"environment" = "prod"
"project" = "my-project"
}
Clean up your infrastructure
Before moving on, destroy the infrastructure you created by running the
terraform destroy
command. Respond to the confirmation prompt with yes
.
$ terraform destroy
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
##...
Plan: 0 to add, 0 to change, 42 to destroy.
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
##...
Destroy complete! Resources: 42 destroyed.
If you used HCP Terraform for this tutorial, after destroying your resources, delete the learn-terraform-locals
workspace from your HCP Terraform organization.
Next steps
In this tutorial, you defined and used Terraform local values to reduce duplication and improve configuration readability. You also combined user-defined variables with locals to improve the consistency of your infrastructure tags.
Review the following resources for more guidance on refining your Terraform configuration:
- Provision multiple resources using the count meta-argument.
- Make your configuration dynamic by using functions.
- Learn how to create and use Terraform modules.