CLI Workflows and Debugging¶
The Professional exam tests CLI fluency under time pressure. You need muscle memory for the commands and the ability to debug quickly using logs, graphs, and console.
The Core Lifecycle Commands¶
terraform init # download providers, configure backend
terraform validate # syntax and internal consistency check
terraform plan # compute diff, show actions
terraform apply # execute the plan
terraform destroy # tear down all managed resources
All are idempotent except apply. plan is safe to run repeatedly and often.
terraform init Flags¶
terraform init \
-backend-config=prod.tfbackend \ # extra backend settings
-upgrade \ # bump providers within constraints
-reconfigure \ # throw away old backend config
-migrate-state \ # copy state to new backend
-get=false \ # skip module download
-input=false \ # no interactive prompts (CI)
-lock-timeout=60s # wait for state lock
-backend-config can point to a file or a key=value pair. Useful for per-environment backends without duplicating the terraform {} block.
terraform plan Flags¶
terraform plan \
-out=tfplan \ # save plan for later apply
-var-file=prod.tfvars \ # load variables
-var="region=us-west-2" \ # inline override
-target=aws_instance.web \ # plan only this resource (use sparingly)
-replace=aws_instance.web \ # force replacement
-refresh-only \ # just refresh state, no diff
-refresh=false \ # skip refresh
-destroy \ # plan for destroy
-detailed-exitcode \ # 0=no changes, 1=error, 2=changes present
-generate-config-out=gen.tf \ # for import blocks
-parallelism=10 # max concurrent operations (default 10)
-detailed-exitcode is CI-friendly: exit 2 means "apply would change things".
terraform apply Flags¶
terraform apply tfplan # apply a saved plan (no prompts)
terraform apply -auto-approve # skip interactive prompt
terraform apply -input=false # no prompts for variables
Saved plans are the safest pattern for CI: plan -out in one job, human approves, apply tfplan in the next job. Saved plans bind to the exact state version; if state changes, apply fails.
terraform state Subcommands¶
See the state management note for detailed usage. Most-used on the exam:
terraform state list
terraform state show ADDRESS
terraform state mv SRC DST
terraform state rm ADDRESS
terraform state pull
terraform state push FILE
terraform state replace-provider OLD_FQN NEW_FQN
terraform console¶
An interactive REPL for expressions:
$ terraform console
> var.region
"us-east-1"
> [for s in aws_subnet.this : s.id]
["subnet-abc", "subnet-def"]
> try(var.nonexistent, "default")
"default"
> cidrsubnet("10.0.0.0/16", 8, 3)
"10.0.3.0/24"
Perfect for:
- Testing
forexpressions before writing HCL - Evaluating
localswithout running apply - Inspecting outputs of existing state
terraform graph¶
Generates a DOT representation of the dependency graph.
terraform graph | dot -Tpng > graph.png
terraform graph -type=plan
terraform graph -type=apply
terraform graph -type=plan-destroy
Useful when debugging cycles or unexpected orderings.
terraform providers¶
terraform providers # show provider tree per module
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64
terraform providers mirror /path # download providers for air-gapped use
terraform providers schema -json # full schema dump
providers lock is essential for multi-platform teams (Mac developers + Linux CI).
Debug Logging¶
export TF_LOG=TRACE # most verbose
export TF_LOG=DEBUG # verbose
export TF_LOG=INFO # informational
export TF_LOG=WARN # warnings only
export TF_LOG=ERROR # errors only
export TF_LOG_PATH=./terraform.log # send to file
export TF_LOG_CORE=DEBUG # core only
export TF_LOG_PROVIDER=TRACE # providers only
export TF_LOG_PROVIDER_AWS=TRACE # single provider (1.6+)
TRACE shows every gRPC call between Terraform and providers. Use it to diagnose:
- Provider crashes (look for panic in logs)
- Slow operations (correlate timestamps)
- Unexpected API calls (e.g., refresh hitting rate limits)
Environment Variables Worth Knowing¶
| Variable | Effect |
|---|---|
TF_VAR_foo | Set input variable foo |
TF_CLI_ARGS | Append flags to every Terraform command |
TF_CLI_ARGS_plan | Append flags only to terraform plan |
TF_INPUT=0 | Disable interactive prompts |
TF_IN_AUTOMATION=1 | Suppress hints about CLI usage |
TF_PLUGIN_CACHE_DIR | Shared provider cache across workspaces |
TF_REGISTRY_DISCOVERY_RETRY | Retry count for registry lookups |
TF_WORKSPACE | Select workspace without terraform workspace select |
TF_PLUGIN_CACHE_DIR=~/.terraform.d/plugin-cache dramatically speeds up init in CI.
Provider Lock File¶
.terraform.lock.hcl pins provider versions and hashes.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.40.0"
constraints = "~> 5.0"
hashes = [
"h1:abc...",
"zh:def...",
]
}
- Commit this file to version control
- Regenerate across platforms with
terraform providers lock -platform=... terraform initenforces matching hashes; mismatch blocks initterraform init -upgradefetches newer versions within constraints
Working with Saved Plans¶
terraform plan -out=tfplan
terraform show tfplan # human-readable
terraform show -json tfplan | jq # machine-readable, pipe to tools
The JSON form is the basis for Sentinel, OPA, Checkov, and custom governance. Structure includes resource_changes, configuration, prior_state, planned_values.
Common Error Patterns¶
"Error locking state: ConditionalCheckFailedException" Someone else holds the lock. Wait, or force-unlock if stale.
"Error: Provider produced inconsistent final plan" Provider bug or a computed attribute that changes between plan and apply. File an issue and/or add lifecycle.ignore_changes.
"Error: Unsupported Terraform Core version" Your CLI is older than required_version. Upgrade.
"Error: Failed to query available provider packages" Network/registry issue. Check proxy, TF_REGISTRY_DISCOVERY_RETRY, or use a mirror.
"Error: Invalid function argument" HCL-level. Run terraform console to isolate.
"Error: Cycle: ..." Circular dependency. Check terraform graph or restructure references.
Targeted Operations¶
-target and -replace are escape hatches. On the exam, use them only if explicitly required. They break the declarative model.
terraform plan -target=aws_instance.web # only this resource
terraform apply -replace=aws_instance.web # force recreate this resource
Prefer moved blocks, removed blocks, import blocks, or lifecycle.replace_triggered_by for persistent intent.
Formatter and Validator¶
terraform fmt -recursive # format all files
terraform fmt -check # exit non-zero if changes needed (CI)
terraform fmt -diff # show what would change
terraform validate # syntax and internal references
terraform validate -json # machine-readable output
Both are fast and cheap. Run both on every commit via pre-commit hooks.
Workspace Commands¶
terraform workspace list
terraform workspace new dev
terraform workspace select prod
terraform workspace delete old-env
terraform workspace show
CLI workspaces are distinct from HCP Terraform workspaces. CLI workspaces just partition state within a single backend. HCP Terraform workspaces are full environments with their own variables, runs, and permissions. In HCP-backed configurations, CLI workspaces map to HCP workspaces via tags.
Exam-Ready Checklist¶
- Can read
TF_LOG=TRACEoutput enough to pinpoint a failing API call - Can save and apply a plan file
- Can use
terraform consoleto debug expressions - Can regenerate the lock file for multiple platforms
- Know when
-targetand-replaceare appropriate (rarely) - Can decode
detailed-exitcodein CI scripts