# Terraform Variables, Outputs and Locals Explained with Examples

> How to use input variables, validation, outputs and local values in Terraform and OpenTofu, and how to set variables with tfvars files and environment variables.

- Source: https://www.itwonderlab.com/terraform-variables-outputs-locals/
- Published: 2026-08-24
- Updated: 2026-08-24
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## 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](https://www.itwonderlab.com/hcl/) take care of this:

- **`variable`**: an input of the configuration or of a [module](https://www.itwonderlab.com/terraform-module/).
- **`output`**: a value that the configuration returns, shown after `apply` and available to other configurations.
- **`locals`**: a named expression calculated inside the configuration, to avoid repeating it.

### Input variables

```hcl title="variables.tf"
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>`:

```hcl title="main.tf"
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:

1. The `default` of the variable.
2. Environment variables named `TF_VAR_<name>`.
3. `terraform.tfvars` and `terraform.tfvars.json`.
4. Files `*.auto.tfvars` and `*.auto.tfvars.json`, in alphabetical order.
5. `-var-file` and `-var` on the command line.

```hcl title="pro.tfvars"
environment    = "pro"
instance_count = 3
tags = {
  Project = "demo"
}
```

```shell
$ tofu plan -var-file=pro.tfvars
$ TF_VAR_db_password='S3cret' tofu apply
```

> [!WARNING]
> `sensitive = true` hides the value in the plan output, but the value is still stored in plain text in the [state file](https://www.itwonderlab.com/terraform-state/). Protect the state with an encrypted [backend](https://www.itwonderlab.com/terraform-backend/) or with [state encryption in OpenTofu](https://www.itwonderlab.com/terraform-state-file-encryption/). See also [secrets management](https://www.itwonderlab.com/terraform-secrets-management/).

### Local values

Use `locals` for expressions that you repeat or to give a name to a calculation:

```hcl title="locals.tf"
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

```hcl title="outputs.tf"
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:

```shell
$ 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](https://www.itwonderlab.com/terraform-data-sources-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](https://www.itwonderlab.com/terraform-lifecycle-meta-argument/)).

### 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](https://www.itwonderlab.com/terraform-project-structure/).
