Top Tags

Terraform CLI Handbook

Quick reference for essential Terraform commands — init, plan, apply, destroy, state management, workspaces, debugging, backends, and more.

Installation & Setup

Install Terraform

bash
1# macOS (Homebrew)
2brew tap hashicorp/tap
3brew install hashicorp/tap/terraform
4
5# Linux (apt — Debian/Ubuntu)
6wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
7echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
8sudo apt update && sudo apt install terraform
9
10# Linux (yum — RHEL/Fedora)
11sudo yum install -y yum-utils
12sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
13sudo yum install terraform
14
15# Verify installation
16terraform -version

Enable Autocomplete

bash
1# Install autocomplete (bash/zsh)
2terraform -install-autocomplete
3
4# Restart shell or source config
5source ~/.bashrc # Bash
6source ~/.zshrc # Zsh

Useful Aliases

bash
1alias tf=terraform
2alias tfi='terraform init'
3alias tfp='terraform plan'
4alias tfa='terraform apply'
5alias tfd='terraform destroy'
6alias tff='terraform fmt'
7alias tfv='terraform validate'
8alias tfs='terraform state list'
9alias tfw='terraform workspace list'

Important Paths & Config Files

Project Structure

bash
1project/
2├── main.tf # Primary resource definitions
3├── variables.tf # Input variable declarations
4├── outputs.tf # Output value definitions
5├── providers.tf # Provider configuration
6├── terraform.tfvars # Variable values (auto-loaded)
7├── terraform.tfvars.json # Variable values in JSON (auto-loaded)
8├── *.auto.tfvars # Additional auto-loaded variable files
9├── backend.tf # Backend configuration (state storage)
10├── versions.tf # Required provider/terraform versions
11├── locals.tf # Local values
12├── data.tf # Data source definitions
13└── modules/ # Local module directory
14 └── vpc/
15 ├── main.tf
16 ├── variables.tf
17 └── outputs.tf

Generated Files & Directories

bash
1.terraform/ # Working directory (providers, modules, plugins)
2.terraform/providers/ # Downloaded provider plugins
3.terraform/modules/ # Downloaded modules
4.terraform.lock.hcl # Dependency lock file (commit to VCS)
5terraform.tfstate # Local state file (DO NOT commit if using remote backend)
6terraform.tfstate.backup # Previous state backup
7crash.log # Crash log (generated on Terraform panic)

Environment Variables

bash
1TF_LOG=TRACE # Enable debug logging (TRACE, DEBUG, INFO, WARN, ERROR)
2TF_LOG_PATH=./terraform.log # Write logs to file
3TF_VAR_region="us-west-2" # Set input variable via env var (TF_VAR_<name>)
4TF_CLI_ARGS="-input=false" # Default CLI arguments
5TF_CLI_ARGS_plan="-out=tfplan" # Default arguments for specific subcommand
6TF_DATA_DIR=.terraform # Set working directory location
7TF_PLUGIN_CACHE_DIR="$HOME/.terraform.d/plugin-cache" # Plugin cache directory
8TF_IN_AUTOMATION=true # Adjust output for CI/CD environments
9TF_INPUT=0 # Disable interactive prompts

Core Workflow

Initialize Project

bash
1terraform init # Initialize working directory, download providers
2terraform init -upgrade # Upgrade providers/modules to latest allowed versions
3terraform init -reconfigure # Reconfigure backend, ignoring saved config
4terraform init -migrate-state # Migrate state to new backend
5terraform init -backend=false # Skip backend initialization
6terraform init -backend-config="bucket=my-bucket" # Partial backend config
7terraform init -backend-config=backend.hcl # Backend config from file
8terraform init -get=false # Skip module downloads

Format & Validate

bash
1terraform fmt # Auto-format .tf files to canonical style
2terraform fmt -check # Check formatting (return non-zero if changes needed)
3terraform fmt -diff # Show formatting differences
4terraform fmt -recursive # Format files in subdirectories too
5
6terraform validate # Check configuration syntax & internal consistency
7terraform validate -json # Output validation results as JSON

Plan Changes

bash
1terraform plan # Preview changes without applying
2terraform plan -out=tfplan # Save plan to file for later apply
3terraform plan -var="region=us-west-2" # Set variable inline
4terraform plan -var-file="prod.tfvars" # Load variables from file
5terraform plan -target=aws_instance.web # Plan only specific resource
6terraform plan -destroy # Preview what would be destroyed
7terraform plan -refresh-only # Only refresh state, no changes
8terraform plan -replace=aws_instance.web # Plan replacement of specific resource
9terraform plan -detailed-exitcode # Exit 0=no changes, 1=error, 2=changes present
10terraform plan -parallelism=20 # Limit concurrent operations (default: 10)
11terraform plan -compact-warnings # Show warnings in compact format

