Terraform

Terraform import ブロック徹底攻略: 宣言的インポートで既存資産を安全に取り込む

2026-04-19
NicheeLab編集部

既存のクラウド資産をTerraformの管理下に入れるとき、これまでは対話的な CLI インポートが主流でした。Terraform 1.5以降は import ブロックで宣言的に実施できます。

この記事では、宣言的インポートの基本、for_each/countやモジュール配下への取り込み、差分や生成コンフィグの扱い、試験での出題ポイントを実務レベルで整理します。

宣言的インポートの前提と試験ポイント

import ブロックは、既存リソースの「プロバイダ固有のインポートID」をTerraform状態に関連付ける宣言です。計画・適用のタイミングで評価され、対象のアドレスへインポートを実行します。データソースは対象外で、管理対象リソースのみがインポートできます。

従来の terraform import (CLI) が命令的手順だったのに対し、import ブロックはコード化されるため、コードレビューや再現性、CI/CDへの組み込みが容易です。適用後は import ブロックを削除しても状態は維持されますが、同じアドレスを重複インポートしようとするとエラーになります。

試験では次が問われやすいです: どのアドレスに取り込むか(count/for_eachやモジュール経路の指定)、インポートIDの形式の理解(プロバイダ依存)、差分発生時の挙動(計画での変更提案)、生成コンフィグの役割と限界、CLI と宣言的手法の使い分け。

  • import ブロックは to と id を最低限指定する
  • モジュール配下へも module.path.resource の完全修飾アドレスで指定可能
  • for_each/count で作られた個別インスタンスは [key]/[index] を明示する
  • 計画時に差分が出るのは正常。段階的に lifecycle.ignore_changes を活用
  • 一部リソースはプロバイダがインポート非対応(providerのドキュメント要確認)

import ブロックの基本構文と最小例

最小構成は to(取り込み先のリソースアドレス)と id(既存リソースのインポートID)です。インポートIDはプロバイダのドキュメントに準拠します。例えば AWS の一部リソースは ARN、S3 バケットは名称、VPC は vpc-xxxx のようなIDを使います。

適用フローはシンプルで、計画時に取り込みが検出され、適用時に状態へ登録されます。その後、リソース定義と実体の差分があれば、通常の変更計画として提示されます。

  • 複数の import ブロックを同一モジュール内に並べられる
  • 対象リソースの定義が存在しない場合、計画時に生成コンフィグ出力オプションで雛形を得られる(Terraform 1.5以降)
  • インポート成功後、import ブロックは恒久的に必要ではない(状態は保持される)

宣言的インポートの流れ

 既存クラウド資産           import ブロック           Terraform 状態
        |                         |                        |
        |  インポートID参照       |                        |
        |------------------------>|  to=addr / id=...      |
        |                         |   (plan/apply時評価)   |
        |                         |----------------------->|
        |                         |      状態に関連付け     |
        |                         |<-----------------------|
        |                         |  以後は通常の差分検出   |

最小例(AWS S3 バケットを取り込む)

provider "aws" {
  region = var.region
}

resource "aws_s3_bucket" "example" {
  bucket = "my-existing-bucket-123"
  acl    = "private"
}

# 宣言的インポート
import {
  to = aws_s3_bucket.example
  id = "my-existing-bucket-123"
}

# 実行例
# terraform init
# terraform plan
# terraform apply

for_each / count / モジュール配下のインポート

for_each や count で作られた個別インスタンスを取り込む場合、アドレスにキーまたはインデックスを明示します。多数の取り込みがあるなら、import ブロック自体に for_each を用いて宣言的に一括化できます(Terraform 1.5以降)。

モジュール配下のリソースは module. 経路を含む完全修飾アドレスを指定します。階層が深い場合も同様で、module.a.module.b.resource["key"] のように書きます。

  • count の場合: aws_instance.web[0] のようにインデックス指定
  • for_each の場合: aws_security_group.sg["admin"] のようにキー指定
  • import ブロックの for_each で多数インポートをコード化し、レビューと再現性を担保
  • モジュール配下: to = module.net.aws_vpc.main のように完全修飾アドレスを使う

