Fatskills
Practice. Master. Repeat.
Study Guide: TECH **Terraform Variables & .tfvars: Zero-Fluff, Hands-On Study Guide**
Source: https://www.fatskills.com/cloud-application-developer/chapter/tech-terraform-variables-tfvars-zero-fluff-hands-on-study-guide

TECH **Terraform Variables & .tfvars: Zero-Fluff, Hands-On Study Guide**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~7 min read

Terraform Variables & .tfvars: Zero-Fluff, Hands-On Study Guide

(For Cloud Engineers, DevOps, and Certification Prep)


1. What This Is & Why It Matters

Variables in Terraform are how you make your infrastructure code reusable, secure, and adaptable. Without them, you’d hardcode every value (e.g., instance_type = "t3.micro")—which means: - No environment separation (dev vs. prod would require duplicate code).
- No secrets management (API keys, passwords, or DB credentials would leak in Git).
- No flexibility (changing a single value would require editing every file).

Real-world scenario:
You’re deploying a multi-region AWS setup. Your dev environment uses t3.small instances, while prod needs m5.large. Without variables, you’d maintain two nearly identical Terraform configurations—a maintenance nightmare. With variables, you define the instance type once and override it per environment using .tfvars files.

What breaks if you ignore this?
- Security breaches: Hardcoded secrets in Git.
- Cost overruns: Accidentally deploying prod-sized resources in dev.
- Failed audits: No traceability of who changed what (variables + .tfvars enable version-controlled config).


2. Core Concepts & Components


1. variable Block

  • Definition: Declares a variable in Terraform (name, type, default, description).
  • Production insight: Always add a description—it’s free documentation and appears in terraform plan.
    hcl variable "instance_type" {
    type = string
    default = "t3.micro"
    description = "EC2 instance type for the web server" }

2. Primitive Types (string, number, bool)

  • Definition: Basic data types for single values.
  • string: "us-east-1", "t3.micro"
  • number: 42, 0.01 (for CPU shares)
  • bool: true, false (e.g., enable_monitoring = true)
  • Production insight: Use number for math (e.g., desired_capacity = 2), not strings ("2").

3. Complex Types (list, map)

  • Definition:
  • list: Ordered collection of values (e.g., ["subnet-123", "subnet-456"]).
  • map: Key-value pairs (e.g., { dev = "t3.micro", prod = "m5.large" }).
  • Production insight: Maps are gold for environment-specific configs. Example: hcl variable "instance_types" {
    type = map(string)
    default = {
    dev = "t3.micro"
    prod = "m5.large"
    } }

4. sensitive Variables

  • Definition: Marks a variable as sensitive (Terraform won’t log its value in CLI output).
    hcl variable "db_password" {
    type = string
    sensitive = true }
  • Production insight: Never hardcode secrets. Use sensitive = true + .tfvars + a secrets manager (AWS Secrets Manager, HashiCorp Vault).

5. .tfvars Files

  • Definition: Files (e.g., dev.tfvars, prod.tfvars) that assign values to variables.
  • Production insight: Always exclude .tfvars from Git (add to .gitignore). Example: # dev.tfvars instance_type = "t3.micro" db_password = "s3cr3t!" # ⚠️ Still bad—use a secrets manager!

6. Variable Precedence

  • Definition: Order Terraform uses to resolve variable values (lowest to highest priority):
  • Defaults in variable blocks.
  • .tfvars files (loaded automatically if named terraform.tfvars or *.auto.tfvars).
  • -var CLI flags (e.g., terraform apply -var="instance_type=t3.large").
  • Environment variables (TF_VAR_instance_type=t3.large).
  • Production insight: Use .tfvars for environment-specific values and -var for one-off overrides (e.g., testing).

7. locals (Bonus)

  • Definition: Computed values inside Terraform (not user-provided).
    hcl locals {
    common_tags = {
    Environment = var.env
    Terraform = "true"
    } }
  • Production insight: Use locals to avoid repeating complex expressions (e.g., tagging logic).


