Terraform Cheat Sheet: Commands, HCL Syntax and Examples

Tested on Ubuntu 26.04 LTS (Resolute Raccoon)
Package terraform 1.15.8-1
Applies to Any host with Terraform installed
Lab environment Ubuntu VM with Terraform — Terraform lab environment on Ubuntu
Privilege Normal user
Man page Terraform CLI commands
Scope Quick reference for Terraform CLI commands, configuration files, HCL block syntax, variables, expressions, meta-arguments, state and import, modules, providers, workspaces, and debugging. Not a full tutorial, function catalog, or cloud-provider guide.
Related guides Terraform Associate certification course
terraform init
terraform plan
terraform apply
Terraform variables

Keep this page open beside your terminal when you need a command or HCL pattern fast. Each entry gives quick-reference syntax, a one-line purpose, and a link to the full lesson when you need depth. Examples use terraform_data and hashicorp/local so nothing here requires cloud credentials.


Terraform CLI Commands Cheat Sheet

Command Purpose
terraform version Show Terraform CLI version
terraform init Initialize working directory — backend, providers, modules
terraform fmt Format .tf files to canonical style
terraform validate Check configuration syntax and internal consistency
terraform plan Preview proposed create, update, replace, and destroy actions
terraform apply Execute planned infrastructure changes
terraform destroy Destroy all resources managed by this configuration
terraform show Display current state or a saved plan file
terraform output Print root-module output values

Common flag variations:

bash
terraform init
terraform init -upgrade
terraform init -reconfigure

terraform fmt
terraform fmt -recursive
terraform fmt -check

terraform validate

terraform plan
terraform plan -out=tfplan
terraform plan -var="environment=dev"
terraform plan -var-file="dev.tfvars"
terraform plan -destroy
terraform plan -refresh-only
terraform plan -detailed-exitcode

terraform show tfplan

terraform apply
terraform apply tfplan
terraform apply -auto-approve

terraform destroy
terraform destroy -auto-approve
Flag / pattern One-line purpose
init -upgrade Reconsider provider versions within configured constraints
init -reconfigure Reinitialize backend without migrating state
fmt -recursive Format child directories too
fmt -check Exit non-zero when files need formatting (CI)
plan -out=tfplan Save plan for later terraform apply tfplan
plan -detailed-exitcode Exit 0 = no changes, 2 = changes pending, 1 = error
plan -refresh-only Propose state refresh without other resource changes
apply -auto-approve Skip interactive yes prompt — labs and automation only

Deeper walkthroughs: terraform init, terraform fmt, terraform validate, terraform plan, terraform apply, terraform destroy.


Terraform Files and Directory Quick Reference

Common filenames in a module directory:

text
main.tf
variables.tf
outputs.tf
providers.tf
versions.tf
terraform.tfvars
*.auto.tfvars

Terraform loads every *.tf and *.tf.json file in the working directory as one module. Names like main.tf and variables.tf are conventions only — Terraform does not require them.

Generated or managed paths:

Path Role
.terraform/ Provider plugins, module cache, backend metadata — recreated by terraform init
.terraform.lock.hcl Provider version selections and checksums — commit for reproducible init
terraform.tfstate Current state snapshot (local backend default)
terraform.tfstate.backup Previous state snapshot after a write

Do not hand-edit state files or .terraform.lock.hcl unless you know exactly why. Delete .terraform/ to force a fresh init; never delete terraform.tfstate while managed resources still exist.


Terraform HCL Syntax Quick Reference

Copyable block patterns for the constructs you reach for most often.

Terraform and provider blocks

hcl
terraform {
  required_version = ">= 1.12.0"

  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "~> 2.5"
    }
  }
}

provider "local" {}

Resource

hcl
resource "local_file" "example" {
  filename = "${path.module}/example.txt"
  content  = "Hello Terraform"
}

Data source

hcl
data "local_file" "example" {
  filename = "${path.module}/existing.txt"
}

Variable

hcl
variable "environment" {
  type        = string
  description = "Deployment environment"
  default     = "dev"
}

Local value

hcl
locals {
  name = "${var.environment}-app"
}

