Terraform Cheatsheet
Core commands, flags, and HCL syntax youโll actually reach for mid-task. For the full explanations, see the Terraform guides.
Commands
| Command | Description |
|---|---|
terraform init | Initializes working directory: backend, provider plugins, modules. Nothing else works until this completes. |
terraform validate | Checks syntax and internal consistency. Catches missing args/type mismatches โ not API-side errors. |
terraform fmt | Formats .tf files to canonical HCL style. |
terraform plan | Computes the diff between config and current state. Makes zero changes to real infrastructure. |
terraform apply | Executes the changes needed to reach the desired state. |
terraform destroy | Removes all resources managed by the current config, in reverse creation order. |
terraform console | Interactive REPL to evaluate HCL expressions and inspect state, without modifying anything. |
terraform import <type>.<name> <id> | Brings existing infrastructure (created outside Terraform) into state management. |
terraform refresh | Deprecated โ use plan -refresh-only / apply -refresh-only instead. |
terraform graph | Outputs the dependency graph in DOT format. |
terraform show | Shows the current state or a saved plan in human-readable (or -json) form. |
terraform force-unlock <lock-id> | Manually releases a stuck state lock. Verify no process is actually running first. |
State
| Command | Description |
|---|---|
terraform state list | Lists all resources tracked in state. |
terraform state show <addr> | Shows attributes of one resource in state. |
terraform state mv <old> <new> | Renames/moves a resource within state. |
terraform state rm <addr> | Removes a resource from state without destroying it. |
terraform state pull / push | Downloads/uploads the raw state file. |
Workspaces
| Command | Description |
|---|---|
terraform workspace list | Lists all workspaces. |
terraform workspace new <name> | Creates and switches to a new workspace. |
terraform workspace select <name> | Switches to an existing workspace. |
terraform workspace show | Prints the current workspace name. |
Output & taint
| Command | Description |
|---|---|
terraform output / terraform output <name> | Prints all outputs, or one by name. |
terraform output -raw <name> | Prints a string output with no quotes โ useful in shell scripts. |
terraform taint / terraform untaint | Soft-deprecated โ mark a resource for recreation on next apply. |
terraform plan -replace=<addr> / apply -replace=<addr> | Modern (1.5.2+) replacement for taint. |
Flags
| Flag | Commands | Purpose |
|---|---|---|
-auto-approve | apply, destroy | Skip interactive confirmation |
-var="key=val" | plan, apply | Override a single variable |
-var-file=file | plan, apply | Load variables from a file |
-target=resource | plan, apply, destroy | Limit to a specific resource |
-out=file | plan | Save the plan for a later apply |
-refresh=false | plan, apply | Skip state refresh |
-lock=false | plan, apply | Disable state locking (caution) |
-lock-timeout=60s | plan, apply | Wait for a lock instead of failing immediately |
-json | plan, apply, show | JSON output for tooling |
-detailed-exitcode | plan | 0 = no changes, 1 = error, 2 = changes detected |
-upgrade | init | Upgrade provider/module versions |
-backend-config="..." | init | Supply backend settings (partial config) |
-generate-config-out=file | plan (with import blocks) | Generate HCL for an imported resource |
Saved-plan pattern โ guarantees what you reviewed is exactly what gets applied:
terraform plan -out=tfplan && terraform apply tfplanSyntax
Variable block
variable "environment" { description = "Target deployment environment" type = string default = "dev"
validation { condition = contains(["dev", "staging", "production"], var.environment) error_message = "environment must be 'dev', 'staging', or 'production'." }}Sensitive variable (hides value from CLI output only โ plaintext still lives in state):
variable "database_password" { type = string sensitive = true}Output block
output "vpc_id" { description = "ID of the created VPC" value = aws_vpc.main.id}
output "database_connection_string" { value = "postgresql://..." sensitive = true}Provider block
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.50" } }}
provider "aws" { region = "us-east-1" default_tags { tags = { ManagedBy = "terraform" } }}
provider "aws" { alias = "us_west" region = "us-west-2"}Version constraints: = 5.54.1 (exact) ยท >= 5.50 (minimum) ยท ~> 5.50 (>= 5.50, < 6.0 โ community standard)
Resource & data source blocks
resource "<PROVIDER_RESOURCE_TYPE>" "<LOCAL_NAME>" { argument_one = "value" nested_block { setting = "value" }}
data "aws_ami" "amazon_linux_2023" { most_recent = true owners = ["amazon"] filter { name = "name" values = ["al2023-ami-*-x86_64"] }}Lifecycle meta-argument:
resource "aws_db_instance" "production" { lifecycle { prevent_destroy = true create_before_destroy = true ignore_changes = [password, engine_version] }}locals
locals { name_prefix = "${var.environment}-${var.project_name}" common_tags = merge(var.tags, { Environment = var.environment ManagedBy = "terraform" })}Module call
module "networking" { source = "./modules/networking" environment = var.environment cidr_block = "10.0.0.0/16"}
module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~> 5.0"}count vs for_each
resource "aws_instance" "workers" { count = 3 instance_type = "t3.micro" tags = { Name = "worker-${count.index}" }}
resource "aws_iam_user" "team" { for_each = var.users name = each.key tags = { Email = each.value.email }}Prefer for_each for named, stable identity โ removing an item from the middle of a count list shifts every subsequent index and can trigger unexpected destroy/recreate cycles.
dynamic block
dynamic "<block_type>" { for_each = <collection> content { <attribute> = <block_type>.value.<field> }}Conditional dynamic block idiom: for_each = condition ? [1] : []
Backend (S3)
terraform { backend "s3" { bucket = "mycompany-terraform-state" key = "environments/production/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "terraform-state-lock" }}Remote state lookup:
data "terraform_remote_state" "networking" { backend = "s3" config = { bucket = "mycompany-terraform-state" key = "shared/networking/terraform.tfstate" region = "us-east-1" }}Gotchas
- State locking prevents concurrent operations from corrupting state. DynamoDB lock tables need a hash key
LockID(typeS). Donโtforce-unlockwithout confirming no process is actually still running. - Never commit
.tfstate,.tfstate.*, or.terraform/to git โ the local backend has no locking and state can contain secrets in plaintext. Always commit.terraform.lock.hcl. sensitive = trueonly hides values from CLI output โ the plaintext value still lives in the state file.terraform destroy -target=resource, removing the resource block + apply, andterraform state rmare three different ways to โstop managingโ a resource โ only the first two actually delete the real infrastructure.prevent_destroy = truemakesterraform destroyerror out rather than proceed, for critical resources like production databases.