We close the chapter on backends and state with a very useful command in the real world: terraform import. It solves a very common situation: you have resources in AWS that were created manually (or with another tool) and you want Terraform to start managing them. Instead of deleting and recreating them, you "adopt" them into your state. Let's see how.

The problem: infrastructure that already exists

Few companies start from scratch with Terraform. Usually, they already have infrastructure created before adopting Infrastructure as Code:

  • Resources created manually from the AWS console (by clicking).
  • Resources created with old scripts or other tools.
  • "Inherited" infrastructure that nobody really remembers how it was set up.

Now you want to manage all that with Terraform (remember the advantages from Chapter 9). But there's a problem: Terraform doesn't know about those resources, because they're not in its state. If you simply write the code and run apply, Terraform would try to create new ones, duplicating what already exists! And you can't just delete the originals, because they're in production.

Real resource in AWS (created manually)   ✓ exists
Terraform state                           ✗ doesn't know it
   → if you apply, Terraform creates a DUPLICATE ⚠️

The solution: terraform import

terraform import solves this: it tells Terraform "this resource that already exists in AWS, register it in your state and associate it with this block in my code." That way, Terraform starts to manage the existing resource, without creating it again.

Real resource in AWS  ──(terraform import)──►  now it's in the STATE
                                               and Terraform manages it

Analogy: import is like adopting something that already exists. Imagine you move into a house that already has furniture. You don't throw it away to buy identical pieces: you register them as yours and start managing them. terraform import does that with infrastructure: it adopts what's already there instead of recreating it.

How it works, step by step

Importing a resource has two parts that you must do together:

Step 1: Write the code block

First, you write in your Terraform code the resource block that will represent that existing resource. For example, if you have an EC2 instance created manually:

resource "aws_instance" "servidor_heredado" {
  # for now, it can be pretty empty;
  # you'll complete it to match the real resource
}

Step 2: Run the import

Then, you run the import command indicating two things: which block in your code it corresponds to, and the real ID of the resource in AWS:

terraform import aws_instance.servidor_heredado  i-0a1b2c3d4e5f67890
                 │                                │
                 │                                └── Real ID in AWS
                 └── your code block

Terraform looks for that instance (i-0a1b2c3d4e5f...) in AWS and registers it in the state, associated with your servidor_heredado block. From now on, Terraform knows that resource belongs to it.

Step 3: Complete the code to match

Here's the part that requires care. After importing, your code must describe the resource exactly as it really is. If your code doesn't match the real resource, the next plan would show changes (Terraform would want to "adjust" the resource to your incomplete code).

After importing:  terraform plan
   → if the code matches reality → "No changes" ✓
   → if it DOESN'T match → shows differences; adjust your code until it fits

The goal is to get a plan that says "no changes": that confirms your code exactly reflects the real resource, and that the adoption was clean.

The challenge: importing lots of infrastructure

Importing one resource is simple. The problem comes when you have to import hundreds of inherited resources, writing the code for each one and adjusting it by hand. It's a tedious job. That's why there are aids:

  • import blocks in the code (modern versions of Terraform): allow you to declare imports directly in the code in a more organized way, and even generate a draft of the resource code automatically.
  • Third-party tools like Terraformer, which scan your AWS account and generate the code and state for many resources at once. Useful for large migrations, though it's always best to review the result.

Real-world example: a company has been creating infrastructure manually in AWS for years and decides to "bring order" by adopting Terraform. Instead of recreating everything (impossible, it's in production), they import their key resources into Terraform state, writing the code that describes them, until plan says "no changes." From then on, they manage that infrastructure with code, with all the advantages from Chapter 9, without having caused any interruption.

When to use import (and a warning)

  • Use it when you have valuable existing resources (in production) that you want to manage with Terraform without recreating them.
  • Do it carefully: importing touches the state (subchapters 20.2 and 20.3), so apply the same precautions: backup, no one else working, and verify with plan at the end.
  • Don't obsess over importing absolutely everything at once. It's valid to adopt resources little by little, starting with the most important ones.

What you should remember

  • terraform import allows Terraform to adopt resources that already exist in AWS (created manually or with another tool), registering them in its state without recreating them.
  • Without import, if you write the code for a resource that already exists and run apply, Terraform would create a duplicate. Import avoids that.
  • Process: write the resource block, run terraform import <block> <real-ID>, and complete the code until plan says "no changes" (sign of a clean adoption).
  • Like adopting furniture in a new house instead of throwing it away and buying identical pieces.
  • For many resources, there are aids: import blocks (which generate code drafts) and tools like Terraformer.
  • Apply state management precautions (backup, verification plan) and adopt resources little by little, starting with the most important ones.

You've finished Chapter 20! You now thoroughly master Terraform state: backends, locking, migration, and import. In Chapter 21 we'll see how to ensure the quality and security of your infrastructure with testing: validation, security analysis, and integration tests.

Cloud, AWS & Terraform — From Zero to Expert

Chapter 1 · What is cloud computing

Chapter 2 · The cloud market and major providers

Chapter 3 · Regions, availability zones and edge

Chapter 4 · Compute: EC2

Chapter 5 · Storage: S3

Chapter 6 · Networking: VPC

Chapter 7 · Identity and access: IAM

Chapter 8 · Managed databases

Chapter 9 · Why Infrastructure as Code

Chapter 10 · HCL: the Terraform language

Chapter 11 · Providers and state

Chapter 12 · Your first real infrastructure in Terraform

Chapter 13 · Load balancing and auto scaling

Chapter 14 · Serverless with Lambda

Chapter 15 · Messaging and events

Chapter 16 · Content delivery and DNS

Chapter 17 · Containers on AWS

Chapter 18 · Modules: reuse and composition

Chapter 19 · Workspaces and environment management

Chapter 20 · Remote backends and locking

Chapter 21 · Infrastructure testing

Chapter 22 · Terraform in CI/CD

Chapter 23 · Defense in depth

Chapter 24 · Observability: logs, metrics and traces

Chapter 25 · Cost optimization

Chapter 26 · High availability and disaster recovery

Chapter 27 · AWS Well-Architected Framework

Chapter 28 · Serverless architectures at scale

Chapter 29 · Data platforms on AWS

Chapter 30 · Multi-account and landing zones

Chapter 31 · Platform Engineering and Internal Developer Platform

Chapter 32 · Relevant AWS certifications

Chapter 33 · Projects to consolidate what you've learned

Chapter 34 · Resources and community

© Copyright 2024. All rights reserved