In today’s blog post, we will discuss terraform variables, specifically variable syntax, data type, declaration, assignment, and validation. A variable is used to pass inputs to a terraform module, resource, data source or in any other configuration and store values. A variable enables you to keep your terraform code reusable.
What are Terraform Variables?
In Terraform, variables act as placeholders for values that can change between deployments or environments. Instead of hardcoding values directly into your configuration, you use variables, allowing you to easily reuse the same Terraform code for different purposes – be it deploying a development, staging, or production environment, or simply tweaking a resource configuration.
Think of them like arguments to a function in a programming language. They make your code modular, flexible, and helps reduce duplication.
Declaring Input Variables: The variable Block
You declare input variables using the variable
block within your Terraform configuration files (commonly in variables.tf
).
# variables.tf
variable "region" {
description = "The AWS region where resources will be deployed."
type = string
default = "us-east-1"
}
variable "instance_count" {
description = "Number of EC2 instances to provision."
type = number
default = 1
}
variable "tags" {
description = "A map of tags to apply to resources."
type = map(string)
default = {
Environment = "dev"
Project = "my-app"
}
}
Explanation:
variable "region"
: This defines a variable namedregion
.description
: (Optional) Provides a human-readable explanation of the variable’s purpose. This is for documentation, especially when your code is shared across projects.type
: (Optional) Specifies the data type of the variable. Terraform will validate the provided value against this type. If omitted, Terraform assumesany
.default
: (Optional) Provides a default value for the variable. If no value is explicitly provided duringterraform plan
orapply
, this default will be used. Variables without adefault
value are considered mandatory and you must provide the value during runtime.
Terraform Variable Types
Terraform supports several primitive and complex data types for variables:
Primitive Types:
string
A sequence of Unicode characters (text).
variable "bucket_name_prefix" {
type = string
description = "Prefix for S3 bucket name."
}
number
A numeric value (integers or floating-point).
variable "disk_size_gb" {
type = number
description = "Size of the disk in GB."
}
bool
A boolean value (true
or false
).
variable "enable_public_ip" {
type = bool
description = "Whether to assign a public IP to the instance."
default = false
}
Complex Types:
list(TYPE)
An ordered sequence of values of a specified type.
variable "instance_types" {
type = list(string)
description = "List of allowed EC2 instance types."
default = ["t2.micro", "t3.small"]
}
map(TYPE)
A collection of key-value pairs, where both keys (strings) and values are of a specified type.
variable "ami_ids" {
type = map(string)
description = "Map of AMIs per region."
default = {
"us-east-1" = "ami-0abcdef1234567890"
"us-west-2" = "ami-0fedcba9876543210"
}
}
set(TYPE)
An unordered collection of unique values of a specified type.
variable "allowed_cidrs" {
type = set(string)
description = "Set of CIDR blocks allowed access."
default = ["0.0.0.0/0"]
}
object(...)
:
A structured type that can contain attributes of different types.
variable "server_config" {
type = object({
cpu_cores = number
memory_gb = number
environment = string
})
description = "Configuration for the server."
default = {
cpu_cores = 2
memory_gb = 8
environment = "dev"
}
}
tuple(...)
An ordered sequence of values where each element can have a different type, fixed in length.
variable "database_credentials" {
type = tuple([string, string, number]) # username, password, port
description = "Database access credentials (username, password, port)."
default = ["admin", "password123", 5432]
}
any
Use any
when the type of the variable is not known in advance, or when it needs to accept multiple types. This sacrifices type safety for flexibility.
Providing Variable Values: Multiple Methods
Terraform offers several ways to provide values for your variables, each with its own use case and precedence.
Command-Line Arguments (-var
and -var-file
)
You can pass individual variable values directly on the command line using the -var
flag:
terraform plan -var="region=us-west-2" -var="instance_count=3"
For a larger set of variables, it is more convenient to use a variable definition file with -var-file
:
terraform plan -var-file="production.tfvars"
.tfvars
Files (Automatic Loading)
Terraform automatically loads variable definitions from files with specific names in the current working directory:
terraform.tfvars
terraform.tfvars.json
- Any files ending with
.auto.tfvars
or.auto.tfvars.json
(e.g.,dev.auto.tfvars
,prod.auto.tfvars
)
Example terraform.tfvars
:
# terraform.tfvars
region = "eu-central-1"
instance_count = 2
tags = {
Environment = "prod"
Owner = "ops-team"
}
Example dev.auto.tfvars
:
# dev.auto.tfvars
instance_count = 1
enable_public_ip = true
Environment Variables (TF_VAR_
)
Terraform looks for environment variables prefixed with TF_VAR_
followed by the variable name (case-insensitive). This is particularly useful in CI/CD pipelines.
export TF_VAR_region="ap-southeast-1"
export TF_VAR_instance_count=5
terraform apply
Important Note on Environment Variables: While convenient, be cautious with sensitive information. Environment variables can sometimes be visible in system logs. For truly sensitive data, consider dedicated secret management tools.
Prompting for Input
If a variable is declared without a default
value and no value is provided through other means, Terraform will prompt you for input during terraform plan
or apply
.
# variables.tf
variable "admin_password" {
description = "Admin password for the database."
type = string
sensitive = true # Mark as sensitive to prevent showing in logs
}
When you run terraform plan
, you will see:
var.admin_password
Admin password for the database.
Enter a value:
Variable Precedence: Who Wins?
When the same variable is defined in multiple places, Terraform follows a specific order of precedence, with later sources overriding earlier ones:
- Environment variables (
TF_VAR_name
) terraform.tfvars
(if present)terraform.tfvars.json
(if present)- Any
*.auto.tfvars
or*.auto.tfvars.json
files (processed in lexical/alphabetical order of their filenames) -var
and-var-file
options on the command line (in the order they are provided)
Example:
If region
is set to us-east-1
in terraform.tfvars
, but you run terraform apply -var="region=us-west-2"
, then us-west-2
will be used.
Using Variables in Your Configuration (var.
prefix)
Once declared, you can reference your input variables within your Terraform configuration using the var.
prefix.
# main.tf
resource "aws_instance" "web_server" {
ami = "ami-0abcdef1234567890" # Example AMI ID
instance_type = "t2.micro"
count = var.instance_count # Using a number variable
tags = var.tags # Using a map variable
user_data = <<-EOF
#!/bin/bash
echo "Hello from $(hostname) in ${var.region}!" > /var/www/html/index.html
EOF
}
resource "aws_s3_bucket" "my_bucket" {
bucket = "${var.bucket_name_prefix}-${var.region}" # String interpolation
acl = "private"
tags = var.tags
}
Explanation:
count = var.instance_count
: Thecount
meta-argument takes its value from theinstance_count
variable.tags = var.tags
: Thetags
block directly uses thetags
map variable."${var.bucket_name_prefix}-${var.region}"
: String interpolation is used to combine thebucket_name_prefix
andregion
variables into a single bucket name.user_data = <<-EOF ... ${var.region} ... EOF
: Here-doc syntax also supports variable interpolation.
Advanced Variable Concepts
Variable Validation (validation
block)
Terraform allows you to define validation rules for your variables using the validation
block. This ensures that the values provided by users meet certain criteria, preventing misconfigurations early.
variable "environment" {
description = "Deployment environment (dev, staging, prod)."
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "The environment must be one of 'dev', 'staging', or 'prod'."
}
}
variable "vpc_cidr_block" {
description = "CIDR block for the VPC."
type = string
validation {
condition = can(cidrhost(var.vpc_cidr_block, 0)) # Checks if it is a valid CIDR
error_message = "The VPC CIDR block must be a valid CIDR notation (e.g., '10.0.0.0/16')."
}
}
Explanation:
condition
: A boolean expression that must evaluate totrue
for the validation to pass.error_message
: The message displayed if the condition evaluates tofalse
.
Sensitive Variables (sensitive = true
)
For variables containing confidential information like passwords, API keys, or private keys, use the sensitive = true
argument. This tells Terraform to redact the variable’s value from CLI output (e.g., terraform plan
, terraform apply
) and state files.
variable "db_password" {
description = "Password for the database user."
type = string
sensitive = true
}
When you run terraform plan
, you will see (sensitive value)
instead of the actual password:
+ resource "aws_db_instance" "my_db" {
...
password = (sensitive value)
...
}
Important: While sensitive = true
redacts output, it does not encrypt the value in the state file. For true security, integrate with dedicated secret management solutions like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault.
Output Variables: Sharing Information (output
block)
Output variables are like return values from your Terraform configuration or module. They allow you to display specific information about the infrastructure you have provisioned, making it accessible to other Terraform configurations, CI/CD pipelines, or simply for your own reference.
# outputs.tf
output "instance_public_ip" {
description = "The public IP address of the primary web server instance."
value = aws_instance.web_server[0].public_ip # Accessing the first instance's IP
}
output "s3_bucket_arn" {
description = "The ARN of the S3 bucket created."
value = aws_s3_bucket.my_bucket.arn
}
output "server_config_output" {
description = "The applied server configuration."
value = var.server_config
sensitive = true # If your object contains sensitive data, mark the output sensitive
}
After terraform apply
, you can retrieve these values using terraform output
(or can be simply viewed under plan, apply terminal window):
terraform output instance_public_ip
Input and Output Variables in Modules
Modules are used for structuring and reusing Terraform code. Input variables allow you to customize a module’s behavior, while output variables allow the module to pass information back to the calling configuration.
Module vpc/main.tf
:
# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr_block
# ... other VPC configuration
}
output "vpc_id" {
value = aws_vpc.main.id
}
Root Configuration main.tf
using the module:
# main.tf
module "my_vpc" {
source = "./modules/vpc"
vpc_cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = module.my_vpc.vpc_id # Using output from the VPC module
cidr_block = "10.0.1.0/24"
# ...
}
Best Practices for Terraform Variables
- Declare variables in
variables.tf
: Keep your variable declarations organized in a dedicated file. - Always provide
description
: Clear descriptions are invaluable for anyone using or maintaining your code. - Specify
type
: Enforce type safety to catch errors early. - Use
default
values carefully Provide defaults for optional variables, but require input for environment-specific or critical values. - Use
.tfvars
files for environment-specific values: Avoid hardcoding values inmain.tf
. Useterraform.tfvars
or*.auto.tfvars
for different environments (e.g.,dev.auto.tfvars
,prod.auto.tfvars
). - Use
sensitive = true
for secrets: Hide sensitive data from console output. For stronger security, use a secret management solution. - Avoid over-parameterization: Only create variables for values that genuinely need to change. If a value is consistently static, hardcode it or use a local value.
- Use
local
values for complex expressions: For values derived from other variables or expressions, define them aslocals
(locals.tf
) for readability and reusability within a single module. This is different fromvar.
aslocals
are not exposed as inputs. - Naming Conventions: Use clear, descriptive, lowercase names with underscores (e.g.,
instance_type
,vpc_cidr_block
). Boolean variables should have positive names (e.g.,enable_feature
instead ofdisable_feature
).
Conclusion
Mastering Terraform variables is an important step in writing efficient and maintainable terraform code. By understanding how to declare them, provide their values, and use advanced features like validation and sensitivity, you can create flexible, reusable, and secure configurations that can used in different environments and requirements.
Author

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com