Input Variables
Input variables let you parameterize your Terraform configurations, making them flexible and reusable across different environments.
Declaring Variables
Declare variables using variable blocks:
variable "<NAME>" {
# Configuration options
}
Basic Example
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
Variable Arguments
Specifies what value types are accepted for the variable. type = string
type = number
type = bool
type = list ( string )
type = map ( number )
Default value when no value is provided. Variables without defaults are required. default = "t2.micro"
default = [ "us-west-2a" , "us-west-2b" ]
default = null
Human-readable description of the variable’s purpose. description = "The instance type for web servers"
Marks the variable as containing sensitive data. Terraform will hide its value in output.
Whether the variable can be null. Defaults to true. nullable = false # Variable must have a non-null value
Custom validation rules for the variable value. validation {
condition = length (var . name ) > 3
error_message = "Name must be longer than 3 characters."
}
Variable Types
Primitive Types
variable "name" {
type = string
}
variable "count" {
type = number
}
variable "enabled" {
type = bool
}
Collection Types
variable "availability_zones" {
type = list ( string )
default = [
"us-west-2a" ,
"us-west-2b" ,
"us-west-2c"
]
}
variable "tags" {
type = map ( string )
default = {
Environment = "production"
Team = "platform"
}
}
variable "unique_ids" {
type = set ( string )
}
Structural Types
variable "server_config" {
type = object ({
instance_type = string
ami_id = string
disk_size = number
})
default = {
instance_type = "t2.micro"
ami_id = "ami-12345678"
disk_size = 20
}
}
variable "port_list" {
type = tuple ([ string , number , bool ])
}
Complex Types
variable "instances" {
type = map ( object ({
instance_type = string
ami = string
tags = map ( string )
}))
}
Any Type
variable "flexible" {
type = any # Accepts any type
}
Using Variables
Reference variables with var.<NAME>:
resource "aws_instance" "web" {
ami = var . ami_id
instance_type = var . instance_type
tags = var . tags
}
Assigning Values
Command Line
terraform apply -var= "instance_type=t2.large"
terraform apply -var= "tags={Environment=dev,Team=eng}"
Variable Files
Create terraform.tfvars:
instance_type = "t2.large"
region = "us-west-2"
tags = {
Environment = "production"
ManagedBy = "Terraform"
}
Or custom files:
terraform apply -var-file= "production.tfvars"
Environment Variables
export TF_VAR_instance_type = "t2.large"
export TF_VAR_region = "us-west-2"
terraform apply
Variable Precedence
When multiple values are provided, Terraform uses this precedence (highest to lowest):
Command-line -var flags
*.auto.tfvars files (alphabetically)
terraform.tfvars file
Environment variables (TF_VAR_*)
Default value in variable declaration
Variable Validation
Add custom validation rules:
variable "instance_type" {
type = string
description = "EC2 instance type"
validation {
condition = can ( regex ( "^t[23] \\ ." , var . instance_type ))
error_message = "Instance type must be t2 or t3 series."
}
}
Multiple Validations
variable "port" {
type = number
validation {
condition = var . port >= 1 && var . port <= 65535
error_message = "Port must be between 1 and 65535."
}
validation {
condition = var . port != 22
error_message = "Port 22 is reserved for SSH."
}
}
Cross-Variable Validation
variable "name" {
type = string
validation {
condition = length (var . name ) > 3
error_message = "Name must be longer than 3 characters."
}
}
From the source code (internal/configs/named_values.go:578-602):
func checkVariableValidationBlock ( varName string , vv * CheckRule ) hcl . Diagnostics {
// The validation condition must include a reference to the variable itself
for _ , traversal := range vv . Condition . Variables () {
ref , moreDiags := addrs . ParseRef ( traversal )
if ! moreDiags . HasErrors () {
if addr , ok := ref . Subject .( addrs . InputVariable ); ok {
if addr . Name == varName {
return nil
}
}
}
}
return diags . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Invalid variable validation condition" ,
Detail : fmt . Sprintf ( "The condition for variable %q must refer to var. %s ..." , varName , varName ),
})
}
Sensitive Variables
Mark variables containing secrets:
variable "database_password" {
type = string
sensitive = true
}
When marked sensitive:
Value is hidden in terraform plan and apply output
Value is redacted in logs
Still stored in state file (encrypt state!)
resource "aws_db_instance" "database" {
password = var . database_password # Value hidden in output
}
Nullable Variables
Control whether null is allowed:
variable "optional_config" {
type = string
nullable = true
default = null
}
variable "required_config" {
type = string
nullable = false
# Must provide a non-null value
}
From internal/configs/named_values.go:34-54:
type Variable struct {
Name string
Description string
Default cty . Value
Type cty . Type
Sensitive bool
Nullable bool
NullableSet bool
// Const indicates that this variable can be used during early evaluation
Const bool
}
Ephemeral Variables
Variables that exist only during operations:
variable "temp_token" {
type = string
ephemeral = true
sensitive = true
}
Ephemeral variables are not persisted in state files.
Deprecated Variables
Mark variables as deprecated:
variable "old_config" {
type = string
deprecated = "Use new_config instead"
}
Variable Naming
Variable names must:
Start with a letter or underscore
Contain only letters, digits, underscores, and hyphens
Not conflict with reserved names
variable "instance_type" { } # Valid
variable "instance-type" { } # Valid
variable "_private" { } # Valid
variable "2nd_instance" { } # Invalid
Certain names are reserved and cannot be used as variable names:
source
version
providers
count
for_each
depends_on
# terraform.tfvars
instance_type = "t2.large"
region = "us-west-2"
availability_zones = [
"us-west-2a" ,
"us-west-2b" ,
]
tags = {
Environment = "production"
ManagedBy = "Terraform"
}
{
"instance_type" : "t2.large" ,
"region" : "us-west-2" ,
"availability_zones" : [
"us-west-2a" ,
"us-west-2b"
],
"tags" : {
"Environment" : "production" ,
"ManagedBy" : "Terraform"
}
}
Variable Examples from Source
From internal/configs/testdata/valid-files/variables.tf:
variable "foo" {
}
variable "bar" {
default = "hello"
}
variable "baz" {
type = list
}
variable "bar-baz" {
default = []
type = list ( string )
}
variable "sensitive_value" {
default = {
"a" = 1 ,
"b" = 2
}
sensitive = true
}
variable "nullable" {
type = string
nullable = true
default = "ok"
}
variable "nullable_default_null" {
type = map ( string )
nullable = true
default = null
}
Best Practices
Use Descriptions Always provide clear descriptions for variables to help users understand their purpose.
Set Type Constraints Specify explicit types to catch errors early and make intent clear.
Validate Input Add validation rules to ensure variables meet requirements.
Use Sensitive Flag Mark sensitive variables to prevent accidental exposure in logs.
Next Steps
Outputs Export values from your configuration
Expressions Use variables in expressions