3. Step-by-Step Hands-On: Deploy a Multi-Environment AWS EC2 Instance


Prerequisites

  • AWS account with IAM permissions for EC2.
  • Terraform installed (terraform -v should work).
  • AWS CLI configured (aws sts get-caller-identity).

Task

Deploy a dev and prod EC2 instance with different instance types using variables and .tfvars.


Step 1: Create the Terraform Config

  1. Make a new directory:
    bash
    mkdir tf-variables-demo && cd tf-variables-demo
  2. Create main.tf:
    ```hcl
    terraform {
    required_providers {
    aws = {
    source = "hashicorp/aws"
    version = "~> 5.0"
    }
    }
    }

provider "aws" {
region = var.aws_region
}

resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
tags = merge(
local.common_tags,
{ Name = "${var.env}-web-server" }
)
}

data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
}

locals {
common_tags = {
Environment = var.env
Terraform = "true"
}
}
3. Create `variables.tf`:hcl
variable "aws_region" {
type = string
default = "us-east-1"
description = "AWS region to deploy resources"
}

variable "instance_type" {
type = string
description = "EC2 instance type"
}

variable "env" {
type = string
description = "Environment (dev/prod)"
}
```


Step 2: Create .tfvars Files

  1. Create dev.tfvars:
    hcl
    aws_region = "us-east-1"
    instance_type = "t3.micro"
    env = "dev"
  2. Create prod.tfvars:
    hcl
    aws_region = "us-east-1"
    instance_type = "m5.large"
    env = "prod"

Step 3: Deploy Dev Environment

  1. Initialize Terraform:
    bash
    terraform init
  2. Plan with dev.tfvars:
    bash
    terraform plan -var-file="dev.tfvars"
  3. Expected output: Terraform shows a t3.micro instance will be created.
  4. Apply:
    bash
    terraform apply -var-file="dev.tfvars" -auto-approve
  5. Verify in AWS Console:
  6. Go to EC2 > Instances. You should see a dev-web-server.

Step 4: Deploy Prod Environment

  1. Destroy the dev instance (to avoid costs):
    bash
    terraform destroy -var-file="dev.tfvars" -auto-approve
  2. Apply prod config:
    bash
    terraform apply -var-file="prod.tfvars" -auto-approve
  3. Verify:
  4. AWS Console should now show an m5.large instance named prod-web-server.

Step 5: Clean Up

terraform destroy -var-file="prod.tfvars" -auto-approve


4. ? Production-Ready Best Practices


Security

  • Never commit .tfvars to Git: Add *.tfvars to .gitignore.
  • Use sensitive = true for secrets: But still never hardcode them—use AWS Secrets Manager or HashiCorp Vault.
  • Environment variables for secrets: Set TF_VAR_db_password in CI/CD instead of .tfvars.

Cost Optimization

  • Use maps for environment-specific sizing: hcl variable "instance_types" {
    type = map(string)
    default = {
    dev = "t3.micro"
    prod = "m5.large"
    } }
  • Default to smallest viable instance: Override in prod via .tfvars.

Reliability & Maintainability

  • Name variables clearly: db_instance_class > instance_type (avoids ambiguity).
  • Use description in variable blocks: Future-you (or your team) will thank you.
  • Validate inputs with validation: hcl variable "env" {
    type = string
    description = "Environment (dev/stage/prod)"
    validation {
    condition = contains(["dev", "stage", "prod"], var.env)
    error_message = "Environment must be dev, stage, or prod."
    } }

