Terraform Variables, Outputs and Locals Explained with Examples
Input variables, output values and local values #
A Terraform or OpenTofu configuration becomes reusable when it stops having fixed values inside the resources. Three blocks of HCL take care of this:
variable: an input of the configuration or of a module.output: a value that the configuration returns, shown afterapplyand available to other configurations.locals: a named expression calculated inside the configuration, to avoid repeating it.
Input variables #
variable "environment" {
type = string
description = "Environment name"
default = "dev"
validation {
condition = contains(["dev", "pre", "pro"], var.environment)
error_message = "The environment must be dev, pre or pro."
}
}
variable "instance_count" {
type = number
default = 1
}
variable "tags" {
type = map(string)
default = {}
}
variable "db_password" {
type = string
sensitive = true
}Use the value with var.<name>:
resource "aws_instance" "web" {
count = var.instance_count
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
tags = merge(var.tags, { Environment = var.environment })
}Types can be string, number, bool, list(...), set(...), map(...), object({...}) and tuple([...]). A variable without default is required.
Setting the values #
Terraform looks for values in this order, the last one wins:
- The
defaultof the variable. - Environment variables named
TF_VAR_<name>. terraform.tfvarsandterraform.tfvars.json.- Files
*.auto.tfvarsand*.auto.tfvars.json, in alphabetical order. -var-fileand-varon the command line.
environment = "pro"
instance_count = 3
tags = {
Project = "demo"
}$ tofu plan -var-file=pro.tfvars
$ TF_VAR_db_password='S3cret' tofu apply
Local values #
Use locals for expressions that you repeat or to give a name to a calculation:
locals {
name_prefix = "ditwl-${var.environment}"
common_tags = merge(var.tags, {
Environment = var.environment
ManagedBy = "opentofu"
})
}
resource "aws_s3_bucket" "logs" {
bucket = "${local.name_prefix}-logs"
tags = local.common_tags
}Locals are not inputs: users of your module cannot change them. A good rule is to use variables for what the caller decides and locals for what is derived.
Output values #
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "db_endpoint" {
value = aws_db_instance.main.endpoint
sensitive = true
}After apply the values are printed, and you can read them any time:
$ tofu output vpc_id
$ tofu output -json
In a module, the outputs are the only way to expose values to the caller: module.network.vpc_id. To read the outputs of another configuration use terraform_remote_state.
Variable validation and preconditions #
validation blocks check a variable before the plan. For checks that involve resources or data sources, use precondition and postcondition inside lifecycle (see lifecycle).
File organization #
By convention, a configuration has main.tf, variables.tf, outputs.tf, providers.tf and versions.tf. Terraform loads every .tf file in the directory, so the names are only a convention. See project structure.