terraform import is the first step when bringing existing cloud assets under Terraform management. It is a "copy into state" operation and does not auto-generate configuration files. The crux, therefore, is how you keep state and configuration in sync.
This article compares the import block recommended since Terraform 1.5 with the legacy terraform import CLI that is still available, focusing on safety and exam preparation.
terraform import links an existing resource to Terraform's state (tfstate). From that point on, the resource is subject to terraform plan/apply. However, import itself does not generate configuration (HCL); you must author the configuration by hand.
Importing is all about "the correct address with the correct ID." The ID format varies by resource type, so confirm the format in the provider's official documentation. It is also assumed that credentials and provider configuration are correctly set up beforehand.
Minimal resource definition example (target of import)
resource "aws_s3_bucket" "example" {
bucket = "my-existing-bucket"
# Start with the minimum attributes that match the real environment
}For import, "where you bind it" matters. A Terraform resource address is composed of module hierarchy, type, name, and index (count/for_each). Importing to the wrong address forces later renames or re-imports, increasing risk and rework.
data sources cannot be imported. Only managed resources can be imported. To import into a module, use the full address including the module.<name> hierarchy.
Representative address examples
# Single
# terraform import aws_instance.web i-0123456789abcdef0
# Index 0 of count
# terraform import aws_instance.web[0] i-0aaa...
# for_each with key=blue
# terraform import aws_instance.web["blue"] i-0bbb...
# Inside a module
# terraform import module.app.aws_instance.web i-0ccc...From Terraform 1.5 onward, the recommended workflow is to declare an import block in configuration and then import via plan/apply. The advantages are that it can be reviewed as code and is highly reproducible. The import block takes to and id: to is the destination resource address, and id is the existing resource's ID.
Both the import block and a matching resource block must exist in configuration. Review the diff with plan and import into state with apply. Then, set attributes explicitly as needed to reduce drift.
Flow of the import block approach
Example of an import block with a matching resource (S3 bucket)
resource "aws_s3_bucket" "example" {
bucket = "my-existing-bucket"
}
import {
to = aws_s3_bucket.example
id = "my-existing-bucket"
}
# Run
# terraform init
# terraform plan # Review the proposed import
# terraform apply # Import into stateThe legacy terraform import ADDRESS ID is still available. It writes to state immediately. If no configuration exists (or attributes don't match) after importing, the next plan will show large diffs, so you must shape the configuration before and after the import.
Be careful with addresses for modules and for_each/count. If you import to the wrong address, terraform state mv can move it, but it is safer to plan ahead.
| Aspect | import block (1.5+) | terraform import CLI | Exam Note |
|---|---|---|---|
| Reproducibility / Auditability | High because it lives in code | Ad-hoc commands; history is hard to keep | Understand that a code-managed approach is the better choice |
| Timing of Execution | Performed as part of plan/apply | State updates immediately | Difference lies in whether it appears in plan |
| Reviewability | Reviewable in PRs | Tends to be verbal or runbook-based | Team-operation perspective is commonly tested |
| Config Generation | Not auto-generated | Not auto-generated | "import does not create configuration" is a classic exam point |
| Rollback | Safely applied via plan diff | Reversing mistakes is laborious | Also know the use cases for state mv/rm |
CLI examples and recovery operations
# Import (identifying by IAM role name)
terraform import aws_iam_role.app_role app-role
# Inside a module
terraform import module.app.aws_s3_bucket.logs my-log-bucket
# Move a mistaken import
terraform state mv aws_s3_bucket.wrong module.app.aws_s3_bucket.logs
# Delete a mistaken import (the actual resource is not deleted)
terraform state rm aws_s3_bucket.wrongIf plan shows a large diff right after import, the configuration isn't expressing the real entity well enough. The more you set attributes explicitly to match the live environment, rather than relying on strong defaults or computed values, the less drift you'll see. In particular, write encryption, lifecycle, tags, and deletion protection explicitly.
Decide on count/for_each and module hierarchy before importing to reduce later moves. When you need to rename, the moved block can safely transfer addresses. For large migrations, do not go all-at-once; proceed incrementally starting with low-importance resources, and rigorously back up state.
Examples of for_each imports and moved blocks
# for_each example (manage keys per tag value)
resource "aws_kms_key" "keys" {
for_each = toset(["blue", "green"])
description = "key-${each.key}"
}
import {
to = aws_kms_key.keys["blue"]
id = "1234abcd-...-blue-key-id"
}
import {
to = aws_kms_key.keys["green"]
id = "5678efgh-...-green-key-id"
}
# Safe address migration
moved {
from = aws_kms_key.keys["blue"]
to = module.security.aws_kms_key.keys["blue"]
}On the exam, the recurring points are: "import only loads into state and does not create configuration," "specify the correct resource address and ID," "data sources are excluded," "handling of module/for_each/count," and "differences between import block and the legacy CLI." Seeing the import proposal in plan before apply is the hallmark of the import block approach.
In operations, the key is to verify state with terraform state show after import and close the gap with configuration. Master state mv/rm for mistaken imports, the moved block for renames, state backup and locking, and proper workspace usage.
Frequently used inspection and adjustment commands
# Inspect state
terraform state list
terraform state show aws_s3_bucket.example
# Safe state moves
terraform state mv aws_s3_bucket.old aws_s3_bucket.new
# Switch workspace
terraform workspace select stagingAssociate / Pro
問題 1
A team uses Terraform 1.5+ and wants to safely bring an existing S3 bucket under Terraform management. They want a reviewable, reproducible procedure. Which approach is most appropriate?
正解: A
In 1.5+, managing the import block as code and importing via plan/apply is the recommended flow. It is highly reviewable and reproducible. The terraform import CLI also works, but state changes immediately, making review harder. Delete-and-recreate is unnecessary and risky. data sources cannot be imported.
Does import auto-generate configuration files?
No. import only links an existing resource to tfstate; it does not generate HCL. After importing, run terraform state show, then add the necessary attributes to your configuration to reduce drift.
How do I import resources that use for_each or count?
Import each element with its own address. Use [index] for count and ["key"] for for_each. Declare multiple import blocks, or run terraform import ADDRESS ID once per element via the CLI.
I accidentally imported to the wrong address. Can I undo it?
Without deleting the resource, you can safely move it to the correct address with terraform state mv. Unwanted entries can be removed from state only with terraform state rm. Neither command deletes the actual cloud resource.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...