All guides
Practice now
D7 · Implementation and maintenance

Advanced CLI: import, logging, and debugging

For the exam and for daily work, it's essential to know how to import existing infrastructure, debug issues with log levels, release stuck locks, and use tools like terraform console to inspect expressions.

terraform import in depth

The full process has specific steps you need to know for the exam.

1

Identify the resource in the cloud

Get the resource ID from the cloud console or CLI. Each resource type has its own ID format (check the provider documentation).

2

Define the resource block in HCL

You MUST create the resource block in your .tf file BEFORE running terraform import. If the block doesn't exist, the import will fail.

3

Run terraform import

Command: terraform import <address> <real-id>. Terraform queries the cloud API, retrieves the current state, and writes it into the tfstate.

4

Run terraform plan

Compares what the tfstate says (actual state) against your resource block (desired configuration). There will almost always be differences.

5

Adjust the HCL code

Modify the resource block to match the actual configuration of the imported resource. Repeat plan until you see "No changes".

6

Verify and commit

With "No changes" in the plan, the resource is correctly imported and managed by Terraform.

# Full example: importing an existing VPC

# Step 1: Get the VPC ID from AWS
# aws ec2 describe-vpcs --query 'Vpcs[?Tags[?Key==`Name`&&Value==`legacy-vpc`]].VpcId'
# Output: "vpc-0abc123def456"

# Step 2: Create the resource block (minimum required)
# main.tf:
resource "aws_vpc" "legacy" {
  cidr_block = "172.16.0.0/16"  # real value — get it from the console
}

# Step 3: Import
terraform import aws_vpc.legacy vpc-0abc123def456

# Step 4: View the plan
terraform plan
# ~ aws_vpc.legacy  (modify because attributes are missing)

# Step 5: Update the code with all the attributes
resource "aws_vpc" "legacy" {
  cidr_block           = "172.16.0.0/16"
  enable_dns_support   = true     # adjust according to the plan
  enable_dns_hostnames = true
  tags = {
    Name = "legacy-vpc"
  }
}

# Repeat plan until you see: "No changes. Your infrastructure matches the configuration."

# ALTERNATIVE: import block + generate-config (Terraform 1.5+)
import {
  to = aws_vpc.legacy
  id = "vpc-0abc123def456"
}

terraform plan -generate-config-out=vpc_legacy.tf  # automatically generates the resource block

💡 ID formats by AWS resource type

ResourceID format
aws_instancei-0123456789abcdef0
aws_vpcvpc-0abc123def456
aws_s3_bucketbucket-name
aws_iam_rolerole-name
aws_security_groupsg-0abc123def456
aws_db_instancedb-identifier

terraform apply -replace (replaces terraform taint)

⚠️ terraform taint has been deprecated since v0.15.2

marked a resource to be recreated on the next apply, but without showing the plan first. It was replaced by terraform apply -replace=<address>, which is safer because it explicitly shows what will change before acting.

Before (deprecated)

# terraform taint — DO NOT use
terraform taint aws_instance.web
# Marks the resource but doesn't show the plan
# The next apply destroys and recreates it
# without you being able to review the change first

Now (modern)

# terraform apply -replace — use this
terraform plan -replace=aws_instance.web
# Shows the replacement plan first

terraform apply -replace=aws_instance.web
# Forces recreation of that specific resource

# Multiple resources:
terraform apply   -replace=aws_instance.web   -replace=aws_eip.web

🎯 For the exam

  • • terraform taint has been deprecated since v0.15.2 — the exam may mention both, but the correct answer uses -replace
  • • -replace shows the plan before acting (symbol -/+ in the plan output)
  • • Useful for forcing the recreation of a resource that's in a bad state without destroying all of the infrastructure
  • • The address follows the format type.name or module.name.type.name for modules

TF_LOG: logging levels

Terraform uses environment variables to control the logging level. They're essential for debugging problems with providers or the core.

LevelVerbosityWhen to use
TRACEMaximum — everythingDeep debugging. Shows every API call, full HTTP requests. Very verbose.
DEBUGHighDetailed debugging. Shows internal decisions from the core and providers.
INFOMediumGeneral information about the execution flow. Recommended for understanding what's happening.
WARNLow — warningsWarnings only. Non-critical situations that require attention.
ERRORMinimal — errors onlyOnly critical errors that cause failures.
OFFNone (default)No additional logging (default behavior).
# Enable logging in the current session
export TF_LOG=DEBUG
terraform plan

# Save logs to a file (without this, they go to stderr)
export TF_LOG=TRACE
export TF_LOG_PATH=/tmp/terraform-debug.log
terraform apply