Apply Changes

bash
1terraform apply # Apply changes (interactive approval)
2terraform apply -auto-approve # Apply without confirmation prompt
3terraform apply tfplan # Apply a saved plan file
4terraform apply -var="env=production" # Set variable inline
5terraform apply -var-file="prod.tfvars" # Load variables from file
6terraform apply -target=aws_instance.web # Apply only specific resource
7terraform apply -replace=aws_instance.web # Force replacement of a resource
8terraform apply -parallelism=20 # Limit concurrent operations
9terraform apply -refresh=false # Skip state refresh before apply
10terraform apply -lock=false # Don't hold state lock during apply

Destroy Infrastructure

bash
1terraform destroy # Destroy all managed resources
2terraform destroy -auto-approve # Destroy without confirmation
3terraform destroy -target=aws_instance.web # Destroy only specific resource
4terraform plan -destroy # Preview what will be destroyed
5terraform plan -destroy -out=destroy.tfplan # Save destroy plan
6terraform apply destroy.tfplan # Apply saved destroy plan

State Management

Inspect State

bash
1terraform show # Show current state in human-readable format
2terraform show tfplan # Show saved plan in human-readable format
3terraform show -json # Output state as JSON
4terraform show -json tfplan # Output saved plan as JSON
5
6terraform state list # List all resources in state
7terraform state list module.vpc # List resources in a module
8terraform state show aws_instance.web # Show details of a specific resource

Modify State

bash
1terraform state mv aws_instance.old aws_instance.new # Rename resource in state
2terraform state mv aws_instance.web module.web.aws_instance.main # Move resource into module
3terraform state mv -state-out=other.tfstate aws_instance.web aws_instance.web # Move to another state
4
5terraform state rm aws_instance.web # Remove resource from state (does NOT destroy)
6terraform state rm module.vpc # Remove entire module from state
7
8terraform state replace-provider hashicorp/aws registry.example.com/aws # Replace provider in state

Pull & Push State

bash
1terraform state pull # Download remote state to stdout
2terraform state pull > backup.tfstate # Backup remote state to file
3terraform state push terraform.tfstate # Upload local state to remote backend
4terraform state push -force state.tfstate # Force push (use with extreme caution)

Import Existing Resources

bash
1terraform import aws_instance.web i-0abc123def456 # Import existing resource into state
2terraform import module.vpc.aws_vpc.main vpc-0123456789 # Import into a module
3terraform import -var-file="prod.tfvars" aws_instance.web i-0abc123 # Import with variables
4
5# Import block (Terraform 1.5+ — declarative, recommended)
6# Add to .tf file:
7# import {
8# to = aws_instance.web
9# id = "i-0abc123def456"
10# }
11# Then run: terraform plan -generate-config-out=generated.tf

State Locking

bash
1terraform force-unlock LOCK_ID # Manually unlock state (use with caution)
2terraform force-unlock -force LOCK_ID # Force unlock without confirmation
3terraform plan -lock=false # Run without state locking
4terraform apply -lock-timeout=5m # Wait up to 5 minutes for lock

Workspaces

Manage Workspaces

bash
1terraform workspace list # List all workspaces
2terraform workspace show # Show current workspace name
3terraform workspace new staging # Create and switch to new workspace
4terraform workspace select production # Switch to existing workspace
5terraform workspace delete staging # Delete a workspace (must be empty)
6terraform workspace delete -force staging # Force delete workspace even with resources

Workspace Usage in Config

hcl
1# Reference current workspace in configuration
2resource "aws_instance" "web" {
3 tags = {
4 Environment = terraform.workspace
5 }
6}
7
8# Conditional logic based on workspace
9locals {
10 instance_type = terraform.workspace == "production" ? "m5.large" : "t3.micro"
11}

Outputs & Expressions

View Outputs

bash
1terraform output # Show all outputs
2terraform output -json # Show all outputs as JSON
3terraform output instance_ip # Show specific output
4terraform output -raw instance_ip # Show raw value (no quotes, useful for scripting)

Interactive Console

bash
1terraform console # Launch interactive expression console
2
3# Example session:
4# > var.region
5# "us-west-2"
6# > aws_instance.web.public_ip
7# "54.183.22.100"
8# > length(var.availability_zones)
9# 3
10# > cidrsubnet("10.0.0.0/16", 8, 1)
11# "10.0.1.0/24"
12# > exit

Providers

Manage Providers

