Skip to main content

Command Palette

Search for a command to run...

The Ultimate Terraform Cheatsheet for DevOps Engineers

Updated
•21 min read•View as Markdown
A
Hi, I'm Atul! I am a software engineer with a strong focus on DevOps, Cloud Infrastructure, and automation. I spend my time building scalable CI/CD pipelines, mastering tools like Docker, Terraform, and AWS, and breaking down complex cloud concepts into accessible, hands-on tutorials. I believe the best way to learn is to build, break, and document the process.

Whether you are spinning up your very first AWS EC2 instance or managing a massive, multi-region Kubernetes cluster, Terraform is the undisputed king of Infrastructure as Code (IaC).

However, remembering every CLI flag, HCL block type, and state management command is nearly impossible. I built this comprehensive cheatsheet as a quick reference guide for my own daily DevOps workflows, and I am sharing it here so you can bookmark it and use it too.

☕ If this cheatsheet saves you time at work today, consider buying me a coffee to support the blog!


💡
Want to save this to your own workspace? You can click here to view and duplicate the live Notion version of this cheatsheet directly into your own Notion account!

⌨️CORE CLI COMMANDS

Workflow Commands

⌨ Command ℹ️ Description
terraform init Initialize working directory, download providers & modules
terraform init -upgrade Upgrade providers to latest allowed versions
terraform init -reconfigure Reconfigure backend (ignores existing state)
terraform validate Validate configuration syntax & internal consistency
terraform fmt Reformat .tf files to canonical HCL style
terraform fmt -recursive Format all files in directory tree
terraform plan Preview changes before applying
terraform plan -out=plan.tfplan Save plan to file for later apply
terraform apply Apply changes to infrastructure
terraform apply plan.tfplan Apply a saved plan file
terraform apply -auto-approve Apply without interactive approval prompt
terraform destroy Destroy all managed infrastructure
terraform destroy -auto-approve Destroy without interactive approval

Inspect & Query Commands

⌨ Command ℹ️ Description
terraform show Display current state or saved plan (human-readable)
terraform show -json plan.tfplan Output plan as JSON for machine parsing
terraform output Display all output values
terraform output <name> Display a specific output value
terraform output -json Display outputs as JSON
terraform state list List all resources in state file
terraform state show <resource> Show detailed attributes of a resource
terraform state pull Download & print current remote state
terraform state push Manually upload local state to remote backend
terraform providers Show providers required by configuration
terraform version Print Terraform and provider versions
terraform graph Generate DOT-format dependency graph

Planning & Targeting Flags

Flag ℹ️ Description
terraform plan -var 'k=v' Pass a single variable on CLI
terraform plan -var-file=f.tfvars Load variables from a file
terraform plan -target=aws_instance.web Plan only specific resource
terraform plan -replace=aws_instance.web Force replace a specific resource
terraform plan -refresh=false Skip refreshing state from real infra
terraform plan -parallelism=10 Set concurrency for resource operations
terraform apply -compact-warnings Show condensed warning messages
terraform apply -lock=false Disable state locking (use with caution)
terraform apply -lock-timeout=60s Wait up to N seconds to acquire lock

📄HCL LANGUAGE REFERENCE

Block Types

Block Type Kind Purpose
terraform {} terraform Global Terraform settings, required_providers, backend
provider {} provider Configure a provider (AWS, Azure, GCP, etc.)
resource {} resource Declare a managed infrastructure resource
data {} data source Read existing infrastructure or external data
variable {} variable Declare an input variable
output {} output Expose values to the caller or user
locals {} locals Define local computed values (like constants)
module {} module Embed a reusable Terraform module
moved {} moved Rename/move resources without destroy & recreate
import {} import Import existing infra into Terraform state (v1.5+) 🆕
check {} check Define custom validation assertions (v1.5+) 🆕

Data Types & Values

Type Example ℹ️ Description
string "hello" UTF-8 text
number 42 / 3.14 Integer or float
bool true / false Boolean value
list(type) ["a", "b"] Ordered sequence of values
set(type) toset(["a","b"]) Unordered unique values
map(type) {key = "value"} Key-value lookup table
object({}) {name="x", count=1} Named attribute collection
tuple([]) ["a", 1, true] Fixed-length mixed-type sequence
any any type Type constraint that accepts any type
null null Absence of a value

