Fatskills
Practice. Master. Repeat.
Study Guide: TECH **Terraform Import: Zero-Fluff, Hands-On Guide**
Source: https://www.fatskills.com/cloud-application-developer/chapter/tech-terraform-import-zero-fluff-hands-on-guide

TECH **Terraform Import: Zero-Fluff, Hands-On Guide**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~8 min read

Terraform Import: Zero-Fluff, Hands-On Guide

Bringing existing cloud resources under Terraform control—without downtime or destruction.


1. What This Is & Why It Matters

You inherit a production AWS environment with 50 EC2 instances, 10 RDS databases, and 3 S3 buckets—all manually created via the console or CLI. Your CTO demands "everything in Terraform by Friday." terraform import is your lifeline.

Why It Matters in Production

  • Avoids downtime: Recreating resources from scratch risks breaking live services.
  • Enables IaC governance: Manual resources are invisible to Terraform’s state, drift detection, and CI/CD pipelines.
  • Saves money: Rebuilding resources (e.g., RDS) can trigger new billing cycles or data loss.
  • Compliance: Auditors demand "everything as code" for reproducibility.

Real-world scenario:
You’re onboarding a new client. Their AWS account has 200+ resources (VPCs, subnets, Lambda functions) with no Terraform state. You must: 1. Inventory existing resources.
2. Write Terraform configs to match them.
3. Import them into state without recreating or deleting anything.


2. Core Concepts & Components


1. terraform import

  • Definition: A CLI command that maps an existing cloud resource to a Terraform resource block in your state file.
  • Production insight: Importing does not generate Terraform config—you must write it manually first.

2. Terraform State (terraform.tfstate)

  • Definition: A JSON file tracking the current state of your infrastructure (IDs, attributes, dependencies).
  • Production insight: If state is corrupted or lost, Terraform treats imported resources as "new," risking recreation/destruction.

3. Resource Address

  • Definition: The path to a resource in your Terraform config (e.g., aws_instance.web).
  • Production insight: Typos in the address cause import to fail silently—double-check names.

4. Cloud Resource ID

  • Definition: The provider-specific identifier for a resource (e.g., AWS EC2 instance ID i-1234567890abcdef0).
  • Production insight: Some resources (e.g., IAM roles) have ARNs instead of IDs—use the correct format.

5. Drift Detection

  • Definition: Terraform’s ability to detect manual changes to imported resources (e.g., someone resizes an EC2 instance via the console).
  • Production insight: Run terraform plan after import to catch drift immediately.

6. terraform state Commands

  • Definition: Subcommands to inspect/modify state (e.g., terraform state list, terraform state show).
  • Production insight: Use terraform state rm to remove a resource from state without destroying it (e.g., if you accidentally import a production DB).

7. Provider-Specific Quirks

  • Definition: Some resources require special handling (e.g., AWS S3 buckets need force_destroy = true to import).
  • Production insight: Always check the Terraform provider docs for import examples.

8. import vs. refresh

  • import: Adds a new resource to state.
  • refresh: Updates state to match real-world resources (but won’t add missing ones).
  • Production insight: Use refresh after import to sync attributes (e.g., tags, IPs).


3. Step-by-Step Hands-On: Import an AWS EC2 Instance


Prerequisites

  • AWS CLI configured with admin permissions (aws sts get-caller-identity should work).
  • Terraform installed (terraform -v).
  • An existing EC2 instance (e.g., i-0123456789abcdef0).


Step 1: Write a Terraform Config for the Resource

Create main.tf with a minimal resource block matching the instance:


provider "aws" {
  region = "us-east-1"  # Must match the instance's region!
}

resource "aws_instance" "web" {
  # Leave attributes empty for now—we'll fill them after import.
# Required: ami, instance_type, subnet_id, etc.
# Example (replace with your instance's values): ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 instance_type = "t2.micro" subnet_id = "subnet-1234567890abcdef0" tags = {
Name = "web-server" } }

Why this matters:
Terraform needs a "target" to map the imported resource to. The config must match the real-world resource’s attributes (e.g., ami, instance_type), or terraform plan will show drift.


Step 2: Initialize Terraform

terraform init

Expected output:


Initializing the backend...
Initializing provider plugins...
Terraform has been successfully initialized!


Step 3: Run the Import Command

terraform import aws_instance.web i-0123456789abcdef0

Expected output:


aws_instance.web: Importing from ID "i-0123456789abcdef0"...
aws_instance.web: Import prepared! Prepared aws_instance for import aws_instance.web: Refreshing state... [id=i-0123456789abcdef0] Import successful!

⚠️ Common failure modes:
- Error: Resource not found → Check the instance ID and region.
- Error: No resource block found → Ensure aws_instance.web exists in main.tf.


Step 4: Verify the Import

terraform state show aws_instance.web

Expected output (truncated):


# aws_instance.web:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
arn = "arn:aws:ec2:us-east-1:123456789012:instance/i-0123456789abcdef0"
instance_type = "t2.micro"
private_ip = "10.0.1.100"
subnet_id = "subnet-1234567890abcdef0"
tags = {
"Name" = "web-server"
}
vpc_security_group_ids = [
"sg-1234567890abcdef0",
] }