bash
1terraform providers # Show providers required by configuration
2terraform providers mirror /path/to/dir # Mirror providers to local directory
3terraform providers lock # Update dependency lock file
4terraform providers lock -platform=linux_amd64 -platform=darwin_amd64 # Lock for specific platforms
5terraform providers schema -json # Output provider schemas as JSON
6
7terraform version # Show Terraform and provider versions

Provider Configuration

hcl
1# Require specific provider versions
2terraform {
3 required_version = ">= 1.5.0"
4
5 required_providers {
6 aws = {
7 source = "hashicorp/aws"
8 version = "~> 5.0"
9 }
10 azurerm = {
11 source = "hashicorp/azurerm"
12 version = ">= 3.0, < 4.0"
13 }
14 }
15}
16
17provider "aws" {
18 region = var.region
19 profile = var.aws_profile
20}

Backend Configuration

Local Backend (Default)

hcl
1terraform {
2 backend "local" {
3 path = "terraform.tfstate"
4 }
5}

S3 Backend (AWS)

hcl
1terraform {
2 backend "s3" {
3 bucket = "my-terraform-state"
4 key = "production/terraform.tfstate"
5 region = "us-west-2"
6 encrypt = true
7 dynamodb_table = "terraform-locks"
8 }
9}

Consul Backend

hcl
1terraform {
2 backend "consul" {
3 address = "consul.example.com:8500"
4 scheme = "https"
5 path = "terraform/production"
6 }
7}

Azure Blob Backend

hcl
1terraform {
2 backend "azurerm" {
3 resource_group_name = "tfstate-rg"
4 storage_account_name = "tfstate12345"
5 container_name = "tfstate"
6 key = "production.terraform.tfstate"
7 }
8}

GCS Backend (Google Cloud)

hcl
1terraform {
2 backend "gcs" {
3 bucket = "my-terraform-state"
4 prefix = "terraform/state"
5 }
6}

Debugging & Troubleshooting

Enable Logging

bash
1# Set log level (TRACE, DEBUG, INFO, WARN, ERROR)
2export TF_LOG=DEBUG
3terraform plan
4
5# Write logs to file
6export TF_LOG=TRACE
7export TF_LOG_PATH=./terraform.log
8terraform apply
9
10# Disable logging
11unset TF_LOG
12unset TF_LOG_PATH

Inspect & Debug

bash
1terraform graph # Generate visual dependency graph (DOT format)
2terraform graph | dot -Tpng > graph.png # Render graph as PNG (requires graphviz)
3
4terraform state list # List all tracked resources
5terraform state show aws_instance.web # Show resource details from state
6
7terraform refresh # Reconcile state with real infrastructure (deprecated, use plan -refresh-only)
8terraform plan -refresh-only # Detect drift without making changes

Common Issues

bash
1# State locked?
2terraform force-unlock LOCK_ID
3
4# Provider version conflict?
5terraform init -upgrade
6
7# Corrupted state?
8terraform state pull > backup.tfstate # Backup first
9terraform state push fixed.tfstate # Push corrected state
10
11# Plugin cache issues?
12rm -rf .terraform/ # Clear providers/modules
13terraform init # Re-download everything
14
15# Crash log generated?
16cat crash.log # Review crash details

Resource Targeting & Lifecycle

Target Specific Resources

bash
1terraform plan -target=aws_instance.web # Plan for one resource
2terraform plan -target=module.vpc # Plan for entire module
3terraform apply -target=aws_instance.web # Apply only one resource
4terraform destroy -target=aws_instance.web # Destroy only one resource

Replace Resources

bash
1terraform apply -replace=aws_instance.web # Force recreation (Terraform 1.0+)
2terraform taint aws_instance.web # Mark for recreation (deprecated)
3terraform untaint aws_instance.web # Unmark for recreation

Variables

Setting Variables

bash
1# Command line
2terraform plan -var="region=us-west-2"
3terraform plan -var='tags={"env":"prod","team":"ops"}'
4
5# Variable files
6terraform plan -var-file="production.tfvars"
7terraform plan -var-file="secrets.tfvars"
8
9# Environment variables (prefix with TF_VAR_)
10export TF_VAR_region="us-west-2"
11export TF_VAR_instance_type="t3.micro"
12terraform plan

Variable Precedence (Lowest → Highest)

PrioritySource
1 (lowest)Default values in variables.tf
2terraform.tfvars or terraform.tfvars.json
3*.auto.tfvars or *.auto.tfvars.json (alphabetical)
4-var-file flag
5 (highest)-var flag or TF_VAR_ environment variables

Modules

Use Modules

bash
1terraform get # Download modules only (without init)
2terraform get -update # Update modules to latest versions
3terraform init # Download modules + providers

Module Sources