アドレス指定の具体例

# count で作られた 0 番目のインスタンスを取り込む
resource "aws_instance" "web" {
  count         = 2
  ami           = var.ami
  instance_type = "t3.micro"
}

import {
  to = aws_instance.web[0]
  id = "i-0ab1c2d3e4f5"
}

# for_each の個別キーを取り込む
resource "aws_security_group" "sg" {
  for_each = {
    admin = "sg-admin"
    app   = "sg-app"
  }
  name = each.value
  vpc_id = var.vpc_id
}

import {
  to = aws_security_group.sg["admin"]
  id = "sg-0123456789abcdef0"
}

# import ブロックに for_each を使って一括宣言
variable "buckets" {
  type = map(string) # key=論理名, value=実在バケット名
}

resource "aws_s3_bucket" "b" {
  for_each = var.buckets
  bucket   = each.value
}

import {
  for_each = var.buckets
  to = aws_s3_bucket.b[each.key]
  id = each.value
}

# モジュール配下の取り込み
module "net" {
  source = "./modules/network"
}

import {
  to = module.net.aws_vpc.main
  id = "vpc-0abc123def456"
}

差分・生成コンフィグ・安全な移行手順

インポート直後は、既存リソースと現在のリソース定義が一致しないことが多く、計画に多数の変更が出ます。段階的移行では、まず安全に取り込むことを優先し、必要に応じて lifecycle.ignore_changes で過剰な差分を一時的に抑制し、設定の収斂後に外すのが現実的です。

Terraform 1.5以降では、対象リソースの定義がまだ無い場合に、計画時のオプションで生成コンフィグを出力できます。これは雛形生成であり、完全一致を保証するものではない点に注意してください。生成物はレビューして必要な属性のみを採用します。

  • まず取り込む → 状態のバックアップ → 差分の意味を精査 → 小さく適用
  • ignore_changes は最小限にし、段階的に外す
  • 生成コンフィグはあくまでたたき台。プロバイダ既定値や不要属性に留意
  • インポートIDは正確に。誤ったIDは apply エラーや意図せぬ差分の原因

一時的な差分抑制の例

resource "aws_s3_bucket" "example" {
  bucket = "my-existing-bucket-123"

  lifecycle {
    ignore_changes = [
      acl,                   # 現状のACLを一旦尊重
      versioning,            # 後で計画的に揃える
    ]
  }
}

# 収斂が済んだら ignore_changes を削除し、改めて計画・適用

CLI terraform import との比較と使い分け

小規模・単発なら CLI でも実務上は足りますが、チーム開発や再現性が重視される現場では import ブロックが有利です。レビュー可能なコードとして残せるため、監査性も高まります。一方、対話的な検証を素早く行う場面では CLI の手軽さも有効です。

生成コンフィグの自動出力や多数同時インポートの表現力は import ブロックが強みです。既存 IaC 標準や運用体制に合わせて併用を検討しましょう。

  • チーム開発・CI/CD・監査性重視 → import ブロック中心
  • スポット作業・試行錯誤 → CLI で素早く試す
  • どちらでも状態は同じ成果に到達するが、履歴性と再現性の差が大きい
観点import ブロック(宣言的)terraform import(CLI)注意点
宣言性・履歴コードに残る。PRレビュー・監査しやすい履歴は操作ログ頼み。手順の再現は人次第組織標準のコードレビューに乗せやすいのは import ブロック
一括実行for_each で大量インポートを宣言可能シェルでループなどが必要人為ミスの削減に効果
生成コンフィグ計画時の生成オプションで雛形を得やすい基本は手動で設定を用意雛形は要レビュー
モジュール階層完全修飾アドレスで自然に指定アドレス指定は可能だが手順が分散しやすい深い階層ほど宣言的が有利
自動化・CIそのままCIに組み込み可能実行順制御・安全策の実装が追加で必要失敗時の再実行容易性に差
学習コストTerraform言語に統一できるCLIフラグや都度の説明が必要チームの習熟状況で選択

