DEV Community

Yency Christopher
Yency Christopher

Posted on

Understanding Terraform Variables the Right Way published: true description: A beginner-friendly guide

variables.tf vs terraform.tfvars - What's the Difference?

If you're new to Terraform, you've probably seen both** variables.tf** and terraform.tfvars files and wondered: "Aren't they doing the same thing?"

Spoiler alert: They're not! Let me break it down in the simplest way possible.

The Quick Answer:

variables.tf = The template (declares WHAT variables exist)
terraform.tfvars = The configuration (provides the ACTUAL values)

Think of it this way:

variables.tf asks the questions
terraform.tfvars gives the answers

Real-World Analogy

Imagine you're ordering a custom pizza:
variables.tf (The Order Form)

variable "size" {
  description = "Pizza size"
  type        = string
  validation {
    condition     = contains(["small", "medium", "large"], var.size)
    error_message = "Size must be small, medium, or large"
  }
}

variable "toppings" {
  description = "Pizza toppings"
  type        = list(string)
  default     = ["cheese"]
}

variable "delivery_address" {
  description = "Where to deliver"
  type        = string
  # No default - this is required!
}

variable "phone" {
  description = "Contact number"
  type        = string
  default     = null  # Optional
}
Enter fullscreen mode Exit fullscreen mode

terraform.tfvars (Your Filled Order)

size             = "large"
toppings         = ["pepperoni", "mushrooms", "olives"]
delivery_address = "123 Main Street, Apt 4B"
phone            = "555-1234"
Enter fullscreen mode Exit fullscreen mode

The restaurant keeps the same order form (variables.tf) for everyone, but YOUR specific order (terraform.tfvars) is unique to you!

Deep Dive: variables.tf
Purpose: Declares the structure and rules for variables

variable "subscription_id" {
  description = "Azure Subscription ID"
  type        = string
  sensitive   = true  # Won't show in logs
}

variable "vm_name" {
  description = "Name of the Virtual Machine"
  type        = string
  default     = "ubuntu-vm"  # Optional default value
}

variable "vm_size" {
  description = "VM size"
  type        = string
  default     = "Standard_B2s"

  validation {
    condition     = can(regex("^Standard_", var.vm_size))
    error_message = "VM size must start with 'Standard_'"
  }
}

variable "allowed_ips" {
  description = "Allowed IP addresses"
  type        = list(string)
  default     = []
}
Enter fullscreen mode Exit fullscreen mode

What it contains:

  • Variable name
  • Type (string, number, bool, list, map, object)
  • Description (documentation)
  • Default value (optional)
  • Validation rules (optional)
  • Sensitive flag (optional)

Think of it as: The schema, the blueprint, the contract

Deep Dive: terraform.tfvars
Purpose: Provides actual values for your infrastructure

subscription_id = "12345678-1234-1234-1234-123456789abc"
vm_name         = "production-web-server"
vm_size         = "Standard_D4s_v3"
allowed_ips     = ["203.0.113.0/24", "198.51.100.0/24"]
Enter fullscreen mode Exit fullscreen mode

What it contains:

  • Just the values!
  • No types, no descriptions, no validation
  • Your specific configuration

Think of it as: The actual data, your answers, the configuration

Why This Separation?
1. Reusability Across Environments
Keep the same variables.tf, but use different values files:
my-terraform-project/
├── variables.tf # Same for all environments
├── main.tf # Same for all environments
├── dev.tfvars # Dev-specific values
├── staging.tfvars # Staging-specific values
└── prod.tfvars # Production-specific values

Deploy to different environments:
terraform apply -var-file="dev.tfvars" # Deploy to dev
terraform apply -var-file="staging.tfvars" # Deploy to staging
terraform apply -var-file="prod.tfvars" # Deploy to production

2. Security & Git Management
**.gitignore
**terraform.tfvars # Contains secrets - DON'T COMMIT
*.auto.tfvars # Contains secrets - DON'T COMMIT

Safe to commit
variables.tf # Just the template
terraform.tfvars.example # Example with fake values

Example file to commit:
terraform.tfvars.example
subscription_id = "YOUR_SUBSCRIPTION_ID_HERE"
vm_name = "your-vm-name-here"
vm_size = "Standard_B2s"