Expressions & Built-in Functions

  • String & Template

    name = "server-\({var.env}-\){count.index}" String interpolation
    desc = <<-EOT multi-line text here EOT Heredoc string
    upper(var.region) "us-east-1" → "US-EAST-1"
    replace("us-east-1", "-", "_") "us_east_1"
    split(",", "a,b,c") ["a","b","c"]
    join("-", ["a","b","c"]) "a-b-c"
    templatefile("tpl.tftpl", { port = 8080 }) Render template file
  • Collection Functions

    length(var.items) Count items
    flatten([[1,2],[3]]) [1,2,3]
    distinct(["a","b","a"]) ["a","b"]
    concat(["a"], ["b","c"]) ["a","b","c"]
    merge({a=1}, {b=2}) {a=1, b=2}
    toset(["a","b","a"]) set("a","b")
    tolist(toset(["b","a"])) ["a","b"]
    zipmap(["a","b"], [1,2]) {a=1, b=2}
    lookup(var.map, "key", "default") Safe map lookup
    contains(["a","b"], "a") true
    index(["x","y","z"], "y") 1
    element(["a","b","c"], 2) "c" (wraps around)
    slice(["a","b","c","d"], 1, 3) ["b","c"]
  • Numeric & Encoding

    min(5, 2, 9)  /  max(5, 2, 9)  /  abs(-3)  /  ceil(1.2)  /  floor(1.9)
    
    pow(2, 10)                                          # 1024
    
    base64encode("hello")  /  base64decode("aGVsbG8=")
    
    jsonencode({key = "val"})  /  jsondecode("{"key":"val"}")
    
    yamlencode({key = "val"})  /  yamldecode("key: val")
    
    md5("content")  /  sha256("content")  /  bcrypt("password")
    
    filebase64("path/to/file")  /  filemd5("file.txt")
    
  • Filesystem & Date

    file("path/to/file.txt")

    Read file as string

    fileexists("optional.tf")

    true/false

    fileset(path.module, "scripts/*.sh")

    List matching files

    timestamp()

    "2024-01-15T10:30:00Z"

    formatdate("YYYY-MM-DD", timestamp())

    "2024-01-15"

    timeadd(timestamp(), "24h")

    Add duration to time

  • Type Conversion & Conditionals

    can(var.optional_attr)

    true if no error

    try(resource.name, "fallback")

    Return first non-error

    coalesce("", null, "first-non-empty")

    "first-non-empty"

    coalescelist([], null, ["a","b"])

    ["a","b"]

    condition ? true_val : false_val

    Ternary operator

    one([])

    null | one(["x"])
    "x"
    Assert single element

    sensitive("secret") / nonsensitive(local.val)

    Sensitivity mark


🧱 RESOURCES & META-ARGUMENTS

Meta-Arguments Reference