Output

hcl
output "application_name" {
  value = local.name
}

Module

hcl
module "example" {
  source = "./modules/example"

  environment = var.environment
}

Block-by-block tutorials: Terraform HCL syntax, Terraform resource, Terraform data sources, Terraform variables, Terraform output, Terraform locals, Terraform modules.


Terraform Variables and Values Cheat Sheet

Pass values at plan or apply time:

bash
terraform plan -var="environment=dev"
terraform plan -var-file="dev.tfvars"

export TF_VAR_environment="dev"

Inside a terraform.tfvars or *.auto.tfvars file:

hcl
environment = "dev"

CLI variable precedence (lowest to highest) on the Terraform CLI:

Priority Source
1 default in the variable block
2 TF_VAR_<name> environment variable
3 terraform.tfvars
4 terraform.tfvars.json
5 *.auto.tfvars / *.auto.tfvars.json (later file name wins)
6 -var and -var-file on the command line, in the order provided

HCP Terraform also supports workspace variables and variable sets with additional precedence rules — see HCP Terraform variables.

Common type constraints:

hcl
string
number
bool
list(string)
set(string)
map(string)

object({
  name        = string
  port        = number
  description = optional(string)
  environment = optional(string, "dev")
})

tuple([string, number])

Full precedence ladder and validation: Terraform variables. Type details: Terraform data types.


Terraform Expressions, References and Functions

Reference Syntax
Resource attribute local_file.example.content
Input variable var.environment
Local value local.name
Data source attribute data.local_file.example.content
Module output module.example.output_name

Conditional:

hcl
var.environment == "prod" ? 3 : 1

for expression:

hcl
[for name in var.names : upper(name)]

Splat:

hcl
local_file.example[*].id

Commonly used functions (not a complete catalog):

Category Functions
String upper(), lower(), trimspace(), replace(), split(), join()
Collection length(), keys(), values(), merge(), flatten(), distinct()
Type tostring(), tonumber(), tolist(), toset(), tomap()
Lookup lookup(), try(), can()
Encoding jsonencode(), jsondecode(), yamlencode(), yamldecode()
Filesystem file(), templatefile(), fileset()
Numeric min(), max(), ceil(), floor()

More operators and examples: Terraform expressions. Function reference depth: Terraform functions.


count, for_each and Lifecycle Quick Reference

count

hcl
resource "terraform_data" "example" {
  count = 3

  input = count.index
}

References: terraform_data.example[0], count.index

for_each

hcl
resource "terraform_data" "example" {
  for_each = toset(["web", "api"])

  input = each.value
}

References: each.key, each.value, terraform_data.example["web"]

Situation Prefer
Nearly identical numbered instances count
Stable instances identified by map or set keys for_each

Explicit dependency:

hcl
depends_on = [terraform_data.foundation]

Lifecycle meta-arguments:

hcl
lifecycle {
  create_before_destroy = true
  prevent_destroy       = true
  ignore_changes        = [content]
  replace_triggered_by  = [terraform_data.trigger]
}

ignore_changes uses attributes of the resource containing the lifecycle block; replace_triggered_by can reference other managed resources.

Dedicated lessons: Terraform count vs for_each, Terraform lifecycle, Terraform resource dependencies.


Terraform State, Import and Refactoring Commands

State subcommands inspect and edit the resource graph Terraform tracks between runs:

bash
terraform state list
terraform state show local_file.example
terraform state mv local_file.old local_file.new
terraform state rm local_file.example
terraform state pull

terraform state push replaces remote or local state with a file you supply. Use only during deliberate recovery — not as a routine command.

Import existing infrastructure:

bash
terraform import local_file.example /path/on/disk

Configuration-driven import block:

hcl
import {
  to = local_file.imported
  id = "/path/on/disk"
}

resource "local_file" "imported" {
  filename = "/path/on/disk"
  content  = "imported"
}

Refactoring without destroy:

hcl
moved {
  from = local_file.old
  to   = local_file.new
}

Stop managing a resource without destroying it:

