Terraform
Set configuration parameters
Make your Terraform modules flexible, composable, and reusable by defining your module's input variables, defining module-scoped local values, and defining output values to expose data from your module.
Overview
Use the following blocks to modify your module's behavior:
- The
variable
block lets you define input arguments, letting module consumers pass in values to customize behavior at runtime. - The
locals
block defines temporary values scoped to a module, letting you name and reuse expressions in your configuration. - The
outputs
block exposes data from a module, making module results available in the Terraform CLI, HCP Terraform, and other parts of your configuration.
Define module input arguments with variables
Hands-on: Try the Customize Terraform Configuration with Variables tutorial.
Add variable
blocks to your configuration so that module consumers can pass in specific input values. This lets module consumers customize module behavior without altering the module's source code. Variables define the interface of your module by specifying what values your module accepts as arguments.
Adding a variables
block to your root module lets consumers pass values into the module at run time. Defining a variable
block in a child module lets a parent module pass values into the child module at run time. Learn more about passing values to child modules.
For example, if your root module has a resource with hardcoded values, Terraform sets up the same resource every time:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
subnet_id = "subnet-12345"
tags = {
Environment = "dev"
Name = "dev-web-server"
}
}
Hardcoded values in your configuration produce the same results every time, making your module inflexible and potentially hard to reuse. If you know a value in your configuration changes between Terraform operations, you can replace hardcoded values with variable
blocks.
Defining variables gives your module consumer the flexibility to change values at run time. Add a variable
block for each input you want to define for your module.
The following example defines instance_type
, subnet_id
, and environment
input variables. These variables lets module consumers specify custom values for customize the instance type, subnet, and deployment environment of your web server at run time:
variable "instance_type" {
type = string
description = "EC2 instance type for the web server"
default = "t2.micro"
}
variable "subnet_id" {
type = string
description = "Subnet ID where the web server will be deployed"
}
variable "environment" {
type = string
description = "Deployment environment name"
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
The environment
variable contains an optional validation
block to ensure the value a consumer assigns meets module requirements. Learn more about validating variable values in your configuration.
To reference a variable
in other parts of your configuration, use var.<NAME>
syntax. For example, to make your web server use the new variables, replace the hardcoded values with references to var.instance_type
, var.subnet_id
, and var.environment
:
# ...
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = var.subnet_id
tags = {
Environment = var.environment
Name = "${var.environment}-web-server"
}
}
Module consumers can use the default values for the environment
, subnet_id
, and instance_type
variables in the web
configuration or input custom values when they run the configuration. If a variable does not have a default value, such as the subnet_id
variable, then Terraform prompts the user to assign a value before it generates a plan.
If you are defining a variable
for sensitive data such as an API key or password, use the sensitive
argument to prevent Terraform from displaying the value in CLI output:
variable "database_password" {
type = string
description = "Password for the RDS database instance"
sensitive = true
}
Terraform still stores the values of sensitive variables in your state. You can add the ephemeral
argument to your variable
configuration to omit the variable from state and plan files. Adding the ephemeral
argument does add restrictions to the values you can assign that variable. To learn more about handling sensitive data in your configuration, refer to Manage sensitive data.
To learn more about the best practices of defining variables, refer to the Terraform style guide. To learn more about the variable
block, refer to the variable
block reference.
Assign values to input variables
You can assign values to root module variables through multiple methods, each with different precedence levels. Child modules receive their inputs from a parent module as arguments. To learn more about calling child modules, refer to Modules.
If a module defines a variable without a default
argument, Terraform prompts the user to supply a value for that variable before it generates a plan. You can assign variable values in the root module using the following methods:
- HCP Terraform variables and variable sets
- The
-var
and-var-file
options on the CLI - Variable definition files
- Environment variables
Once you assign a value to a variable, you cannot reassign that variable within the same file. However, if the root module receives multiple values for the same variable name from different sources, Terraform uses the following order of precedence:
- Any
-var
and-var-file
options on the command line in the order provided and variables from HCP Terraform - Any
*.auto.tfvars
or*.auto.tfvars.json
files in lexical order - The
terraform.tfvars.json
file - The
terraform.tfvars
file - Environment variables
- The
default
argument of thevariable
block
Values defined in HCP Terraform and on the command line take precedence over other ways of assigning variable values. The variable's default
argument is at the lowest level of precedence.
Manage variables in HCP Terraform
HCP Terraform provides the following variable management capabilities:
- Assign values to variables in your configuration through workspaces.
- Group variables into sets that you can apply to multiple workspaces.
- Use access control settings to limit who can view and create new variables.
To learn more about variables in HCP Terraform, refer to the Variables overview.
Command-line variables
Inputting variable values with Terraform CLI commands is useful for one-off deployments or when you need to override specific values without creating new files. You can assign variable values with the Terraform CLI using the -var=<VAR_NAME>=<VALUE>
flag:
terraform apply -var="instance_type=t3.medium" -var="environment=prod"
terraform apply -var='subnet_ids=["subnet-12345","subnet-67890"]'
If you are passing complex types through environment variables or command line flags, use proper JSON syntax to comply with the string escaping rules in your shell:
export TF_VAR_complex_config='{"key": "value", "list": ["a", "b"]}'
For readability, and to avoid shell escaping, we recommend setting complex variable values with variable definition files. For more details on shell quoting and the Windows command prompt, refer to Input variables on the command line.
Variable definition files
Variable definition files are ideal for managing different environment configurations and for managing variable values in your version control system. Create a variable definition file to assign multiple variable values in one file.
You can assign values directly to variable names in files with a .tfvars
or .auto.tfvars
extension. For example, the following file assigns variable values for a production environment:
production.auto.tfvars
instance_type = "t3.large"
environment = "prod"
subnet_ids = ["subnet-12345", "subnet-67890", "subnet-abcdef"]
enable_monitoring = true
Terraform automatically loads variable definition files if it detects any of the following:
- File names ending in
.auto.tfvars
or.auto.tfvars.json
- A file named
terraform.tfvars.json
- A file named
terraform.tfvars
Terraform loads different variable definition files at different times, and uses the following precedence order for the values assigned in variable definition files:
- Any
*.auto.tfvars
or*.auto.tfvars.json
files in lexical order - The
terraform.tfvars.json
file - The
terraform.tfvars
file
If a file name ends with .json
, then Terraform parses that file as a JSON object, using variable names as the root object keys:
{
"image_id": "ami-abc123",
"availability_zone_names": ["us-west-1a", "us-west-1c"]
}
You can also apply a .tfvars
variable file directly to the CLI using the -var-file
flag:
terraform apply -var-file="production.auto.tfvars"
If your variable definition files contains any sensitive values, ensure you ignore those files in your VCS provider.
Environment variables
Environment variables are useful in CI/CD pipelines when you want to inject configuration values without creating additional files. You can set environment variables using the TF_VAR_
prefix to a variable name:
export TF_VAR_instance_type=t3.medium
export TF_VAR_environment=staging
terraform apply
If you are passing complex types through environment variables or command line flags, use proper JSON syntax to comply with the string escaping rules in your shell:
export TF_VAR_complex_config='{"key": "value", "list": ["a", "b"]}'
For readability, and to avoid shell escaping, we recommend setting complex variable values with variable definition files.
Undeclared variables
Terraform handles setting values for undeclared variables differently depending on how you assign that value:
- Terraform ignores any assigned environment variables that do not have a matching
variable
block. - Terraform warns you if you assign an undeclared variable in a variable definition file, letting you catch accidental misspellings in your configuration or definition files.
- Terraform errors if you attempt to assign a value for an undeclared variable with
-var
on the command line.
Define locals to reuse expressions
Hands-on: Try the Simplify Terraform Configuration with Locals tutorial.
Local values are similar to function-scoped variables in other programming languages. Local values assign names to expressions, letting you use the name multiple times within a module instead of repeating that expression.
Define a locals
block when you want to reuse an expression throughout your configuration. You can define the locals
block in any module. The value you assign can be any valid Terraform expression and can reference the following:
- Variables
- Resource attributes
- Function outputs
- Other local values
For example, you could define a locals
block to create a consistent naming convention, identify your primary subnet, and to set aside environmental configuration settings:
locals {
# Naming convention
resource_name = "${var.project_name}-${var.environment}"
# Process the subnet list
primary_public_subnet = var.subnet_ids[0]
subnet_count = length(var.subnet_ids)
# Environmental deployment settings
is_production = var.environment == "prod"
monitoring_enabled = var.monitoring || local.is_production
}
Use the local.<NAME>
syntax to reference values from a locals
block in your configuration. The locals
block defines local values, but you must use the singular local
keyword to reference the individual values.
For example, in your configuration you can reference local.resource_name
to name resources, and use local.primary_public_subnet
with local.monitoring_enabled
to configure a web server:
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = local.primary_public_subnet
monitoring = local.monitoring_enabled
tags = {
Name = local.resource_name
Environment = var.environment
}
}
resource "aws_security_group" "web" {
name = "${local.resource_name}-sg"
tags = {
Name = "${local.resource_name}-security-group"
}
}
You can access local values in the module where you define them, but not in other modules. However, you can pass a local value to a child module as an argument.
Local values help avoid repetition and give a meaningful name to the value of an expression. However, they can make configuration harder to read because they obscure where values originate. Use local values in situations where you either reuse a single value in many places to let you change a value in a single place or when the value is the result of a complex expression.
To learn more about the locals
block, refer to the locals
block reference.
Define outputs to expose module data
Hands-on: Try the Output data from Terraform tutorial.
Outputs let you expose information about your infrastructure on the command line, in HCP Terraform, and in other Terraform configurations. The output
block serves the following purposes in Terraform:
- Child modules can expose resource attributes to parent modules.
- Root modules can display values in CLI output.
- Other Terraform configurations using remote state can access root module outputs with the
terraform_remote_state
data source. - Pass information from a Terraform operation to an automation tool.
Add output
blocks to export information about your module. For example, you can add two output
blocks to expose the ID and the IP address of your configuration's web server:
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
output "instance_ip" {
description = "Private IP address of the EC2 instance"
value = aws_instance.web.private_ip
}
resource "aws_instance" "web" {
# …
}
Depending on whether an output value is defined in your root module or a child module, you can access the information exposed in different ways. Terraform displays root module output values in the CLI after you apply your configuration, and if you are using HCP Terraform, your workspace's overview page lists your configuration's outputs.
You can use outputs to pass values from a child module to a parent module, and parent modules access those outputs using module.<CHILD_MODULE_NAME>.<OUTPUT_NAME>
syntax.
For example, your parent module can access the instance_ip
and instance_id
outputs from a child module named web_server
by referencing module.web_server.NAME
:
module "web_server" {
source = "./modules/web_server"
# …
}
resource "aws_route53_record" "web" {
zone_id = data.aws_route53_zone.main.zone_id
name = "web.example.com"
type = "A"
records = [module.web_server.instance_ip]
#...
}
resource "aws_cloudwatch_alarm" "web_health" {
alarm_name = "web-server-health"
comparison_operator = "GreaterThanThreshold"
threshold = "80"
dimensions = {
InstanceId = module.web_server.instance_id
}
# ...
}
If you are outputting sensitive data such as a password or API key, use the sensitive
argument to prevent Terraform from displaying the value in CLI output:
output "database_password" {
description = "Auto-generated password for the RDS database instance"
value = aws_db_instance.main.password
sensitive = true
}
Terraform does still store the values of sensitive outputs in your state, and if you use the terraform output
CLI command with the -json
or -raw
flags then Terraform displays sensitive outputs in plain text.
Adding the ephemeral
argument to an output omits that value from state and plan files, but it adds restrictions to the values you can assign that output. To learn more about handling and outputting sensitive data in your configuration, refer to Manage sensitive data.
To learn more about the outputs
block, refer to the outputs
block reference.
Next steps
- Variable block reference - A complete reference of the
variable
block. - Locals block reference - A complete reference of the
locals
block. - Output block reference - A complete reference of the
output
block. - Test your Validation - Validate your configuration to improve your module consumer's troubleshooting, make your runs more predictable, and help your maintainers understand your configuration's intent.
- Manage sensitive data - Learn how to handle sensitive and ephemeral data in your configuration and state.