Menu

Topic 2 of 8

Terraform Basics

Learn Terraform Basics for free with explanations, exercises, and a quick test (for Platform Engineer).

Published: January 23, 2026 | Updated: January 23, 2026

Who this is for

You build and operate infrastructure as a Platform Engineer or Backend Engineer. You want reproducible environments, fast provisioning, and safer changes.

Prerequisites

  • Basic CLI skills (shell or PowerShell)
  • Installed: Terraform 1.4+ on your machine
  • Optional: Cloud account credentials if you want to try provider-specific resources (not required for local examples)

Learning path

  1. Core workflow: init → validate → plan → apply → destroy
  2. Providers, resources, data sources
  3. Variables, outputs, tfvars
  4. State and backends (what they are and why they matter)
  5. Modules and composition

Why this matters

  • Provision environments consistently for dev/stage/prod.
  • Review changes before they happen (plans in PRs).
  • Detect and fix drift from manual changes.
  • Spin up ephemeral environments for feature branches.
  • Enable self-service infrastructure for product teams.

Concept explained simply

Terraform reads configuration files (.tf), compares the desired state to the current state, and produces a plan. When you apply, Terraform makes API calls to providers to reach the desired state. The state file records what was created so Terraform can track and update resources safely.

Mental model

  • Configuration = your recipe.
  • Providers = the kitchens you can cook in (AWS, Azure, GCP, Kubernetes, local).
  • Resources = the dishes to create (buckets, VMs, files).
  • State = the ledger of what exists and how it maps to your config.
  • Plan = the diff between recipe and ledger.
  • Apply = executing the diff.
Key terms you will use
  • provider: plugin that talks to an API (e.g., aws, azurerm, google, kubernetes, local, random).
  • resource: something Terraform manages (e.g., aws_s3_bucket, local_file).
  • data source: reads data from an API without creating it.
  • variable: inputs you can pass to configs.
  • output: values printed after apply, often consumed by other tools.
  • module: a folder of .tf files you can reuse and call.
  • state: mapping between your config and real-world objects.
  • workspace: named state instances (e.g., default, dev, prod).

Core workflow

  1. terraform init — download providers and set up the working directory.
  2. terraform validate — static checks for config correctness.
  3. terraform plan — preview changes; no side effects.
  4. terraform apply — execute the plan to reach desired state.
  5. terraform destroy — remove all managed resources in the workspace.
Safety tips
  • Do not commit secrets or terraform.tfstate to public repos.
  • Pin provider versions to avoid unexpected upgrades.
  • Use variables and tfvars for environment-specific values.
  • Avoid provisioners unless absolutely necessary.

Worked examples

Example 1: Write a local file (safe to run)

Creates a text file on your machine using the local provider.

# files: main.tf
terraform {
  required_version = ">= 1.4.0"
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.4"
    }
  }
}

provider "local" {}

variable "message" {
  type        = string
  description = "Content of the file"
  default     = "Hello, Terraform!"
}

resource "local_file" "note" {
  content  = var.message
  filename = "${path.module}/hello.txt"
}

output "file_path" {
  value = local_file.note.filename
}
  • Run: terraform init → terraform plan → terraform apply
  • Result: hello.txt is created; output shows the file path.

Example 2: Add a random suffix and variables

Demonstrates dependencies and idempotency.

# files: main.tf
terraform {
  required_providers {
    local = { source = "hashicorp/local", version = "~> 2.4" }
    random = { source = "hashicorp/random", version = "~> 3.5" }
  }
}

provider "local" {}
provider "random" {}

variable "message" { type = string, default = "Hello" }

resource "random_id" "suffix" { byte_length = 2 }

resource "local_file" "note" {
  content  = "${var.message} - ${random_id.suffix.hex}"
  filename = "${path.module}/hello-${random_id.suffix.hex}.txt"
}

output "file_path" { value = local_file.note.filename }
  • First apply creates the file with a random suffix.
  • Re-running apply without changes should show No changes.
  • Forcing regeneration: terraform apply -replace="random_id.suffix" will create a new file.

Example 3: Create and reuse a simple module

Creates two files via a reusable module.

# files: modules/two_files/main.tf
variable "name" { type = string }