Meta-Argument ℹ️ Description
count = N Create N identical copies of resource; use count.index to differentiate
count = var.enabled ? 1 : 0 Conditionally create a resource (0 = don't create)
for_each = var.map Create one resource per map entry; access key/value via each.key / each.value
for_each = toset(var.list) Create one resource per list item; each.key = each.value = item
depends_on = [res.x] Explicit dependency when implicit reference isn't sufficient
lifecycle { create_before_destroy = true } Create replacement before destroying old resource
lifecycle { prevent_destroy = true } Block resource from being destroyed (terraform error)
lifecycle { ignore_changes = [tags] } Ignore drift in specific attributes
lifecycle { replace_triggered_by = [var.x] } Replace resource when variable changes
provider = aws.useast2 Use non-default provider alias for this resource
timeouts { create = '30m' } Override default API timeout for create/update/delete

Resource References & Expressions

# Reference another resource
subnet_id = aws_subnet.main.id

# Reference a data source
ami = data.aws_ami.ubuntu.id

# Self reference inside resource
name = self.id

# Module output reference
vpc_id = module.network.vpc_id

# For-each resource reference
bucket = aws_s3_bucket.buckets[each.key].id

# Count resource reference (index)
security_groups = [aws_security_group.sg[count.index].id]

# Splat operator - get all IDs from count/for_each
instance_ids = aws_instance.servers[*].id

# Dynamic block - repeat nested blocks
dynamic "ingress" {
  for_each = var.ingress_rules
  content {
    from_port = ingress.value.from_port
    to_port   = ingress.value.to_port
    protocol  = ingress.value.protocol
  }
}

📥VARIABLES & OUTPUTS

Variable Block Syntax

variable "instance_type" {
  type        = string                              # Data type constraint
  default     = "t3.micro"                         # Default value (optional var)
  description = "EC2 instance type"
  sensitive   = false                              # Hide from logs/output
  nullable    = false                              # Disallow null (v1.1+)

  validation {                                     # Custom validation rule
    condition     = contains(["t3.micro","t3.small"], var.instance_type)
    error_message = "Must be t3.micro or t3.small."
  }
}
# Complex type constraints
variable "tags" {
  type = map(string)
}

variable "subnets" {
  type = list(object({
    cidr = string
    az   = string
  }))
}

Variable Precedence (Lowest to Highest)

Source Priority
default value 1. Default in variable block (lowest priority)
terraform.tfvars 2. Auto-loaded variable file (alphabetical order)
*.auto.tfvars 3. Auto-loaded .auto.tfvars files
-var-file=custom.tfvars 4. Explicitly loaded var file (CLI flag)
-var 'key=value' 5. CLI -var flag
TF_VAR_name env variable 6. Environment variable TF_VAR_ (highest)

Output Block Syntax

output "instance_ip" {
  value       = aws_instance.web.public_ip
  description = "Public IP of the web server"
  sensitive   = true                              # Mask value in CLI output
  depends_on  = [aws_eip.web]                   # Explicit output dependency
}
# Reference module output in parent
module.mymodule.output_name

🔍LOCALS & DATA SOURCES

Locals

locals {
  # Computed values — evaluated once, referenced many times
  env    = terraform.workspace
  region = var.aws_region

  name_prefix = "\({var.project}-\){local.env}"

  common_tags = {
    Project     = var.project
    Environment = local.env
    ManagedBy   = "Terraform"
  }

  # Conditional logic
  instance_type = local.env == "prod" ? "t3.large" : "t3.micro"
}
💡
Locals are not variables — they cannot be overridden from outside. Use them to avoid repetition and simplify complex expressions.

Data Sources

# Query existing AWS resources
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]         # Canonical
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
  }
}

data "aws_vpc" "default"    { default = true }
data "aws_subnets" "public" { filter { name="vpc-id" values=[data.aws_vpc.default.id] } }
data "aws_caller_identity" "current" {}       # Get account ID
data "aws_region" "current" {}                # Get current region
data "aws_availability_zones" "available" { state = "available" }

# HTTP data source (requires hashicorp/http provider)
data "http" "my_ip" { url = "https://api.ipify.org" }

# Template file data source
data "template_file" "init" {
  template = file("${path.module}/init.sh.tpl")
  vars     = { server_name = var.name }
}

📦MODULES

Module Sources

Source Pattern ℹ️ Description
source = "./modules/vpc" Local path module
source = "github.com/org/repo//vpc" GitHub (// = subdir)
source = "git::https://...//mod" Any Git repo via HTTPS
source = "git::ssh://...//mod?ref=v2" Git with specific branch/tag
source = "registry.terraform.io/org/mod/aws" Public Terraform Registry
source = "app.terraform.io/org/mod/aws" Private Terraform Registry
source = "s3::https://s3.amazonaws.com/bucket/module.zip" S3 archive
source = "gcs::https://storage.googleapis.com/bucket/mod.zip" GCS archive

Module Block Example

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"                              # Version constraint

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = var.environment != "prod"
}
# Access module outputs
resource "aws_instance" "web" {
  subnet_id = module.vpc.private_subnets[0]
}

Version Constraints

Constraint Meaning
= 1.2.0 Exact version only
!= 1.2.0 Exclude this version
> 1.2.0 Greater than
>= 1.2.0 Greater than or equal
< 2.0.0 Less than
~> 1.2 Allow patch: >= 1.2, < 2.0 (pessimistic constraint)
~> 1.2.3 Allow patch only: >= 1.2.3, < 1.3.0
>= 1.0, < 2.0 Range (multiple constraints combined)

🗄️BACKENDS & STATE MANAGEMENT