hcl
removed {
  from = local_file.legacy

  lifecycle {
    destroy = false
  }
}

Refresh state without other changes:

bash
terraform plan -refresh-only
terraform apply -refresh-only

State depth: Terraform state explained, Terraform state commands, Terraform import, moved and removed blocks, drift and refresh-only.


Terraform Modules, Providers and Workspaces Quick Reference

Local module:

hcl
module "example" {
  source = "./modules/example"
}

Registry module:

hcl
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"
}

Provider inspection:

bash
terraform providers
terraform providers lock

Provider alias:

hcl
provider "aws" {
  alias  = "west"
  region = "us-west-2"
}

resource "aws_s3_bucket" "west" {
  provider = aws.west
  bucket   = "example-west"
}

Pass provider into a module:

hcl
module "example" {
  source = "./modules/example"

  providers = {
    aws = aws.west
  }
}

CLI workspace commands (separate state instances within the same backend, when supported):

bash
terraform workspace list
terraform workspace show
terraform workspace new dev
terraform workspace select dev
terraform workspace delete dev

CLI workspaces are not the same as HCP Terraform workspaces — the name collides, but HCP workspaces are remote state containers with run history and settings.

Further reading: Terraform providers, provider aliases in modules, Terraform modules, Terraform workspaces.


Terraform Debugging and Useful Environment Variables

Turn on verbose logging when a plan or apply fails without a clear error, then re-run the command that misbehaved:

bash
export TF_LOG=DEBUG
export TF_LOG_PATH=terraform.log

terraform validate
terraform providers
terraform state list
terraform show

Log levels (most to least verbose): TRACE, DEBUG, INFO, WARN, ERROR

Variable Purpose
TF_LOG Log verbosity for Terraform operations
TF_LOG_PATH Append enabled Terraform logs to a specified file; requires TF_LOG
TF_VAR_<name> Set root-module input variable values
TF_CLI_ARGS Extra arguments appended to every command
TF_CLI_ARGS_plan Extra arguments appended only to terraform plan
TF_DATA_DIR Override default .terraform metadata directory
TF_WORKSPACE Select workspace without terraform workspace select

Full debugging workflow: Terraform debug logging, Terraform troubleshooting.


Terraform Command Workflow Quick Reference

Day-to-day change cycle:

bash
terraform fmt
terraform init
terraform validate
terraform plan
terraform apply

Teardown:

bash
terraform plan -destroy
terraform destroy

Memorization flow:

text
Edit configuration
terraform fmt
terraform validate
terraform plan
Review changes
terraform apply
Verify outputs / state

Terraform — interview corner

What is the difference between terraform plan and terraform apply?

terraform plan compares configuration, state, and refreshed provider data, then prints a proposed change set without applying create, update, or destroy actions. terraform apply executes those changes and updates persistent state when operations succeed.

A strong answer is:

"Plan is the safety preview; apply is where infrastructure actually changes."

Why does Terraform need state?

State maps resource addresses in configuration (local_file.example) to real object IDs and last-known attributes. Providers identify objects by their own IDs, not by Terraform addresses, so state is how Terraform knows which API object to update or destroy on the next run.

A strong answer is:

"State is the ledger that binds configuration names to real infrastructure."

When should you use count versus for_each?

Use count when you need a fixed number of nearly identical instances indexed by integer. Use for_each when each instance is identified by a stable string key from a map or set — especially when instances may be added or removed without shifting indexes.

A strong answer is:

"count for numbered clones; for_each when the key matters and should stay stable."

Does marking a variable sensitive keep it out of state?

No. sensitive = true redacts values in many CLI outputs, but state can still store the value. Treat state files and remote backends as sensitive when they hold secrets.

A strong answer is:

"Sensitive controls display; state access still needs restriction."

How do Terraform CLI workspaces differ from HCP Terraform workspaces?

CLI workspaces are extra named state instances for one configuration on one backend. HCP Terraform workspaces are remote containers with their own state, variables, run history, and access control in a hosted organization.

A strong answer is:

"CLI workspaces provide separate state instances within one backend; HCP workspaces are separate managed infrastructure workspaces."


References

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)