hcl
1# Local path
2module "vpc" {
3 source = "./modules/vpc"
4}
5
6# Terraform Registry
7module "vpc" {
8 source = "terraform-aws-modules/vpc/aws"
9 version = "5.0.0"
10}
11
12# GitHub
13module "vpc" {
14 source = "github.com/org/repo//modules/vpc?ref=v1.0.0"
15}
16
17# S3 bucket
18module "vpc" {
19 source = "s3::https://bucket.s3.amazonaws.com/modules/vpc.zip"
20}

Terraform Cloud & Login

Authentication

bash
1terraform login # Authenticate with Terraform Cloud / HCP Terraform
2terraform logout # Remove stored credentials

Cloud Configuration

hcl
1terraform {
2 cloud {
3 organization = "my-org"
4 workspaces {
5 name = "my-workspace"
6 }
7 }
8}

Testing

Terraform Test (1.6+)

bash
1terraform test # Run all tests in tests/ directory
2terraform test -filter=tests/vpc.tftest.hcl # Run specific test file
3terraform test -verbose # Show detailed test output

Test File Example

hcl
1# tests/main.tftest.hcl
2run "verify_instance" {
3 command = plan
4
5 assert {
6 condition = aws_instance.web.instance_type == "t3.micro"
7 error_message = "Instance type must be t3.micro"
8 }
9}
10
11run "verify_tags" {
12 command = apply
13
14 assert {
15 condition = aws_instance.web.tags["Environment"] == "production"
16 error_message = "Environment tag must be production"
17 }
18}

Output Formatting

Common Flags

FlagDescription
-jsonOutput in JSON format
-no-colorDisable colored output
-input=falseDisable interactive prompts
-auto-approveSkip approval prompt
-compact-warningsShow warnings in compact form
-parallelism=NLimit concurrent operations (default: 10)
-lock=falseDon't hold state lock
-lock-timeout=NsWait N seconds for state lock
-target=RESOURCETarget specific resource
-var="key=value"Set variable value
-var-file=FILELoad variable values from file

HCL Quick Reference

Common Functions

hcl
1# String
2upper("hello") # "HELLO"
3lower("HELLO") # "hello"
4format("Hello, %s!", "world") # "Hello, world!"
5join(", ", ["a", "b", "c"]) # "a, b, c"
6split(",", "a,b,c") # ["a", "b", "c"]
7replace("hello", "l", "L") # "heLLo"
8trimspace(" hello ") # "hello"
9
10# Numeric
11min(1, 2, 3) # 1
12max(1, 2, 3) # 3
13ceil(4.2) # 5
14floor(4.8) # 4
15
16# Collection
17length(["a", "b", "c"]) # 3
18contains(["a", "b"], "a") # true
19merge({a=1}, {b=2}) # {a=1, b=2}
20lookup({a=1}, "a", "default") # 1
21keys({a=1, b=2}) # ["a", "b"]
22values({a=1, b=2}) # [1, 2]
23flatten([[1,2],[3,4]]) # [1, 2, 3, 4]
24distinct([1, 2, 2, 3]) # [1, 2, 3]
25element(["a","b","c"], 1) # "b"
26
27# Filesystem
28file("path/to/file") # Read file contents
29fileexists("path/to/file") # Check if file exists
30templatefile("tpl.tftpl", {v=1}) # Render template file
31
32# Encoding
33base64encode("hello") # "aGVsbG8="
34base64decode("aGVsbG8=") # "hello"
35jsonencode({a = 1}) # '{"a":1}'
36jsondecode('{"a":1}') # {a = 1}
37yamlencode({a = 1}) # "a: 1\n"
38
39# Network
40cidrsubnet("10.0.0.0/16", 8, 1) # "10.0.1.0/24"
41cidrhost("10.0.1.0/24", 5) # "10.0.1.5"
42
43# Type conversion
44tostring(42) # "42"
45tonumber("42") # 42
46tolist(["a", "b"]) # ["a", "b"]
47toset(["a", "b", "a"]) # ["a", "b"]
48tomap({a = 1}) # {a = 1}

Common Resource Shortnames

ProviderResourceCommon Usage
AWSaws_instanceEC2 instances
AWSaws_vpcVirtual Private Cloud
AWSaws_subnetVPC subnets
AWSaws_security_groupSecurity groups
AWSaws_s3_bucketS3 storage buckets
AWSaws_iam_roleIAM roles
AWSaws_lambda_functionLambda functions
AWSaws_rds_instanceRDS databases
Azureazurerm_resource_groupResource groups
Azureazurerm_virtual_networkVirtual networks
Azureazurerm_virtual_machineVirtual machines
GCPgoogle_compute_instanceCompute instances
GCPgoogle_storage_bucketStorage buckets
K8skubernetes_deploymentK8s deployments
Dockerdocker_containerDocker containers