Variables make Terraform code reusable. Outputs share data between modules and display results. Together they are how you parameterize infrastructure.
Input Variables
Basic Types
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "instance_count" {
description = "Number of instances"
type = number
default = 2
}
variable "enable_monitoring" {
description = "Enable detailed monitoring"
type = bool
default = false
}Complex Types
# List
variable "availability_zones" {
type = list(string)
default = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
}
# Map
variable "instance_types" {
type = map(string)
default = {
dev = "t3.micro"
staging = "t3.small"
prod = "t3.medium"
}
}
# Object
variable "database" {
type = object({
engine = string
version = string
instance_class = string
storage_gb = number
multi_az = bool
})
default = {
engine = "postgres"
version = "16"
instance_class = "db.t3.micro"
storage_gb = 20
multi_az = false
}
}
# List of objects
variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
description = string
}))
default = [
{
port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTP"
},
{
port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS"
}
]
}Validation
variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}
variable "cidr_block" {
type = string
validation {
condition = can(cidrhost(var.cidr_block, 0))
error_message = "Must be a valid CIDR block."
}
}
variable "name" {
type = string
validation {
condition = length(var.name) >= 3 && length(var.name) <= 32
error_message = "Name must be 3-32 characters."
}
validation {
condition = can(regex("^[a-z][a-z0-9-]*$", var.name))
error_message = "Name must start with a letter and contain only lowercase letters, numbers, and hyphens."
}
}Sensitive Variables
variable "db_password" {
type = string
sensitive = true # Hidden in plan/apply output
}
variable "api_key" {
type = string
sensitive = true
}Setting Variable Values
Priority (lowest to highest)
defaultin variable block- Environment variables (
TF_VAR_name) terraform.tfvarsfile*.auto.tfvarsfiles-var-fileflag-varflag (highest)
tfvars Files
# terraform.tfvars (auto-loaded)
environment = "production"
instance_type = "t3.large"
instance_count = 3
# production.tfvars (loaded with -var-file)
database = {
engine = "postgres"
version = "16"
instance_class = "db.r6g.large"
storage_gb = 500
multi_az = true
}terraform apply -var-file=production.tfvarsEnvironment Variables
export TF_VAR_db_password="S3cur3P@ss!"
export TF_VAR_environment="production"
terraform applyMaster this topic with hands-on labs
Go beyond reading — build real projects in sandboxed environments with expert video guidance.
Browse Courses →Locals
Computed values that simplify expressions:
locals {
name_prefix = "${var.project}-${var.environment}"
common_tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
Team = var.team
}
is_production = var.environment == "production"
instance_type = local.is_production ? "t3.large" : "t3.micro"
subnet_cidrs = [for i in range(var.az_count) : cidrsubnet(var.vpc_cidr, 8, i)]
}
resource "aws_instance" "app" {
instance_type = local.instance_type
tags = merge(local.common_tags, { Name = "${local.name_prefix}-app" })
}Outputs
output "vpc_id" {
description = "The VPC ID"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "Public subnet IDs"
value = aws_subnet.public[*].id
}
output "database_endpoint" {
description = "Database connection endpoint"
value = aws_db_instance.main.endpoint
sensitive = true # Hidden in CLI output
}
output "load_balancer_dns" {
description = "ALB DNS name"
value = aws_lb.main.dns_name
depends_on = [aws_lb_listener.https]
}Using Outputs
# View all outputs
terraform output
# Get specific output
terraform output vpc_id
# JSON format (for scripts)
terraform output -json
# Raw value (no quotes)
terraform output -raw load_balancer_dnsCross-Module References
# In root module
module "vpc" {
source = "./modules/vpc"
}
module "database" {
source = "./modules/rds"
vpc_id = module.vpc.vpc_id # Output from vpc module
subnet_ids = module.vpc.private_subnet_ids
}Get weekly IT automation tips
Docker, Ansible, Terraform, MLOps — curated insights delivered to your inbox. No spam.
Subscribe Free →Patterns
Feature Flags
variable "features" {
type = object({
enable_cdn = bool
enable_waf = bool
enable_monitoring = bool
})
default = {
enable_cdn = false
enable_waf = false
enable_monitoring = true
}
}
resource "aws_cloudfront_distribution" "cdn" {
count = var.features.enable_cdn ? 1 : 0
# ...
}Environment-Specific Defaults
variable "env_config" {
type = map(object({
instance_type = string
min_size = number
max_size = number
}))
default = {
dev = { instance_type = "t3.micro", min_size = 1, max_size = 2 }
staging = { instance_type = "t3.small", min_size = 2, max_size = 4 }
prod = { instance_type = "t3.medium", min_size = 3, max_size = 10 }
}
}
locals {
config = var.env_config[var.environment]
}What's Next?
Our Terraform for Beginners course covers variables, outputs, and modules across 15 hands-on lessons. First lesson is free.
---
Ready to go deeper? Check out our hands-on course: Terraform for Beginners — practical exercises you can follow along on your own machine.
Ready to learn by doing?
Stop reading tutorials — start building. Expert video courses with hands-on labs in real sandboxed environments.
Related Articles
GitHub Actions CI/CD for Terraform
Automate Terraform with GitHub Actions. Plan on PR, apply on merge, remote state locking, and secure secrets for IaC pipelines.
Terraform Workspaces for Environments
Use Terraform workspaces for dev, staging, and production. Practical patterns, trade-offs, and best practices for multi-env IaC.
Terraform Ansible Kubernetes Stack
Combine Terraform, Ansible, and Kubernetes for complete DevOps infrastructure. Provision, configure, and deploy end to end.
Terraform vs CloudFormation vs Pulumi
Compare the top Infrastructure as Code tools — Terraform, AWS CloudFormation, and Pulumi. Understand their strengths, weaknesses, and ideal use cases.
Terraform Workspaces Multi-Env
Manage environments with Terraform workspaces. CLI workspaces, Terraform Cloud workspaces, directory isolation, and trade-offs.
Terragrunt DRY Terraform Guide
Terragrunt eliminates copy-paste in multi-environment Terraform setups. Learn how to use Terragrunt for DRY configuration, remote state management.
Explore topics
Browse more articles on the topics covered here.