Introduction
When terraform plan or terraform apply fails with a cryptic error, you need debug output. Terraform’s built-in debugging system uses environment variables to control log verbosity — no code changes required.
This guide covers everything you need to debug Terraform: enabling TF_LOG, choosing the right log level, saving output to files, and reading debug logs to find the actual problem.
Quick Start: Enable Terraform Debug Mode
The fastest way to get debug output:
# Linux/macOS
export TF_LOG=DEBUG
terraform plan
# Windows PowerShell
$env:TF_LOG = "DEBUG"
terraform plan
# Windows CMD
set TF_LOG=DEBUG
terraform plan
To turn it off:
# Linux/macOS
unset TF_LOG
# Windows PowerShell
$env:TF_LOG = ""
TF_LOG Levels Explained
Terraform supports 5 log levels, from most verbose to least:
| Level | What It Shows | When to Use |
|---|---|---|
TRACE | Every internal operation, API calls, HTTP requests/responses, provider plugin communication | Deep debugging, provider issues |
DEBUG | Resource diffs, state operations, dependency graph | Most debugging scenarios |
INFO | High-level operational messages | General monitoring |
WARN | Potential problems that don’t cause failures | Reviewing configuration health |
ERROR | Only errors that stop execution | Filtering noise in large runs |
TRACE Level
TRACE is the most verbose level. It shows everything including raw HTTP requests to cloud provider APIs:
export TF_LOG=TRACE
terraform plan
Example TRACE output:
2024-02-07T10:15:32.456Z [TRACE] provider.terraform-provider-aws: Calling AWS API: ec2:DescribeInstances
2024-02-07T10:15:32.789Z [TRACE] provider.terraform-provider-aws: HTTP Request: GET https://ec2.us-east-1.amazonaws.com/?Action=DescribeInstances
2024-02-07T10:15:33.123Z [TRACE] provider.terraform-provider-aws: HTTP Response: 200 OK
Use TRACE when you suspect the provider is making incorrect API calls or when you need to see exactly what Terraform sends to the cloud.
DEBUG Level
DEBUG is the most commonly used level. It shows resource changes, state operations, and the dependency graph without the noise of raw HTTP traffic:
export TF_LOG=DEBUG
terraform apply
Example DEBUG output:
2024-02-07T10:15:32.456Z [DEBUG] provider: planning resource change: resource=aws_instance.web
2024-02-07T10:15:32.789Z [DEBUG] provider: diff result: attribute=instance_type old="t2.micro" new="t3.micro"
This is your go-to level for most debugging.
Save Debug Output to a File with TF_LOG_PATH
Console output scrolls fast. Save it to a file for easier analysis:
# Save all debug output to a file
export TF_LOG=DEBUG
export TF_LOG_PATH=./terraform-debug.log
terraform plan
The file captures all Terraform output. You can then search it:
# Find errors
grep "ERROR" terraform-debug.log
# Find API calls
grep "HTTP" terraform-debug.log
# Find a specific resource
grep "aws_instance" terraform-debug.log
Important: TF_LOG_PATH only works when TF_LOG is also set. The file is overwritten on each run — rename or move it if you want to keep it.
Separate Provider Logs
Since Terraform 0.15, you can set log levels separately for Terraform core and providers:
# Terraform core at INFO, providers at TRACE
export TF_LOG_CORE=INFO
export TF_LOG_PROVIDER=TRACE
terraform plan
This is useful when you know the issue is in a provider (like the AWS or Azure provider) and don’t want noise from Terraform’s core operations.
Debug Specific Terraform Commands
Debug terraform init
export TF_LOG=DEBUG
terraform init
Common issues init debug logs reveal:
- Provider download failures (network/proxy issues)
- Registry authentication errors
- Backend configuration problems
- Module source resolution failures
Debug terraform plan
export TF_LOG=DEBUG
terraform plan
Look for:
- State refresh failures (resource not found)
- Provider credential errors
- Resource dependency ordering issues
- Variable interpolation problems
Debug terraform apply
export TF_LOG=DEBUG
terraform apply
Watch for:
- API rate limiting (429 responses in TRACE mode)
- Resource creation timeouts
- Dependency ordering failures
- State locking conflicts
Common Debugging Scenarios
Authentication Errors
If you see InvalidClientTokenId or AuthFailure:
export TF_LOG=TRACE
terraform plan 2>&1 | grep -i "auth\|credential\|token\|403\|401"
Check that your credentials are set:
# AWS
aws sts get-caller-identity
# Azure
az account show
# GCP
gcloud auth application-default print-access-token
Provider Plugin Crashes
When a provider crashes, enable TRACE to see the plugin communication:
export TF_LOG=TRACE
export TF_LOG_PATH=./crash-debug.log
terraform plan
# Look for the crash
grep -A5 "plugin" crash-debug.log
State Lock Issues
export TF_LOG=DEBUG
terraform plan 2>&1 | grep -i "lock\|state"
If the state is locked, you’ll see the lock ID. Force unlock with:
terraform force-unlock LOCK_ID
Slow Operations
Use TRACE to find which API calls are slow:
export TF_LOG=TRACE
export TF_LOG_PATH=./slow-debug.log
terraform plan
# Find slow operations (look at timestamps)
grep "HTTP Response" slow-debug.log | sort -k1
Debug in CI/CD Pipelines
GitLab CI
terraform_plan:
script:
- export TF_LOG=DEBUG
- export TF_LOG_PATH=./terraform-debug.log
- terraform plan -out=tfplan
artifacts:
paths:
- terraform-debug.log
when: on_failure
GitHub Actions
- name: Terraform Plan
env:
TF_LOG: DEBUG
TF_LOG_PATH: ./terraform-debug.log
run: terraform plan -out=tfplan
- name: Upload Debug Logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: terraform-debug-logs
path: terraform-debug.log
Environment Variable Reference
| Variable | Values | Description |
|---|---|---|
TF_LOG | TRACE, DEBUG, INFO, WARN, ERROR | Set log level for all components |
TF_LOG_CORE | Same as TF_LOG | Set log level for Terraform core only |
TF_LOG_PROVIDER | Same as TF_LOG | Set log level for providers only |
TF_LOG_PATH | File path | Write logs to file instead of stderr |
Best Practices
- Start with DEBUG, escalate to TRACE: DEBUG covers 90% of issues. Only use TRACE when you need API-level detail.
- Always save to file: Debug output is massive. Use
TF_LOG_PATHso you can search it. - Sanitize before sharing: Logs may contain credentials, account IDs, or resource details. Scrub them before posting to GitHub Issues or forums.
- Disable after debugging: Debug logging slows Terraform and creates large files. Always unset
TF_LOGwhen done. - Use provider-specific logging:
TF_LOG_COREandTF_LOG_PROVIDERlet you focus on the relevant component. - Version your state: If debugging a state issue, back up
.terraform.tfstatebefore making changes.
Hands-On Courses
Learn by doing with interactive courses on CopyPasteLearn:
Conclusion
Terraform debug mode is your most powerful troubleshooting tool. Set TF_LOG=DEBUG to see what Terraform is actually doing, use TF_LOG_PATH to save output for analysis, and use TRACE when you need to see raw API calls. Combined with TF_LOG_CORE and TF_LOG_PROVIDER for targeted debugging, you can quickly diagnose any Terraform issue from authentication failures to provider crashes.




