Skip to main content

Terraform Import Block Explained: Bring Existing Resources Under Management

Key Takeaway

Use Terraform import blocks to bring existing AWS resources under management. Code-based import without CLI commands, generate config automatically, and import in bulk.

Table of Contents

The import block (Terraform 1.5+) lets you bring existing infrastructure under Terraform management without CLI commands. Write it in HCL, run terraform plan, and Terraform generates the import plan. This replaces the old terraform import CLI workflow for most use cases.

Import Block vs CLI Import

Old Way: CLI Import

# One resource at a time, imperative
terraform import aws_instance.web i-0abc123def456
terraform import aws_s3_bucket.data my-bucket-name
terraform import aws_vpc.main vpc-0abc123

Problems:

  • Must run manually, one resource at a time
  • No plan preview — imports immediately
  • Not version-controlled
  • Can’t be code-reviewed in a PR

New Way: Import Block

# Declarative, code-reviewed, bulk-capable
import {
  to = aws_instance.web
  id = "i-0abc123def456"
}

import {
  to = aws_s3_bucket.data
  id = "my-bucket-name"
}

import {
  to = aws_vpc.main
  id = "vpc-0abc123"
}
terraform plan   # Shows what will be imported — no changes yet
terraform apply  # Imports into state

Basic Usage

Step 1: Write the Import Block

# imports.tf
import {
  to = aws_instance.web
  id = "i-0abc123def456"
}

Step 2: Write the Resource Configuration

# main.tf
resource "aws_instance" "web" {
  ami           = "ami-abc123"
  instance_type = "t3.micro"
  subnet_id     = "subnet-xyz789"

  tags = {
    Name = "web-server"
  }
}

Step 3: Plan and Apply

terraform plan
# aws_instance.web: Importing...
# Plan: 1 to import, 0 to add, 0 to change, 0 to destroy

terraform apply
# aws_instance.web: Importing... [id=i-0abc123def456]
# Import successful!

Step 4: Remove the Import Block

After applying, the import block is no longer needed:

# imports.tf — can be deleted after successful import
# import {
#   to = aws_instance.web
#   id = "i-0abc123def456"
# }

Auto-Generate Configuration

Don’t want to write the resource block manually? Use -generate-config-out:

terraform plan -generate-config-out=generated.tf

This creates generated.tf with the full resource configuration based on the actual cloud resource:

# generated.tf (auto-generated)
resource "aws_instance" "web" {
  ami                         = "ami-abc123"
  instance_type               = "t3.micro"
  subnet_id                   = "subnet-xyz789"
  vpc_security_group_ids      = ["sg-abc123"]
  associate_public_ip_address = true
  key_name                    = "my-key"

  root_block_device {
    volume_size = 20
    volume_type = "gp3"
    encrypted   = true
  }

  tags = {
    Name = "web-server"
  }
}

Review and clean up the generated config (remove computed attributes, add variables), then apply.

Bulk Import

Import many resources at once:

# Import an entire VPC setup
import {
  to = aws_vpc.main
  id = "vpc-0abc123"
}

import {
  to = aws_subnet.public[0]
  id = "subnet-pub1"
}

import {
  to = aws_subnet.public[1]
  id = "subnet-pub2"
}

import {
  to = aws_subnet.private[0]
  id = "subnet-priv1"
}

import {
  to = aws_subnet.private[1]
  id = "subnet-priv2"
}

import {
  to = aws_internet_gateway.main
  id = "igw-xyz789"
}

import {
  to = aws_nat_gateway.main
  id = "nat-abc456"
}

import {
  to = aws_route_table.public
  id = "rtb-pub123"
}

import {
  to = aws_route_table.private
  id = "rtb-priv456"
}
# Generate config for all
terraform plan -generate-config-out=generated_vpc.tf

# Review, clean up, then apply
terraform apply

Import with for_each

locals {
  existing_buckets = {
    logs    = "company-logs-bucket"
    data    = "company-data-bucket"
    backups = "company-backups-bucket"
  }
}

import {
  for_each = local.existing_buckets
  to       = aws_s3_bucket.imported[each.key]
  id       = each.value
}

resource "aws_s3_bucket" "imported" {
  for_each = local.existing_buckets
  bucket   = each.value
}

Common Import IDs by Resource

ResourceImport ID FormatExample
aws_instanceInstance IDi-0abc123def456
aws_s3_bucketBucket namemy-bucket
aws_vpcVPC IDvpc-0abc123
aws_subnetSubnet IDsubnet-xyz789
aws_security_groupSG IDsg-abc123
aws_db_instanceDB identifiermy-database
aws_iam_roleRole namemy-role
aws_route53_zoneZone IDZ1234567890
aws_lambda_functionFunction namemy-function
aws_ecs_clusterCluster ARNarn:aws:ecs:...

Tips and Gotchas

Plan Shows Changes After Import

This is normal — the imported resource may differ from your written config:

terraform plan
# aws_instance.web will be updated in-place
#   ~ instance_type = "t3.micro" -> "t3.small"

Fix: update your HCL to match the actual resource, or accept the planned change.

Attributes You Can’t Set

Some attributes are read-only (computed). Remove them from generated config:

resource "aws_instance" "web" {
  # Remove these — they're computed
  # arn = "arn:aws:ec2:..."
  # id  = "i-0abc123"
  # public_ip = "1.2.3.4"

  ami           = "ami-abc123"
  instance_type = "t3.micro"
}

Import Doesn’t Create Resources

Import only adds existing resources to state. It never creates, modifies, or deletes the actual cloud resource during the import step.

Hands-On Courses

Conclusion

The import block replaces terraform import CLI for most workflows. It’s declarative, code-reviewable, and supports bulk imports. Use -generate-config-out to auto-generate resource configurations, then clean up and apply. Remove import blocks after successful import — they’re one-time use.

🚀

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.