resource "local_file" "a" {
  filename = "${path.module}/${var.name}-a.txt"
  content  = "module ${var.name} - A"
}

resource "local_file" "b" {
  filename = "${path.module}/${var.name}-b.txt"
  content  = "module ${var.name} - B"
}

output "files" {
  value = [local_file.a.filename, local_file.b.filename]
}

# files: main.tf (root)
terraform {
  required_providers {
    local = { source = "hashicorp/local", version = "~> 2.4" }
  }
}
provider "local" {}

module "alpha" { source = "./modules/two_files" name = "alpha" }
module "beta"  { source = "./modules/two_files" name = "beta" }

output "all_files" {
  value = concat(module.alpha.files, module.beta.files)
}
  • Run: terraform init then terraform apply.
  • Result: four files and an output list of their paths.
Optional cloud example: AWS S3 bucket (do not apply without credentials)
terraform {
  required_providers {
    aws = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}
provider "aws" { region = var.aws_region }

variable "aws_region" { type = string, default = "us-east-1" }
variable "bucket_name" { type = string }

resource "aws_s3_bucket" "b" {
  bucket = var.bucket_name
  tags = { ManagedBy = "Terraform" }
}

output "bucket_name" { value = aws_s3_bucket.b.bucket }

Plan only if you don’t have credentials set up. Apply only in a safe sandbox account.

Exercises

Note: The quick test is available to everyone; sign in to save your progress.

Exercise 1 — Config file generator (local)

Create a Terraform config that writes a JSON file config.json containing two fields: env and service. Values must come from variables.

  • Use the local provider and a single resource local_file.
  • Expose an output named file_path showing the absolute path.
  • Run init → plan → apply. Verify the file content.

Exercise 2 — Reusable module (local)

Create a module logset that writes two files: {name}-app.log and {name}-audit.log. Call the module twice with different names.

  • Module variable: name (string).
  • Output: files (list of file paths).
  • Root outputs: combined list of all created files.

Exercise checklist

  • terraform init completes with providers downloaded.
  • terraform validate passes.
  • terraform plan shows only expected adds, no destroys.
  • terraform apply succeeds and outputs are printed.
  • Re-running apply shows No changes.

Common mistakes and self-check

  • Forgetting init: If plan fails with provider errors, run terraform init.
  • Committing state: Add terraform.tfstate, terraform.tfstate.backup, and .terraform/ to .gitignore.
  • Hardcoding secrets: Use variables, environment variables, and mark sensitive = true when needed.
  • Unstable resource keys: Prefer for_each with stable map keys instead of count for sets that can reorder.
  • Manual changes in console: Causes drift. Detect with plan; fix by updating code or importing resources.
  • Unpinned providers: Pin versions to avoid breaking changes.
Self-check routine
  • Run: terraform fmt -check, terraform validate.
  • Run plan twice; second plan should show 0 to change.
  • Change a variable value and confirm plan shows only intended changes.

Practical projects

  • Local bootstrap: Use local and random providers to generate project skeleton files (README, .env.example) with variables and outputs.
  • Template files: Create multiple environment-specific config files using variables and a module.
  • Ephemeral artifacts: Use workspaces (default/dev) and observe separate state and outputs.

Next steps

  • Learn remote state backends and locking (e.g., S3 + DynamoDB, Terraform Cloud).
  • Add CI to run terraform fmt, validate, and plan on pull requests.
  • Introduce modules for each service and adopt versioned releases.

Mini challenge

Build a module website that creates two files: index.html and styles.css using the local provider.

  • Inputs: site_name (string), primary_color (string).
  • Outputs: file paths of both files.
  • Idempotent: running apply twice must show no changes.
  • Stretch: add a random suffix variable (toggle via boolean) to filenames.

Practice Exercises

2 exercises to complete

Instructions

Create a Terraform project that writes a JSON file config.json with fields env and service from variables.

  • Use provider local.
  • Resource: local_file.
  • Output: file_path (absolute path).
  • Run init → validate → plan → apply and verify file content.
Expected Output
terraform plan shows 1 to add; terraform apply creates config.json and prints output file_path.

Terraform Basics — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about Terraform Basics?

AI Assistant

Ask questions about this tool