# Separate Core and Provider logs (Terraform 0.15+)
export TF_LOG_CORE=INFO       # Terraform core logs
export TF_LOG_PROVIDER=DEBUG  # provider logs (calls to the AWS API, etc.)

# Disable logging
unset TF_LOG
# Or:
export TF_LOG=OFF

# Windows PowerShell:
$env:TF_LOG = "DEBUG"
$env:TF_LOG_PATH = "C:\temp\tf-debug.log"
terraform plan

🎯 For the exam

The order of verbosity from lowest to highest is: ERROR → WARN → INFO → DEBUG → TRACE. TF_LOG_PATH saves the logs to a file (without this they only go to stderr). TF_LOG_CORE and TF_LOG_PROVIDER let you control logging separately.

terraform force-unlock

When a is interrupted abruptly (kill, lost connection, crash), it can leave the lock unreleased. terraform force-unlock manually releases that lock.

# Typical error when the state is locked:
# Error acquiring the state lock: ConditionalCheckFailedException: ...
# Lock Info:
#   ID:        3a1c2fa8-b5d9-4f71-a3c7-123456789abc  ← this is the Lock ID
#   Path:      terraform.tfstate
#   Operation: OperationTypeApply
#   Who:       user@hostname
#   Version:   1.6.3
#   Created:   2024-01-15 10:23:45 UTC
#   Info:

# Release the lock with the ID shown in the error
terraform force-unlock 3a1c2fa8-b5d9-4f71-a3c7-123456789abc

# Confirm with -force (skips the interactive confirmation)
terraform force-unlock -force 3a1c2fa8-b5d9-4f71-a3c7-123456789abc

⚠️ Verify there is no apply in progress before force-unlock

If you release a lock while another Terraform process is running (that you don't see), you can cause two applies to run simultaneously and corrupt the state. Before running force-unlock, verify that the process that set the lock has actually finished.

terraform providers and console

terraform providers

Lists all providers required by the current configuration and their installed versions.

terraform providers
# Providers required by configuration:
# .
# ├── provider[registry.terraform.io/hashicorp/aws] ~> 5.0
# └── module.vpc
#     └── provider[registry.terraform.io/hashicorp/aws] >= 4.0

# Update the lock file for multiple platforms:
terraform providers lock   -platform=linux_amd64   -platform=darwin_arm64

terraform console

Interactive REPL for evaluating HCL expressions. Has access to the state and variables.

terraform console
> var.environment
"prod"
> max(3, 7, 2)
7
> join("-", ["api", "prod", "v1"])
"api-prod-v1"
> jsonencode({env = "prod"})
"{"env":"prod"}"
> cidrsubnet("10.0.0.0/16", 8, 1)
"10.0.1.0/24"
> Ctrl+C or Ctrl+D to exit

Parallelism and performance

Terraform runs operations in parallel when resources have no dependencies between them. The -parallelism flag controls how many concurrent operations are allowed.

# Default value: 10 concurrent operations
terraform apply

# Increase parallelism (faster but more load on the API)
terraform apply -parallelism=20

# Reduce parallelism (useful if the cloud API has rate limiting)
terraform apply -parallelism=5

# The same flag works with plan and destroy
terraform plan -parallelism=15
terraform destroy -parallelism=5

# ⚠️ Increasing it too much can cause throttling errors on cloud APIs
# AWS, Azure, and GCP all have rate limits on their APIs
Environment variablePurposeExample
TF_LOGGlobal logging levelTF_LOG=DEBUG
TF_LOG_PATHFile where logs are savedTF_LOG_PATH=/tmp/tf.log
TF_LOG_CORELogging level for the core onlyTF_LOG_CORE=INFO
TF_LOG_PROVIDERLogging level for the provider onlyTF_LOG_PROVIDER=TRACE
TF_VAR_nameValue of an input variableTF_VAR_env=prod
TF_CLI_ARGSAdditional flags for all commandsTF_CLI_ARGS="-no-color"
TF_CLI_ARGS_planAdditional flags only for terraform planTF_CLI_ARGS_plan="-refresh=false"
TF_DATA_DIRAlternate directory for .terraform/TF_DATA_DIR=/tmp/tf-plugins

Did you get this topic?

Test what you just learned

An engineer runs terraform apply and the process is interrupted by a network issue. When trying to run terraform plan again, they receive: 'Error acquiring the state lock'. What should they do?

Ready to test "Advanced CLI: Import, Logging and Debugging"?

Put what you learned to the test with original Terraform Associate exam questions.

Start Terraform Associate mock exam

Unlimited mock exams with the Pro plan

Log in to track your progress.