Step 5: Update the Config to Match the Real Resource

Copy the output from terraform state show into main.tf:


resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro"
  subnet_id     = "subnet-1234567890abcdef0"
  vpc_security_group_ids = ["sg-1234567890abcdef0"]
  tags = {
Name = "web-server" } }


Step 6: Check for Drift

terraform plan

Expected output:


No changes. Your infrastructure matches the configuration.

If you see changes:
- Someone manually modified the instance (e.g., changed the instance_type).
- Your config is missing attributes (e.g., user_data, ebs_block_device).


Step 7: (Optional) Import Associated Resources

Repeat Steps 1–6 for: - Security groups (aws_security_group).
- EBS volumes (aws_ebs_volume).
- Elastic IPs (aws_eip).

Pro tip: Use terraform state list to see all imported resources.


4. ? Production-Ready Best Practices


Security

  • Least privilege IAM: Use a dedicated IAM role for imports with only the necessary permissions (e.g., ec2:Describe*).
  • Secrets: Never hardcode credentials in configs. Use aws-vault or environment variables: bash export AWS_ACCESS_KEY_ID="..." export AWS_SECRET_ACCESS_KEY="..."
  • State encryption: Store terraform.tfstate in an S3 bucket with SSE-S3 or SSE-KMS.

Cost Optimization

  • Tag imported resources: Add CostCenter and Environment tags to track spending.
  • Right-size resources: After import, use terraform plan to identify over-provisioned resources (e.g., t3.xlarget3.medium).

Reliability & Maintainability

  • Modularize configs: Group related resources into modules (e.g., modules/ec2-instance).
  • Use depends_on: Explicitly declare dependencies (e.g., an EC2 instance depends on a security group).
  • Idempotency: Ensure terraform apply is repeatable. Avoid manual changes post-import.

Observability

  • Enable CloudTrail: Log all API calls during imports for audit trails.
  • Monitor drift: Set up a CI job to run terraform plan daily and alert on changes.
  • Tagging: Use consistent tags (e.g., Terraform = true) to identify managed resources.


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Importing without a config terraform import succeeds, but plan shows the resource will be destroyed. Always write a matching resource block before importing.
Incorrect resource address terraform import fails with "resource not found." Double-check the address (e.g., aws_instance.web, not aws_instance.web_server).
Region mismatch terraform import fails with "resource not found." Ensure the provider region matches the resource’s region.
Missing required attributes terraform plan shows drift (e.g., missing subnet_id). Use terraform state show to copy all attributes into the config.
Importing dependent resources out of order terraform plan fails with dependency errors. Import resources in dependency order (e.g., VPC → Subnet → EC2).


6. ? Exam/Certification Focus


Typical Question Patterns

  1. Scenario: "You imported an EC2 instance, but terraform plan shows it will be destroyed. What’s the issue?"
  2. Answer: The Terraform config doesn’t match the imported resource’s attributes.

  3. Command syntax: "Which command imports an existing S3 bucket my-bucket into aws_s3_bucket.data?"

  4. Answer: terraform import aws_s3_bucket.data my-bucket

  5. Drift detection: "How do you check if an imported resource has been manually modified?"

  6. Answer: Run terraform plan.

Key Trap Distinctions

  • import vs. refresh:
  • import adds a new resource to state.
  • refresh updates state for existing resources.
  • State file corruption: If state is lost, Terraform treats imported resources as "new" and may destroy them on apply.

Common Scenario Question

Question: "You need to import 50 EC2 instances into Terraform. What’s the most efficient approach?" Answer:
1. Use aws ec2 describe-instances to list all instances.
2. Generate Terraform configs dynamically (e.g., with jq or Python).
3. Import in batches using a script (e.g., for i in $(aws ec2 describe-instances ...); do terraform import ...; done).


7. ? Hands-On Challenge

Challenge: Import an existing AWS Security Group into Terraform.
1. Create a security group manually in the AWS Console (e.g., sg-1234567890abcdef0).
2. Write a Terraform config for aws_security_group.web.
3. Import the security group into state.
4. Verify no drift exists.

Solution:


# main.tf
resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Allow HTTP/HTTPS"
  vpc_id      = "vpc-1234567890abcdef0"

  ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] } }
terraform import aws_security_group.web sg-1234567890abcdef0
terraform plan  # Should show no changes

Why it works: The config matches the real security group’s attributes (name, VPC ID, rules).


8. ? Rapid-Reference Crib Sheet

Command/Syntax Purpose
terraform import <resource> <id> Import a resource into state.
terraform state show <resource> View attributes of an imported resource.
terraform state list List all resources in state.
terraform state rm <resource> Remove a resource from state without destroying it.
terraform refresh Sync state with real-world resources.
aws ec2 describe-instances List EC2 instances (use to find IDs for import).
aws s3api list-buckets List S3 buckets (use to find names for import).
⚠️ Default behavior: import does not generate config—you must write it manually.
⚠️ Provider region: Must match the resource’s region.


9. ? Where to Go Next

  1. Terraform Import Docs – Official command reference.
  2. AWS Provider Import Examples – Resource-specific import guides.
  3. Terraform State Management – Deep dive into state files.
  4. Terraform Best Practices – Production-ready patterns.


ADVERTISEMENT