Skip to main content

Terraform Import

The terraform import command brings an existing cloud resource that was created outside of Terraform under Terraform management by adding it to the state file. After importing, Terraform can plan and apply changes to that resource without recreating it.

What Is Terraform Import?

You joined a company where the entire AWS account was built by clicking in the console over three years. There are 200 EC2 instances, 50 S3 buckets, and a VPC with 30 subnets — none of it managed by Terraform. The team wants to adopt Terraform without destroying and recreating everything.

terraform import is how you do it. It reads an existing cloud resource and writes its current state into the Terraform state file. Once imported, Terraform knows the resource exists. You can then write HCL that matches it, and Terraform will manage all future changes through code.

Remember

terraform import adds the resource to state — it does NOT write the HCL configuration for you (except with the newer -generate-config-out flag in Terraform 1.5+). You still need to write the resource block yourself.

The Classic Two-Step Import Process

Step 1 — Write the Resource Configuration

Write a resource block in your .tf files that matches the existing resource. You do not need every argument yet — just enough to make Terraform accept the block:

HCL
# main.tf — write this BEFORE running terraform import
resource "aws_instance" "payments_api" {
# Arguments will be filled in after import
# The block just needs to exist for import to work
ami = "ami-0f5ee92e2d63afc18" # fill in the real values
instance_type = "t3.medium"
tags = {
Name = "razorpay-payments-api-prod"
}
}
Step 2 — Run terraform import
Bash
# Syntax: terraform import <resource_type>.<resource_name> <real_resource_id>
terraform import aws_instance.payments_api i-0a1b2c3d4e5f6789
# aws_instance.payments_api: Importing from ID "i-0a1b2c3d4e5f6789"...
# aws_instance.payments_api: Import prepared!
# Prepared aws_instance for import
# aws_instance.payments_api: Refreshing state... [id=i-0a1b2c3d4e5f6789]
#
# Import successful!
# The resources that were imported are shown above. These resources are now in
# your Terraform state and will henceforth be managed by Terraform.
Step 3 — Run terraform plan and Fix Mismatches

After import, run terraform plan. It will show differences between what you wrote in HCL and what is actually on the resource. Fix each difference:

Bash
terraform plan
# ~ aws_instance.payments_api will be updated in-place
# ~ instance_type = "t3.medium" -> "t3.large" <- real instance is t3.large
# ~ monitoring = false -> true <- real instance has monitoring on
#
# Fix your HCL to match the real resource, then re-plan until the output is:
# No changes. Your infrastructure matches the configuration.

Finding the Resource ID for Common AWS Resources

Bash
# EC2 Instance
aws ec2 describe-instances --filters "Name=tag:Name,Values=razorpay-payments-api"
# ID format: i-0a1b2c3d4e5f6789
terraform import aws_instance.payments_api i-0a1b2c3d4e5f6789
# S3 Bucket
# ID is just the bucket name
terraform import aws_s3_bucket.assets razorpay-assets-prod
# Security Group
aws ec2 describe-security-groups --filters "Name=group-name,Values=payments-api-sg"
# ID format: sg-0a1b2c3d4e5f6789
terraform import aws_security_group.payments sg-0a1b2c3d4e5f6789
# RDS Instance
# ID is the DB identifier
terraform import aws_db_instance.main razorpay-payments-postgres-prod
# IAM Role
# ID is the role name
terraform import aws_iam_role.app_role razorpay-payments-api-role
# VPC
aws ec2 describe-vpcs --filters "Name=tag:Name,Values=razorpay-prod-vpc"
# ID format: vpc-0a1b2c3d4e5f
terraform import aws_vpc.main vpc-0a1b2c3d4e5f

The New Import Block (Terraform 1.5+)

Terraform 1.5 introduced a declarative import block that lives in your .tf files — no CLI command needed. Import happens automatically during terraform apply:

HCL
# imports.tf — declarative import (Terraform 1.5+)
import {
to = aws_instance.payments_api
id = "i-0a1b2c3d4e5f6789"
}
import {
to = aws_s3_bucket.assets
id = "razorpay-assets-prod"
}

Auto-Generating HCL with -generate-config-out (Terraform 1.5+)

Terraform 1.5 can generate the HCL resource block automatically from an import block:

Bash
# Add an import block with no corresponding resource block
# Then run:
terraform plan -generate-config-out=generated.tf
# Terraform reads the real resource and writes HCL into generated.tf
# Review and clean up generated.tf — it includes every attribute
# including computed ones you do not need to specify

Troubleshooting Import

Error Root Cause Fix
Resource already managed by Terraform Resource already in state Check terraform state list — may already be imported
Cannot import non-existent remote object Wrong resource ID Verify the ID in the AWS console or CLI
Error: Resource type does not support import Provider does not support import Check the provider docs — not all resources are importable
Plan shows large diff after import HCL does not match real resource Run terraform state show to see all attributes, update HCL to match
InvalidInstanceID.NotFound Wrong EC2 instance ID format Must start with i- followed by hex characters
Common Mistake

Running terraform apply immediately after import without checking the plan. If your HCL does not exactly match the real resource, apply will change the resource to match your code — potentially disabling security settings or changing production configuration.

Frequently Asked Questions

Does terraform import write the HCL configuration for me, or just update the state?

It only adds the resource to the state file — it does not generate the matching `.tf` configuration block. You still have to hand-write (or use `terraform plan -generate-config-out` in newer Terraform versions, which does generate a starting config) the resource block yourself, and if your written config doesn't match the real resource's actual attributes, the very next `terraform plan` will show a diff trying to change it back to match your config.

What's the safest way to import a resource without accidentally modifying it?

Import it, then immediately run `terraform plan` before touching anything else — if the plan shows changes, your HCL doesn't match reality yet, and applying at that point would modify the live resource to match your (likely incomplete) config, not the other way around. Iterate on the config until `terraform plan` shows no changes; only then is the resource safely under Terraform management without unintended drift being 'fixed' by an apply.