Terraform Import and State Management
Terraform state management is critical for maintaining consistency between your infrastructure code and real-world resources. Whether you're importing existing infrastructure, migrating backends, or manipulating state directly, mastering these techniques allows you to manage complex production environments without downtime or drift.
Prerequisites
- Terraform 1.0+ installed
- Access to a cloud provider (AWS, GCP, Azure) or local resources
- Backend storage configured (S3, GCS, or local)
- Basic familiarity with Terraform HCL syntax
Understanding Terraform State
Terraform stores the mapping between your configuration and real infrastructure in a state file (terraform.tfstate). This file tracks resource IDs, attributes, and dependencies.
# View current state
terraform state list
# Show details for a specific resource
terraform state show aws_instance.web
# Pull state from remote backend
terraform state pull > backup.tfstate
The state file is JSON and contains sensitive data — never commit it to version control.
Importing Existing Resources
Use terraform import to bring existing infrastructure under Terraform management without destroying and recreating it.
# Basic import syntax
terraform import <resource_type>.<resource_name> <resource_id>
# Import an AWS EC2 instance
terraform import aws_instance.web i-0abc123def456789
# Import an AWS S3 bucket
terraform import aws_s3_bucket.assets my-production-bucket
# Import an Azure resource group
terraform import azurerm_resource_group.main /subscriptions/SUB_ID/resourceGroups/my-rg
# Import a GCP compute instance
terraform import google_compute_instance.vm projects/PROJECT/zones/us-central1-a/instances/my-vm
Before importing, write the resource block in your configuration:
# main.tf - write this BEFORE running terraform import
resource "aws_instance" "web" {
# Leave empty initially; terraform will populate after import
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.medium"
tags = {
Name = "web-server"
}
}
After importing, run terraform plan to reconcile the configuration with actual state:
# Import the resource
terraform import aws_instance.web i-0abc123def456789
# Check what needs to be updated
terraform plan
# Adjust your HCL to match actual resource attributes
# Then verify no changes are needed
terraform plan # Should show "No changes"
Bulk Import with import blocks (Terraform 1.5+)
# import.tf
import {
to = aws_instance.web
id = "i-0abc123def456789"
}
import {
to = aws_s3_bucket.logs
id = "my-logs-bucket"
}
# Generate configuration automatically
terraform plan -generate-config-out=generated.tf
State Manipulation Commands
Moving Resources
Use terraform state mv when renaming resources or moving them into modules:
# Rename a resource
terraform state mv aws_instance.old_name aws_instance.new_name
# Move resource into a module
terraform state mv aws_instance.web module.servers.aws_instance.web
# Move resource out of a module
terraform state mv module.servers.aws_instance.web aws_instance.web
# Move between state files (cross-project)
terraform state mv -state=source.tfstate -state-out=dest.tfstate \
aws_instance.web aws_instance.web
Removing Resources from State
Use terraform state rm to stop managing a resource without destroying it:
# Remove a single resource from state
terraform state rm aws_instance.web
# Remove all resources in a module
terraform state rm module.servers
# Remove multiple resources at once
terraform state rm aws_instance.web aws_security_group.web_sg
Replacing State Manually
# Push a modified state file
terraform state push modified.tfstate
# Force push (use with extreme caution)
terraform state push -force modified.tfstate
Backend Configuration and Migration
Configuring Remote Backends
# backend.tf - S3 backend
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# GCS backend
terraform {
backend "gcs" {
bucket = "my-terraform-state"
prefix = "prod"
}
}
Migrating Between Backends
# Step 1: Update backend configuration in main.tf
# Step 2: Initialize with migration flag
terraform init -migrate-state
# Terraform will prompt to copy existing state to new backend
# Answer "yes" to confirm migration
# Step 3: Verify state is in new backend
terraform state list
# Step 4: Remove old backend files if migrating from local
rm terraform.tfstate terraform.tfstate.backup
State Locking
State locking prevents concurrent operations from corrupting state. With S3 + DynamoDB:
# Create DynamoDB table for locking (AWS CLI)
aws dynamodb create-table \
--table-name terraform-locks \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
# Force unlock if a lock is stuck (get lock ID from error message)
terraform force-unlock LOCK_ID
# Disable locking for a single operation (emergency use only)
terraform apply -lock=false
Workspace Management
Workspaces allow multiple state files within a single backend — useful for staging/production separation:
# List workspaces
terraform workspace list
# Create a new workspace
terraform workspace new staging
# Switch to a workspace
terraform workspace select production
# Show current workspace
terraform workspace show
# Delete a workspace (must switch away first)
terraform workspace select default
terraform workspace delete staging
Use workspace name in configuration:
locals {
env = terraform.workspace
instance_type = {
default = "t3.small"
staging = "t3.medium"
production = "t3.large"
}
}
resource "aws_instance" "web" {
instance_type = local.instance_type[local.env]
}
Detecting and Resolving Drift
Infrastructure drift occurs when real resources differ from state. Use terraform refresh and plan:
# Refresh state to reflect real infrastructure (deprecated in 1.x)
terraform refresh
# Preferred: use plan with refresh
terraform plan -refresh=true
# Skip refresh for speed (when you know state is current)
terraform plan -refresh=false
# Apply only a refresh (update state without changing infrastructure)
terraform apply -refresh-only
# Target specific resources for plan/apply
terraform plan -target=aws_instance.web
terraform apply -target=aws_instance.web
# Detect all drifted resources
terraform plan | grep -E "will be|must be|has been"
Troubleshooting
State lock errors:
# Get the lock ID from the error message, then force unlock
terraform force-unlock <LOCK_ID>
# Check DynamoDB for stuck locks
aws dynamodb scan --table-name terraform-locks
"Resource already exists" during import:
# The resource may already be in state under a different name
terraform state list | grep <resource_type>
# Remove old reference if it's stale
terraform state rm <old_resource_reference>
State file corruption:
# Restore from backup
cp terraform.tfstate.backup terraform.tfstate
# Or pull from remote and inspect
terraform state pull | python3 -m json.tool | head -50
Backend initialization failures:
# Reconfigure backend without migrating
terraform init -reconfigure
# Upgrade providers and backend
terraform init -upgrade
Conclusion
Terraform state management is the backbone of reliable infrastructure-as-code workflows. By mastering import for onboarding existing resources, state manipulation commands for refactoring, and backend migration for team collaboration, you can maintain full control over complex multi-environment deployments. Always back up your state before performing destructive operations and use remote backends with locking for any production workload.