Backend Configuration Examples

  • ☁️ S3 Backend (AWS)


    terraform {
      backend "s3" {
        bucket         = "my-tfstate-bucket"
        key            = "envs/prod/terraform.tfstate"
        region         = "us-east-1"
        encrypt        = true
        dynamodb_table = "terraform-state-lock"      # State locking table
        role_arn       = "arn:aws:iam::123:role/TerraformRole"
      }
    }
    
  • ☁️ Azure Blob Storage Backend


    terraform {
      backend "azurerm" {
        resource_group_name  = "rg-terraform-state"
        storage_account_name = "tfstatestorage"
        container_name       = "tfstate"
        key                  = "prod.terraform.tfstate"
      }
    }
    
  • ☁️ GCS Backend (GCP)


    terraform {
      backend "gcs" {
        bucket = "my-terraform-state"
        prefix = "terraform/state"
      }
    }
    
  • Terraform Cloud / HCP Terraform


    terraform {
      cloud {
        organization = "my-org"
        workspaces { name = "my-workspace" }
      }
    }
    

State Management Commands

⌨ Command ℹ️ Description
terraform state list List all resources tracked in state
terraform state show <addr> Show attributes of a specific resource
terraform state mv <src> <dst> Move/rename a resource in state
terraform state rm <addr> Remove resource from state (without destroying)
terraform state replace-provider Change provider for tracked resources
terraform import <addr> <id> Import existing resource into state (CLI)
terraform force-unlock <lock-id> Break a stuck state lock (use carefully)
terraform refresh Sync state with real infrastructure (⚠️ deprecated)
terraform taint <addr> Mark resource for recreation (⚠️ deprecated, use -replace)
terraform untaint <addr> Remove taint from resource (⚠️ deprecated)

🗂️WORKSPACES

Workspace Commands

⌨ Command ℹ️ Description
terraform workspace list List all workspaces (* marks current)
terraform workspace new staging Create and switch to new workspace
terraform workspace select prod Switch to existing workspace
terraform workspace show Print current workspace name
terraform workspace delete staging Delete a workspace (must not be current)

Workspace Usage in Config

locals {
  env = terraform.workspace   # "default", "staging", "prod"

  instance_count = {
    default = 1
    staging = 1
    prod    = 3
  }
}

resource "aws_instance" "web" {
  count = local.instance_count[terraform.workspace]
  # ...
}

🔌PROVIDERS & MULTI-REGION / MULTI-ACCOUNT

required_providers Block

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = ">= 2.20"
    }
    helm = {
      source  = "hashicorp/helm"
      version = "~> 2.10"
    }
  }
}

Provider Aliases (Multi-Region / Multi-Account)

# Default provider
provider "aws" {
  region = "us-east-1"
}

# Alias for second region
provider "aws" {
  alias  = "eu_west"
  region = "eu-west-1"
}

# Alias for different account (using role assumption)
provider "aws" {
  alias = "prod_account"
  assume_role {
    role_arn = "arn:aws:iam::999999999999:role/TerraformRole"
  }
  region = "us-east-1"
}

# Use specific alias in a resource
resource "aws_s3_bucket" "eu_bucket" {
  provider = aws.eu_west
  bucket   = "my-eu-bucket"
}

# Pass alias into module
module "replica" {
  source    = "./modules/replica"
  providers = { aws = aws.eu_west }
}

🔄FOR EXPRESSIONS & LOOPS

For Expressions

# List comprehension
[for s in var.list : upper(s)]
[for s in var.list : upper(s) if s != "skip"]     # with filter

# Map comprehension
{for k, v in var.map : k => upper(v)}
{for i, v in var.list : i => v}                   # list to indexed map

# Invert a map (values become keys)
{for k, v in var.map : v => k}

# Group values into list by key
{for item in var.items : item.type => item... }    # => {type: [item,...]}

# Use for expression in resource
resource "aws_iam_user" "users" {
  for_each = toset(var.usernames)
  name     = each.value
}

# Output all created user ARNs
output "user_arns" {
  value = {for k, v in aws_iam_user.users : k => v.arn}
}

Common Loop Patterns

Pattern Use Case
count = length(var.list) Simple counted loop over list
for_each = var.map Loop with named keys from map
for_each = toset(var.list) Loop over list (deduped), key=value
for_each = { for item in var.list : item.name => item } List of objects to map
[for x in aws_instance.web : x.id] Collect attribute from all instances
aws_instance.web[*].id Splat: shorthand for list of attribute
aws_instance.web["key"].id For-each: access by key
aws_instance.web[0].id Count: access by index