3. Type Safety & Validation
variables.tf enforces rules:

variable "environment" {
  type = string
  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod"
  }
}

variable "vm_count" {
  type = number
  validation {
    condition     = var.vm_count >= 1 && var.vm_count <= 10
    error_message = "VM count must be between 1 and 10"
  }
}
Enter fullscreen mode Exit fullscreen mode

If you try to pass invalid values in terraform.tfvars, Terraform will catch it!

The Complete Flow

Image description

Alternative Ways to Provide Values
You don't HAVE to use terraform.tfvars. Here are your options:

Option 1: terraform.tfvars (Recommended ✅)

terraform apply
# Automatically reads terraform.tfvars
Enter fullscreen mode Exit fullscreen mode

Option 2: Custom .tfvars file

terraform apply -var-file="production.tfvars"
Enter fullscreen mode Exit fullscreen mode

Option 3: Command-line flags

terraform apply -var="vm_name=my-vm" -var="location=East US"
# Gets tedious with many variables!
Enter fullscreen mode Exit fullscreen mode

Option 4: Environment variables

export TF_VAR_vm_name="my-vm"
export TF_VAR_location="East US"
terraform apply
Enter fullscreen mode Exit fullscreen mode

Option 5: Interactive prompts

terraform apply
# Terraform prompts you:
# var.vm_name
#   Enter a value: _
Enter fullscreen mode Exit fullscreen mode

**

Best Practices

**
✅ DO:

  1. Always create variables.tf - it's your documentation
  2. Use terraform.tfvars for local development
  3. Create terraform.tfvars.example with dummy values to commit
  4. Use separate .tfvars files per environment
  5. Add terraform.tfvars to*.gitignore*
  6. Use descriptive variable names and descriptions
  7. Set reasonable defaults where appropriate
  8. Use validation rules for critical variables

❌ DON'T:

  1. Commit terraform.tfvars with real secrets to Git
  2. Put actual values in variables.tf (except defaults)
  3. Use the same values across all environments
  4. Skip variable descriptions
  5. Forget to document required variables

Image description

Practical Example

💻
Let's say you're deploying Azure VMs with Terraform:
variables.tf

variable "subscription_id" {
  description = "Azure Subscription ID"
  type        = string
  sensitive   = true
}

variable "resource_group_name" {
  description = "Name of the resource group"
  type        = string
}

variable "location" {
  description = "Azure region"
  type        = string
  default     = "East US"
}

variable "vm_size" {
  description = "Size of the VM"
  type        = string
  default     = "Standard_B2s"
}

variable "environment" {
  description = "Environment (dev/staging/prod)"
  type        = string

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Must be dev, staging, or prod"
  }
}
Enter fullscreen mode Exit fullscreen mode

dev.tfvars

subscription_id     = "dev-subscription-id-here"
resource_group_name = "rg-myapp-dev"
location            = "East US"
vm_size             = "Standard_B2s"  # Smaller for dev
environment         = "dev"
Enter fullscreen mode Exit fullscreen mode

prod.tfvars

subscription_id     = "prod-subscription-id-here"
resource_group_name = "rg-myapp-prod"
location            = "East US"
vm_size             = "Standard_D4s_v3"  # Larger for prod
environment         = "prod"
Enter fullscreen mode Exit fullscreen mode

Deploy:

terraform apply -var-file="dev.tfvars"   # For development
terraform apply -var-file="prod.tfvars"  # For production
Enter fullscreen mode Exit fullscreen mode

Conclusion

🎓
Remember the golden rule:

variables.tf = The questions (template/schema)
terraform.tfvars = The answers (your specific values)

This separation gives you:

✅ Reusable code across environments
✅ Better security (secrets not in code)
✅ Type safety and validation
✅ Clear documentation
✅ Team collaboration

Now go forth and Terraform with confidence! 🚀

Found this helpful? Give it a ❤️ and follow me for more DevOps and Cloud content!
Questions? Drop them in the comments below! 👇

Related Articles
https://learn.microsoft.com/en-us/azure/developer/terraform/provision-infrastructure-using-azure-deployment-slots

https://www.codegenes.net/blog/best-practices-when-using-terraform-is-there-any-official-guide-to-it/

Top comments (0)