terraform import
terraform import brings existing infrastructure that was created outside Terraform into Terraform’s state management. This lets you gradually adopt IaC without recreating resources — critical for migrating production infrastructure to Terraform without downtime.
The Problem terraform import Solves
Your team manually created an RDS database, several S3 buckets, and a VPC six months ago. Now you want to manage all of it with Terraform. You can’t just write the config and run apply — Terraform would try to create duplicates. Import tells Terraform: “This existing resource should be tracked in state as this resource block.”
Two Ways to Import (Old vs. New)
Modern: Import Blocks (Terraform 1.5+, Recommended)
# import.tf — declare what to importimport { to = aws_s3_bucket.data_lake id = "my-existing-data-lake-bucket"}
import { to = aws_instance.web_server id = "i-1234567890abcdef0"}
import { to = aws_db_instance.production id = "prod-postgres-db"}Run plan to preview, then apply to import:
terraform plan # Shows: 3 resources to importterraform apply # Imports and updates stateClassic: Command-Line Import
terraform import <resource_type>.<resource_name> <provider_resource_id>
# AWS examplesterraform import aws_s3_bucket.data_lake my-existing-bucketterraform import aws_instance.web i-1234567890abcdef0terraform import aws_security_group.web sg-0abc123def456terraform import aws_vpc.main vpc-0abc123def456789
# Azure examplesterraform import azurerm_resource_group.main \ /subscriptions/{sub-id}/resourceGroups/my-resource-group
# GCP examplesterraform import google_compute_instance.web \ projects/my-project/zones/us-central1-a/instances/my-instanceConfig Generation (Terraform 1.5+)
The most powerful new capability — Terraform can generate the configuration for you:
# 1. Write import blocks in import.tf# 2. Generate the HCL configuration automaticallyterraform plan -generate-config-out=generated.tf
# 3. Review and edit generated.tf# 4. Apply to complete the importterraform applyGenerated output example:
# generated.tf (auto-generated — review and clean up)resource "aws_s3_bucket" "data_lake" { bucket = "my-existing-data-lake-bucket" tags = { Environment = "production" Owner = "data-team" }}
resource "aws_s3_bucket_versioning" "data_lake" { bucket = aws_s3_bucket.data_lake.id versioning_configuration { status = "Enabled" }}Step-by-Step Import Workflow
Step 1: Find the Resource ID
Each resource type uses a specific ID format. Look in the provider documentation or the cloud console:
| Resource | ID Format |
|---|---|
aws_s3_bucket | Bucket name: my-bucket |
aws_instance | Instance ID: i-1234567890abcdef0 |
aws_vpc | VPC ID: vpc-0abc123 |
aws_security_group | SG ID: sg-0abc123 |
aws_iam_role | Role name: my-role |
aws_rds_cluster | Cluster identifier: my-cluster |
azurerm_virtual_machine | Full ARM resource ID |
Step 2: Write the Resource Block
Before importing, you must have a resource block in your configuration:
resource "aws_s3_bucket" "data_lake" { bucket = "my-existing-data-lake-bucket" # Attributes will be populated from state after import}Step 3: Import
terraform import aws_s3_bucket.data_lake my-existing-data-lake-bucketStep 4: Reconcile Configuration
After import, terraform plan likely shows differences between your config and the actual resource. Update your config to match reality:
terraform plan# Shows: ~ aws_s3_bucket.data_lake will be updated in-place# ~ tags = {} -> {"Environment" = "production"}Update the config to reflect the actual tags, then re-run plan until it shows No changes.
Importing Multiple Resources: Bulk Import Script
#!/bin/bash# bulk_import.sh — import many resources at once
# S3 bucketsbuckets=("logs-bucket" "data-lake" "artifacts" "backups")for bucket in "${buckets[@]}"; do terraform import "aws_s3_bucket.${bucket//-/_}" "$bucket"done
# EC2 instances from a tag queryaws ec2 describe-instances \ --filters "Name=tag:Environment,Values=production" \ --query 'Reservations[].Instances[].InstanceId' \ --output text | \while read -r id; do name=$(aws ec2 describe-instances --instance-ids "$id" \ --query 'Reservations[0].Instances[0].Tags[?Key==`Name`].Value' \ --output text) terraform import "aws_instance.${name}" "$id"doneCommon Import Pitfalls
Pitfall 1: Forgetting sub-resources
Importing aws_s3_bucket doesn’t automatically import associated resources like aws_s3_bucket_versioning, aws_s3_bucket_lifecycle_configuration, or aws_s3_bucket_policy. Each sub-resource must be imported separately.
Pitfall 2: Plan shows destroy after import
Your config doesn’t match the real resource’s attributes. Run terraform state show <resource> to see what Terraform imported, then update your config.
terraform state show aws_s3_bucket.data_lake# Shows all actual attributes — copy relevant ones to your configPitfall 3: Duplicate resource
Terraform will error if you try to import into a resource name that already exists in state. Use terraform state list to check first.