🏗️COMMON PATTERNS & BEST PRACTICES

Project Structure

project/
  environments/
    dev/
      main.tf          # Module calls
      variables.tf     # Input variables
      outputs.tf       # Output values
      terraform.tfvars # Dev-specific values
      backend.tf       # Dev backend config
    staging/
    prod/
  modules/
    vpc/
      main.tf  variables.tf  outputs.tf  README.md
    eks/
    rds/
  .terraform.lock.hcl  # Dependency lock file (commit this!)
  .terraformignore     # Exclude from TF Cloud uploads**

Conditional Resource Creation

# Create resource only if var is true (count approach)
resource "aws_eip" "web" {
  count    = var.create_eip ? 1 : 0
  instance = aws_instance.web.id
}

# Create resource only if string is not empty
resource "aws_route53_record" "dns" {
  count = var.domain_name != "" ? 1 : 0
}

# Reference conditionally-created resource safely
output "eip" {
  value = length(aws_eip.web) > 0 ? aws_eip.web[0].public_ip : null
}

Moved Block (Refactoring Without Destroy)

# Rename a resource without destroy/recreate
moved {
  from = aws_instance.old_name
  to   = aws_instance.new_name
}

# Move resource into a module
moved {
  from = aws_instance.web
  to   = module.webserver.aws_instance.web
}

# Move between for_each keys
moved {
  from = aws_s3_bucket.buckets["old-key"]
  to   = aws_s3_bucket.buckets["new-key"]
}

Import Block (v1.5+) 🆕

# Declarative import — put in main.tf, run terraform plan
import {
  to = aws_instance.web
  id = "i-0abc12345def67890"        # AWS resource ID
}

# Generate config for imported resource
terraform plan -generate-config-out=generated.tf

# Legacy CLI import (still valid)
terraform import aws_instance.web i-0abc12345def67890

Check Blocks (v1.5+) 🆕

check "website_is_up" {
  data "http" "health" {
    url = "https://${aws_lb.web.dns_name}/health"
  }

  assert {
    condition     = data.http.health.status_code == 200
    error_message = "Website health check failed: ${data.http.health.status_code}"
  }
}

🌐ENVIRONMENT VARIABLES

Terraform CLI Environment Variables

Variable ℹ️ Description
TF_VAR_<name>=value Set input variable from environment
TF_CLI_ARGS=-auto-approve Add CLI flags globally (or per-command: TF_CLI_ARGS_apply)
TF_DATA_DIR=/tmp/tfdata Override .terraform working directory location
TF_WORKSPACE=staging Select workspace without terraform workspace select
TF_LOG=DEBUG Log level: TRACE, DEBUG, INFO, WARN, ERROR, OFF
TF_LOG_PATH=terraform.log Write logs to file instead of stderr
TF_INPUT=false Disable interactive prompts (for CI/CD)
TF_IN_AUTOMATION=true Adjust output for automated environments
TF_REGISTRY_HOST=... Override default registry host
TF_TOKEN_app_terraform_io=... Auth token for HCP Terraform / Terraform Cloud
TF_PLUGIN_CACHE_DIR=~/.tf-cache Cache downloaded provider plugins

AWS Provider Auth (Environment)

Variable ℹ️ Description
AWS_ACCESS_KEY_ID AWS access key (avoid in prod — prefer roles)
AWS_SECRET_ACCESS_KEY AWS secret key
AWS_SESSION_TOKEN Temporary session token (for assumed roles)
AWS_DEFAULT_REGION Default region (overridden by provider block)
AWS_PROFILE Use named profile from ~/.aws/credentials
AWS_ROLE_ARN IAM role ARN for aws-sdk to assume

🧪  TESTING & DEBUGGING

Built-in Testing (v1.6+) 🆕

# File: tests/main.tftest.hcl
run "creates_vpc" {
  command = plan                    # or "apply"

  variables {
    environment = "test"
  }

  assert {
    condition     = aws_vpc.main.cidr_block == "10.0.0.0/16"
    error_message = "VPC CIDR does not match expected value"
  }
}

terraform test                      # Run all .tftest.hcl files
terraform test -filter=tests/main.tftest.hcl

Console & Debugging

terraform console                   # Interactive expression evaluator
> var.environment                   # Inspect variable values
> aws_instance.web.public_ip        # Inspect resource attributes
> [for s in ["a","b"] : upper(s)]  # Test expressions
> jsondecode(file("data.json"))     # Test functions

