Skip to main content
Terraform Debug Mode: How to Enable TF_LOG and Read Debug Output

Terraform Debug Mode: How to Enable TF_LOG and Read Debug Output

Key Takeaway

Enable Terraform debug mode with TF_LOG=DEBUG, save logs to file with TF_LOG_PATH, and troubleshoot terraform plan/apply errors.

Table of Contents

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:

LevelWhat It ShowsWhen to Use
TRACEEvery internal operation, API calls, HTTP requests/responses, provider plugin communicationDeep debugging, provider issues
DEBUGResource diffs, state operations, dependency graphMost debugging scenarios
INFOHigh-level operational messagesGeneral monitoring
WARNPotential problems that don’t cause failuresReviewing configuration health
ERROROnly errors that stop executionFiltering 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

VariableValuesDescription
TF_LOGTRACE, DEBUG, INFO, WARN, ERRORSet log level for all components
TF_LOG_CORESame as TF_LOGSet log level for Terraform core only
TF_LOG_PROVIDERSame as TF_LOGSet log level for providers only
TF_LOG_PATHFile pathWrite logs to file instead of stderr

Best Practices

  1. Start with DEBUG, escalate to TRACE: DEBUG covers 90% of issues. Only use TRACE when you need API-level detail.
  2. Always save to file: Debug output is massive. Use TF_LOG_PATH so you can search it.
  3. Sanitize before sharing: Logs may contain credentials, account IDs, or resource details. Scrub them before posting to GitHub Issues or forums.
  4. Disable after debugging: Debug logging slows Terraform and creates large files. Always unset TF_LOG when done.
  5. Use provider-specific logging: TF_LOG_CORE and TF_LOG_PROVIDER let you focus on the relevant component.
  6. Version your state: If debugging a state issue, back up .terraform.tfstate before 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.

🚀

Level Up Your Terraform Skills

Hands-on courses, books, and resources from Luca Berton

Luca Berton
Written by

Luca Berton

DevOps Engineer, AWS Partner, Terraform expert, and author. Creator of Ansible Pilot, Terraform Pilot, and CopyPasteLearn.