落とし穴とトラブルシュート

よくあるつまずきは、すでに状態に登録済みのアドレスを再度インポートしようとしてエラーになるケース、インポートIDの誤り、プロバイダや別リージョン・エイリアスの取り違えです。まず terraform state list で現状を把握し、アドレスとID、プロバイダ選択を見直します。

インポートはリソース実体を変更しません。状態への関連付けのみを行い、その後の差分調整で初めて変更が発生します。影響範囲が大きい場合は、対象ワークスペースのバックアップを取ってから段階的に進めてください。

  • resource already managed エラー時は import ブロックを外すか、正しいインスタンスアドレスに修正
  • インポートIDの形式はプロバイダ依存。ARNか名称かを事前確認
  • provider alias/region の取り違えに注意(同名資産が別リージョンに存在しないか)
  • 差分が大きいときは ignore_changes とスコープ分割で安全に収斂
  • 状態破損に備え terraform state pull でバックアップを取得してから作業

現状確認の最小コマンド例

# 現在の状態にあるアドレス一覧
terraform state list

# 特定アドレスの詳細
terraform state show aws_s3_bucket.example

# 必要に応じてバックアップ
terraform state pull > state-backup.jsonl

問題で確認

Pro

問題 1

Terraform 1.5以降、既存の5個のS3バケットを同一モジュールで for_each により管理したい。宣言的に再現性を保ちつつ最小の手数で取り込む適切な方法はどれか。

  1. import ブロックに for_each を指定し、to で各キーのアドレスにマップする
  2. terraform import (CLI) を5回手動実行し、後で for_each に書き換える
  3. moved ブロックを使って未管理のバケットを for_each 管理下へ移動する
  4. data ソースとして参照し、その後 apply で自動的に状態へ取り込む

正解: A

宣言的インポートでは import ブロック自体に for_each を指定でき、to で aws_s3_bucket.resource[each.key] のように各インスタンスアドレスへ対応付けられる。CLI の反復実行は再現性とレビュー性で劣り、moved は既存の状態間の移動に使うもので未管理→管理への取り込みではない。data ソースはインポート対象外。

よくある質問

インポート後は import ブロックを削除しても大丈夫ですか?

はい。import ブロックは計画・適用時に取り込みを指示する宣言で、成功後は状態に関連付けが残ります。以後は通常のリソース定義で管理されるため、import ブロックは恒久的に不要です。残しても再適用時に同一アドレスが既に管理対象ならエラーになり得るため、完了後は削除するのが一般的です。

インポートは既存リソースの設定を変えますか?停止や再作成は発生しますか?

インポート自体は状態への関連付けのみで、既存リソースを変更しません。計画で差分が検出された場合に限り、適用で変更が提案されます。停止や再作成は、定義と実体の不一致が大きい場合に発生し得るため、計画内容を必ず精査してください。

インポートIDの形式が分かりません。どうすれば良いですか?

インポートIDはプロバイダの各リソースごとに定義されています。たとえば AWS では ARN、リソース名、もしくはリソースID(vpc-... など)が求められます。対象プロバイダのリソースドキュメントを確認し、要求される形式に合わせて正確な値を指定してください。

この記事で学んだ内容を問題で確認しましょう

16,000問以上の問題で実力チェック

無料で問題を解いてみる
この記事の著者

NicheeLab編集部

データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。


関連記事
Terraform

Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける

Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...

Terraform

Terraform Authoring & Ops Pro: 上位資格の範囲と対策

上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...

Terraform

Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす

Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...

Terraform

Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義

Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...

Terraform

Terraform Data Source徹底理解:既存リソースの参照で壊さず足す

Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...

Terraformの記事一覧 (102件)
© 2026 NicheeLab All rights reserved.