# Enable debug logging
TF_LOG=DEBUG terraform plan 2>&1 | tee debug.log

# Validate without network calls
terraform validate

# Check formatting
terraform fmt -check -diff         # Show what would change
💡
Pro tip: Use terraform plan -json | jq '.resource_changes[] | select(.change.actions[] == "delete")' to inspect planned deletions in CI/CD pipelines.

⚙️CI/CD PATTERNS

🐙 GitHub Actions Workflow

name: Terraform
on:
  push:   { branches: [main] }
  pull_request: {}

jobs:
  terraform:
    runs-on: ubuntu-latest
    permissions:
      id-token: write         # For OIDC auth
      contents: read
      pull-requests: write    # To post plan comments
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/GitHubActions
          aws-region: us-east-1
      - uses: hashicorp/setup-terraform@v3
        with: { terraform_version: "1.9.0" }
      - run: terraform init
      - run: terraform validate
      - run: terraform plan -no-color -out=tfplan
        if: github.event_name == "pull_request"
      - run: terraform apply -auto-approve
        if: github.ref == "refs/heads/main"

Best Practices for CI/CD

  • Always run terraform validate and terraform fmt -check as first steps

  • Save plan to file (terraform plan -out=tfplan) and apply the saved plan, not re-plan

  • Use OIDC-based authentication (no long-lived access keys stored in secrets)

  • Enable TF_IN_AUTOMATION=true to suppress interactive prompts and adjust output

  • Store state remotely with locking (S3+DynamoDB, Azure Blob, GCS, HCP Terraform)

  • Pin provider versions in .terraform.lock.hcl and commit it to version control

  • Use separate workspaces or directories per environment (not branches)

  • Implement PR-based workflow: plan on PR, apply on merge to main

  • Use Atlantis or HCP Terraform for PR-based GitOps workflows

  • Scan IaC with Checkov, tfsec, or Snyk before applying


⚡QUICK REFERENCE CARD

Path & Special Variables

Expression Value
path.module Filesystem path to current module directory
path.root Filesystem path to root module directory
path.cwd Current working directory of Terraform process
terraform.workspace Name of current workspace

Lifecycle Quick Reference

Lifecycle Argument When to Use
create_before_destroy = true For zero-downtime replacements (LBs, certs)
prevent_destroy = true Block terraform destroy for critical resources (DBs)
ignore_changes = [tags, ami] Ignore drift in listed attributes
replace_triggered_by = [null_resource.trigger] Force replace when trigger changes
precondition { condition = ... } Validate inputs before resource operation
postcondition { condition = ... } Validate outputs after resource operation

Useful One-Liners

# Get all resource addresses in state
terraform state list | grep aws_

# Delete all resources of a type
terraform state list | grep aws_instance | xargs -I{} terraform state rm {}

# Plan with JSON output and count changes by action
terraform plan -json -out=p.tfplan | jq -r .type | sort | uniq -c

# Show all outputs as JSON
terraform output -json | jq .

# Refresh a single resource
terraform apply -target=aws_instance.web -refresh-only

# Upgrade all providers
terraform init -upgrade && terraform providers lock \
  -platform=linux_amd64 -platform=darwin_arm64

# Generate dependency graph and visualize
terraform graph | dot -Tsvg > graph.svg
📌
Keep .terraform.lock.hcl in version control to ensure reproducible builds across team members and CI environments. Run terraform providers lock with -platform flags to support all target platforms.

The Takeaway

Terraform is an incredibly powerful tool, but it requires discipline. Keep this cheatsheet handy, always double-check your terraform plan output before applying, and never commit your state file to a public repository!

If you want to see how to put these commands into action, check out my tutorial on Provisioning an AWS EC2 Instance with Terraform.


☕ Did this reference guide help you out? Buy me a coffee to help keep Atul Codes running!

More from this blog

A

Atul Codes | DevOps & Cloud Engineering

19 posts

Welcome to Atul Codes! This blog is dedicated to helping developers master DevOps, Cloud Computing, and infrastructure automation. Expect weekly, hands-on tutorials covering CI/CD pipelines, Docker, AWS, and Terraform. Whether you are deploying your first container or looking to optimize your cloud architecture, you will find practical, step-by-step guides and real-world solutions here.