Skip to content

Terraform Import: Managing Existing Infrastructure with IaC

DodaTech 5 min read

Terraform import allows you to bring existing cloud infrastructure under Terraform management without recreating resources, enabling gradual adoption of Infrastructure as Code.

What You'll Learn

In this tutorial, you will learn how to use Terraform import to adopt existing resources, use the import block for declarative imports, generate configuration from state, and import resources at scale.

Why It Matters

Most organizations have existing infrastructure created before IaC adoption. Recreating everything is risky and time-consuming. Import brings existing resources under Terraform management safely, enabling incremental migration without downtime.

Real-World Use

DodaTech imported over 200 existing AWS resources into Terraform management when adopting IaC. Durga Antivirus Pro's production database clusters, CDN distributions, and DNS zones were all imported without a single minute of downtime.

Import Command

The <a href="/devops/terraform/">terraform</a> import command attaches an existing resource to a Terraform resource block:

terraform import aws_instance.web i-0abcd1234efgh5678

Expected output:

aws_instance.web: Importing from ID "i-0abcd1234efgh5678"...
aws_instance.web: Import prepared!
aws_instance.web: Refreshing state...
Import successful!

The resource is now tracked in state. However, the configuration block must exist before importing.

Prerequisite: Configuration Block

Create a matching resource block in your configuration before importing:

resource "aws_instance" "web" {
  # Arguments will be populated after import
}

Expected output: After import, <a href="/devops/terraform/">terraform</a> plan shows the configuration differs from the imported resource. Copy the state attributes into the configuration to converge.

Import Block (Terraform 1.5+)

The import block provides a declarative way to import resources:

import {
  to = aws_instance.web
  id = "i-0abcd1234efgh5678"
}

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

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

resource "aws_s3_bucket" "data" {
  bucket = "existing-data-bucket"
}

Expected output: Running <a href="/devops/terraform/">terraform</a> plan shows the import as part of the plan. Running <a href="/devops/terraform/">terraform</a> apply executes the import alongside any resource changes.

Import Blocks at Scale

locals {
  instances = {
    web    = "i-0abcd1234efgh5678"
    worker = "i-0efgh5678ijkl9012"
    batch  = "i-0ijkl9012mnop3456"
  }
}

resource "aws_instance" "this" {
  for_each = local.instances
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
}

import {
  for_each = local.instances
  to       = aws_instance.this[each.key]
  id       = each.value
}

Expected output: All three instances are imported in a single apply. The for_each on the import block mirrors the resource's for_each.

Generating Configuration

After import, generate the resource configuration from the state:

terraform show -no-color > imported.tf

Expected output: The <a href="/devops/terraform/">terraform</a> show command outputs the full configuration with all attributes. You can extract resource blocks and copy them into your configuration files.

Using Terraform Config Inspect

terraform providers schema -json | jq '.resource_schemas["aws_instance"].block'

Expected output: The JSON schema for the AWS instance resource shows all attributes, their types, and whether they are required or computed.

Bulk Import Strategy

For importing many resources, use a scripted approach:

# List all EC2 instances via AWS CLI
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,Tags[?Key==`Name`].Value|[0]]' --output text | while read id name; do
  cat > "import-${name}.tf" << EOF
import {
  to = aws_instance.${name}
  id = "${id}"
}
EOF
done

Expected output: One import block file per EC2 instance. Run <a href="/devops/terraform/">terraform</a> apply to import all instances with a single command.

State Operations for Import

List Resources in State

terraform state list

Expected output: Shows all resources currently tracked in state, including imported ones.

Remove Imported Resources

terraform state rm aws_instance.web

Expected output: Removes the resource from state without destroying it. The resource exists but Terraform no longer manages it.

Common Mistakes

1. Importing Without Configuration

Running <a href="/devops/terraform/">terraform</a> import without a matching resource block fails. Create the resource block first.

2. Missing Required Arguments

After import, <a href="/devops/terraform/">terraform</a> plan shows changes because required arguments differ from the imported resource. Copy state attributes to converge.

3. Not Backing Up State Before Bulk Import

Bulk imports can corrupt state if interrupted. Back up state before running large imports.

4. Importing Resources That Belong to Another Configuration

Resources already managed by another Terraform configuration cause state conflicts. Verify ownership before importing.

5. Forgetting to Update Configuration After Import

Imported resources remain in state but diverge from configuration. Always update configuration to match the resource after import.

Practice Questions

1. What does <a href="/devops/terraform/">terraform</a> import do? Attaches an existing cloud resource to a Terraform resource block, adding it to the state file without modifying the resource.

2. What is the advantage of the import block over the <a href="/devops/terraform/">terraform</a> import command? The import block is declarative, supports for_each, and runs as part of <a href="/devops/terraform/">terraform</a> plan and <a href="/devops/terraform/">terraform</a> apply.

3. How do you generate Terraform configuration from an imported resource? Run <a href="/devops/terraform/">terraform</a> show -no-color to view the full state, then extract resource attributes into configuration files.

4. Why do you need a resource block before importing? The resource block provides the address (aws_instance.web) that the import command attaches to. Without it, there is no address.

5. Challenge: Create an S3 bucket through the AWS console, then import it into Terraform using the import block, generate configuration, and verify <a href="/devops/terraform/">terraform</a> plan shows no changes.

Mini Project: Infrastructure Migration

Create an EC2 instance through the AWS CLI or console. Write a Terraform configuration that manages it. Use the import block to bring it under Terraform management. Generate the configuration from state, verify a clean plan, then add a tag through Terraform.

Built-in Functions
Terraform Testing

What's Next

Master Terraform import to adopt existing infrastructure, then learn Testing to validate configurations with validate, fmt, tflint, and tfsec.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro