Skip to content

Terraform 80/20 — Commands, Concepts, Context

The Core Workflow

Every Terraform task follows this exact sequence. You will use these commands on every single thing you do.

# 1 - Initialise a working directory
# Downloads providers and modules defined in your config.
# Run this first, always. Nothing works without it.
terraform init

# 2 - Preview what Terraform will do
# Shows what will be created, changed, or destroyed.
# Nothing is applied. Read this carefully before applying.
terraform plan

# 3 - Apply the changes
# Actually creates/modifies/destroys infrastructure.
# Asks for confirmation unless you pass -auto-approve.
terraform apply

# 4 - Destroy all infrastructure managed by this config
# Tears everything down. Useful for labs/cleanup.
# Same as apply but in reverse.
terraform destroy

Inspecting State

Terraform tracks what it created in a state file (terraform.tfstate). State is the source of truth — it's how Terraform knows what exists. These commands let you inspect and manage it.

# 5 - List all resources Terraform is tracking
terraform state list

# 6 - Show details of a specific tracked resource
# Use after state list to get the resource address
terraform state show <resource_address>

# 7 - Show current state of all resources (full output)
terraform show

# 8 - Remove a resource from state WITHOUT destroying it
# Use when you want Terraform to forget about something
# without actually deleting it from the infrastructure
terraform state rm <resource_address>

# 9 - Import existing infrastructure into state
# Use when something already exists and you want
# Terraform to start managing it
terraform import <resource_address> <real_world_id>

Validation and Formatting

Before applying, always validate and format. Catches syntax errors early. Keeps code readable.

# 10 - Check config files for syntax errors
# Does not check against provider APIs — just HCL syntax
terraform validate

# 11 - Auto-format .tf files to canonical style
# Run this before committing code
terraform fmt

# 12 - Recursive format (all subdirectories)
terraform fmt -recursive

Variables and Outputs

Variables let you reuse configs. Outputs expose values after apply (e.g. IP addresses, IDs). You'll use both constantly.

# 13 - Pass a variable value on the command line
terraform apply -var="instance_type=t2.micro"

# 14 - Pass variables from a file
# .tfvars files hold key=value pairs
terraform apply -var-file="prod.tfvars"

# 15 - Show all output values after apply
terraform output

# 16 - Show a specific output value
terraform output <output_name>

Workspaces

Workspaces let you manage multiple environments (dev, staging, prod) from the same config. Each workspace has its own state file. Default workspace is called "default".

# 17 - List all workspaces
terraform workspace list

# 18 - Create a new workspace
terraform workspace new <name>

# 19 - Switch to a workspace
terraform workspace select <name>

# 20 - Show current workspace
terraform workspace show

Providers and Modules

Providers are plugins that talk to APIs (AWS, GCP, Azure). Modules are reusable chunks of config.

# 21 - Show required providers and their versions
terraform providers

# 22 - Upgrade providers to latest allowed version
terraform init -upgrade

# 23 - Download modules defined in config
# Actually just terraform init — modules are fetched on init
terraform get

Targeting and Partial Applies

Sometimes you only want to apply or destroy one specific resource. Use -target. Avoid this in production — it creates state drift.

# 24 - Apply only a specific resource
terraform apply -target=<resource_address>

# 25 - Destroy only a specific resource
terraform destroy -target=<resource_address>

Useful Flags

# Skip the yes/no confirmation prompt
terraform apply -auto-approve
terraform destroy -auto-approve

# Save a plan to a file, then apply exactly that plan
terraform plan -out=myplan.tfplan
terraform apply myplan.tfplan

# See verbose debug output (use when something is broken)
TF_LOG=DEBUG terraform apply

Key Concepts (no terminal needed)

HCL Blocks — the 5 you must know

# Provider — tells Terraform which API to talk to
provider "aws" {
  region = "us-east-1"
}

# Resource — the thing you're creating
resource "aws_instance" "web" {
  ami           = "ami-12345"
  instance_type = "t2.micro"
}

# Variable — input value, makes config reusable
variable "instance_type" {
  type    = string
  default = "t2.micro"
}

# Output — expose a value after apply
output "instance_ip" {
  value = aws_instance.web.public_ip
}

# Data source — READ existing infrastructure, don't create it
data "aws_ami" "latest" {
  most_recent = true
  owners      = ["amazon"]
}

State — what it is and why it matters

  • Terraform stores what it created in terraform.tfstate
  • Every apply reads state to know what already exists
  • State is the diff between "what the code says" and "what's real"
  • If state is lost or corrupted, Terraform doesn't know what it owns
  • Never edit state manually — use terraform state commands
  • In teams, state is stored remotely (S3 + DynamoDB for AWS) so multiple people don't conflict

The Dependency Graph

  • Terraform figures out the order to create resources automatically
  • If resource B references resource A, Terraform creates A first
  • You don't specify order — it's inferred from references
  • terraform graph outputs a visual dependency graph (DOT format)

Modules

  • A module is just a folder with .tf files
  • Every Terraform config is technically a module (the root module)
  • You call child modules to reuse config across environments
  • Input = variables, Output = outputs
  • Public modules available at registry.terraform.io

Remote Backend (S3 example)

terraform {
  backend "s3" {
    bucket         = "my-tf-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "tf-state-lock"   # prevents concurrent applies
  }
}
- Local state is fine for solo/lab work - Remote state is required for teams - DynamoDB table provides state locking — prevents two people applying at once


Lifecycle Rules

resource "aws_instance" "web" {
  ami           = "ami-12345"
  instance_type = "t2.micro"

  lifecycle {
    create_before_destroy = true   # create replacement before deleting old
    prevent_destroy       = true   # block destroy — good for databases
    ignore_changes        = [tags] # don't update resource if only tags changed
  }
}

Exam Cheat Reference

Concept What to remember
init downloads providers + modules, run first always
plan preview only, nothing changes
apply makes real changes, asks confirmation
state source of truth, never edit manually
tfstate local file storing state, don't commit to git
workspace separate state per environment, same config
module reusable folder of .tf files
provider plugin that talks to an API (AWS, GCP etc)
data source reads existing infra, doesn't create anything
remote backend stores state in S3/GCS for team use
-target apply/destroy one resource only, avoid in prod
prevent_destroy lifecycle rule to block accidental deletion
terraform.tfvars auto-loaded variable file, don't commit secrets