Observability

  • Tag everything: Use locals for common tags (e.g., Environment, Terraform).
  • Log variable values in CI/CD: Add terraform plan -out=tfplan && terraform show -json tfplan to your pipeline.


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Hardcoding secrets in .tfvars Git history leaks passwords. Use sensitive = true + secrets manager (AWS Secrets Manager, Vault).
Forgetting -var-file in apply Terraform uses defaults (e.g., t3.micro in prod). Always specify -var-file or use terraform.tfvars.
Using string for numbers desired_capacity = "2" fails in math ops. Use number type for numeric values.
Not setting sensitive = true terraform plan logs DB passwords. Mark secrets as sensitive.
Overriding variables in wrong order -var CLI flag doesn’t work. Remember precedence: defaults < .tfvars < -var < TF_VAR_*.


6. ? Exam/Certification Focus


Typical Question Patterns

  1. Variable precedence:
  2. Question: "You run terraform apply -var="instance_type=t3.large" but the instance is still t3.micro. Why?"
  3. Answer: A terraform.tfvars file is overriding the CLI flag (precedence issue).

  4. Sensitive variables:

  5. Question: "How do you prevent Terraform from logging a DB password in the CLI output?"
  6. Answer: Set sensitive = true in the variable block.

  7. Complex types:

  8. Question: "You need to deploy subnets in multiple AZs. Which variable type should you use?"
  9. Answer: list(string) (e.g., ["us-east-1a", "us-east-1b"]).

Key Trap Distinctions

  • .tfvars vs. terraform.tfvars:
  • terraform.tfvars is loaded automatically.
  • Other .tfvars files must be specified with -var-file.
  • sensitive vs. encryption:
  • sensitive = true hides values from logs but does not encrypt them in state.
  • For encryption, use a backend (e.g., S3 with KMS) + secrets manager.


7. ? Hands-On Challenge

Challenge: Deploy an S3 bucket with a variable for the bucket name. Use a map to set different ACLs for dev (private) and prod (public-read). Test both environments.

Solution: 1. variables.tf:
```hcl
variable "env" {
type = string
}

variable "bucket_acls" {
type = map(string)
default = {
dev = "private"
prod = "public-read"
}
}
2. `main.tf`:hcl
resource "aws_s3_bucket" "demo" {
bucket = "my-bucket-${var.env}"
acl = var.bucket_acls[var.env]
}
3. `dev.tfvars`:hcl
env = "dev"
4. Deploy:bash
terraform apply -var-file="dev.tfvars"
```
- Verify in AWS Console: The bucket should be private.

Why it works: - The map (bucket_acls) lets you define environment-specific ACLs in one place.
- var.bucket_acls[var.env] dynamically selects the ACL based on the environment.


8. ? Rapid-Reference Crib Sheet

Concept Command/Example Notes
Declare a variable variable "instance_type" { type = string } Always add description.
Set default value default = "t3.micro" Overridden by .tfvars or CLI.
Use a variable instance_type = var.instance_type Reference with var.<name>.
List variable variable "subnets" { type = list(string) } Use var.subnets[0] to access elements.
Map variable variable "instance_types" { type = map(string) } Access with var.instance_types["dev"].
Sensitive variable variable "db_password" { sensitive = true } Hides value in logs but not encrypted.
.tfvars file terraform apply -var-file="prod.tfvars" terraform.tfvars is loaded automatically.
CLI override terraform apply -var="instance_type=t3.large" Highest precedence (except TF_VAR_* env vars).
Environment variable export TF_VAR_instance_type=t3.large Useful for CI/CD.
Validation validation { condition = contains(["dev", "prod"], var.env) } Prevents invalid values.
Locals locals { common_tags = { Terraform = "true" } } Computed values (not user-provided).
⚠️ .tfvars in Git Never commit .tfvars! Add to .gitignore. Use secrets manager for sensitive values.
⚠️ Default region AWS provider defaults to us-east-1 if not set. Always set explicitly.


9. ? Where to Go Next

  1. Terraform Variables Docs – Official reference.
  2. Terraform Sensitive Variables – How to hide secrets.
  3. AWS Secrets Manager + Terraform – Secure secrets management.
  4. Terraform Best Practices – Real-world patterns.


ADVERTISEMENT