こうした状況は、クラウド運用の現場でよく見かけます。Terraform導入前に作ったリソース、別チームが手動で作成したセキュリティグループ、移行作業で一時的にAWSコンソールから起動したEC2インスタンスなど、IaC管理外のリソースは思いのほか多いものです。
この記事では、terraform import を使って既存AWSリソースをTerraformのstate管理下に移す方法を解説します。旧来の
terraform import コマンド方式(Terraform 1.4以前から利用可能)と、Terraform 1.5以降で使えるimportブロック(宣言的な新方式)の両方を比較しながら、実践的な手順を順を追って説明します。この記事のポイント
・terraform importでAWSコンソール作成済みリソースをIaC管理下に移せる
・Terraform 1.5以降のimportブロックはPRレビュー可能な宣言的方式で推奨
・state mv/rmとは軸が異なる——実インフラを「取り込む」操作がimportの本質
・よくある失敗はIDフォーマット不一致とplan差分の修正漏れの2パターン
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜterraform importが必要なのか?
Terraformを使ったIaC管理は「最初から全リソースをコードで作る」が理想ですが、現実のプロジェクトではそうはいきません。代表的な状況として、次のようなケースがあります。
・Terraform導入以前にAWSコンソールで手作業構築したリソースが本番稼働中
・急場しのぎでコンソールからセキュリティグループや踏み台EC2を追加した
・チーム外のメンバーがコンソールでS3バケットやIAMロールを作成した
・CloudFormationから移行する際に既存スタックリソースをTerraformに引き継ぎたい
これらのリソースをそのままにすると「コードに書いてないインフラ」が本番に存在し続けます。
terraform plan でも見えず、誰が何を変更したかも追えない。IaCの恩恵を受けられない状態です。terraform import(およびimportブロック)は、このギャップを埋めるための機能です。既存リソースをTerraformのstateに登録し、以降の変更をTerraformで管理できるようにします。terraform importの2つのアプローチ比較
現在、Terraformには既存リソースをインポートする方法が2つあります。| 方式 | 対応バージョン | 操作方法 | HCL生成 | PR管理 |
|---|---|---|---|---|
| terraform importコマンド | Terraform 0.x ~ | CLIコマンドで実行 | 手書き必須 | 不可(ローカル実行) |
| importブロック(新方式) | Terraform 1.5以降 | .tfファイルに宣言 | -generate-config-outで自動生成可 | 可(コードとして管理) |
terraform import コマンドは今でも動作しますが、Terraform 1.5以降を使っているなら importブロックへの移行を推奨します。理由は3つです。・インポート操作がコードに残るためPRでレビューできる
・
-generate-config-out オプションでHCLを自動生成できる・CI/CD(GitHub Actions等)パイプラインにそのまま組み込める
それぞれの手順を詳しく見ていきましょう。
terraform importコマンドによる移行手順(旧方式)
1. HCLリソースブロックを先に書く
terraform import コマンドは、先にHCLでリソースブロックを定義しておく必要があります。インポート前の状態ではリソースの詳細な設定は不明なため、最低限の骨格だけ書きます。例として、既存EC2インスタンスをインポートする場合:
# main.tf(骨格のみ・インポート前の暫定記述) resource "aws_instance" "web_server" { # ami, instance_typeは実際の値をあとで埋める ami = "ami-0123456789abcdef0" instance_type = "t3.medium" }
terraform plan を実行すると「リソースが差分あり」と表示されますが、インポート後に修正します。2. terraform importコマンドを実行する
コマンドの形式は次のとおりです。# 書式 terraform import [リソースタイプ].[リソース名] [インポートID] # EC2インスタンスのインポート例 terraform import aws_instance.web_server i-0abcdef1234567890
$ terraform import aws_instance.web_server i-0abcdef1234567890 aws_instance.web_server: Importing from ID "i-0abcdef1234567890"... aws_instance.web_server: Import prepared! Prepared aws_instance for import aws_instance.web_server: Refreshing state... [id=i-0abcdef1234567890] Import successful! The resources that were imported are shown above. These resources are now in your Terraform state and will henceforth be managed by Terraform.
3. terraform planで差分を確認・修正する
インポート直後は、HCLに書いた内容と実際のリソース設定に差分があります。terraform plan を実行してその差分を確認し、HCLを実態に合わせて修正します。$ terraform plan # aws_instance.web_server will be updated in-place ~ resource "aws_instance" "web_server" { id = "i-0abcdef1234567890" ~ instance_type = "t3.medium" -> "t3.large" + tags = { + "Environment" = "production" + "Name" = "web-server-01" } # ... 以下略 }
instance_type がHCLで t3.medium と書いたが実態は t3.large だった、というのがよくあるパターンです。HCLを実態に合わせて書き直し、terraform plan で「No changes.」と表示されるまで繰り返します。importブロックによる宣言的インポート(Terraform 1.5以降・推奨)
1. importブロックを書く
importブロックは通常の .tf ファイルに書きます。慣例として imports.tf というファイルを別途作成するチームも多いです。# imports.tf import { to = aws_instance.web_server id = "i-0abcdef1234567890" } import { to = aws_s3_bucket.static_assets id = "my-company-static-assets-2024" } import { to = aws_security_group.alb_sg id = "sg-0abc12345def67890" }
2. -generate-config-outでHCLを自動生成する
Terraform 1.5以降では-generate-config-out オプションを使うと、インポート対象リソースのHCLを自動生成できます。手動でHCLを書く手間が大幅に省けます。# HCLを自動生成しながらplanを確認する terraform plan -generate-config-out=generated_resources.tf
generated_resources.tf に次のようなHCLが生成されます(実際の値はAWSから取得)。# generated_resources.tf(自動生成例) resource "aws_instance" "web_server" { ami = "ami-0abcdef1234567890" instance_type = "t3.large" key_name = "my-keypair" subnet_id = "subnet-0abc12345def67890" vpc_security_group_ids = ["sg-0abc12345def67890"] root_block_device { volume_size = 30 volume_type = "gp3" } tags = { Environment = "production" Name = "web-server-01" } }
3. 生成されたコードを確認・整理する
自動生成されたHCLはそのまま使えることもありますが、いくつかの点を確認・整理します。・不要な属性の削除:AWSが自動設定するデフォルト値(例:
source_dest_check = true)は削除してスッキリさせる・変数化:
ami や instance_type は var.xxx に切り出すと再利用しやすい・コメント追加:インポート元の経緯をコメントで残しておくと運用時の助けになる
整理後、再度
terraform plan を実行してNo changesとなることを確認します。4. terraform applyでインポートを確定する
terraform plan で差分がなくなったら、terraform apply を実行してインポートを確定します。$ terraform apply aws_instance.web_server: Importing... [id=i-0abcdef1234567890] aws_instance.web_server: Import complete [id=i-0abcdef1234567890] aws_s3_bucket.static_assets: Importing... [id=my-company-static-assets-2024] aws_s3_bucket.static_assets: Import complete [id=my-company-static-assets-2024] Apply complete! Resources: 2 imported, 0 added, 0 changed, 0 destroyed.
imports.tf 内の importブロックは自動的に削除しても構いません(stateにすでに登録済みのため)。gitにコミットする前に削除しておくと、次回の terraform plan で混乱が起きません。AWSリソース別インポートID早見表
>> Terraform実践セミナーの詳細はこちら
| AWSリソース | TerraformリソースタイプID | インポートIDの形式 | 確認コマンド例 |
|---|---|---|---|
| EC2インスタンス | aws_instance |
インスタンスID(i-xxxxx) | aws ec2 describe-instances --query 'Reservations[*].Instances[*].InstanceId' |
| S3バケット | aws_s3_bucket |
バケット名 | aws s3 ls |
| セキュリティグループ | aws_security_group |
セキュリティグループID(sg-xxxxx) | aws ec2 describe-security-groups --query 'SecurityGroups[*].GroupId' |
| VPC | aws_vpc |
VPC ID(vpc-xxxxx) | aws ec2 describe-vpcs --query 'Vpcs[*].VpcId' |
| RDSインスタンス | aws_db_instance |
DBインスタンス識別子 | aws rds describe-db-instances --query 'DBInstances[*].DBInstanceIdentifier' |
| IAMロール | aws_iam_role |
ロール名 | aws iam list-roles --query 'Roles[*].RoleName' |
| Elastic IP | aws_eip |
Allocation ID(eipalloc-xxxxx) | aws ec2 describe-addresses --query 'Addresses[*].AllocationId' |
よくある失敗パターンとトラブルシューティング
【エラー1】「Error: Cannot import non-existent remote object」
指定したインポートIDが間違っているか、リソースが別リージョンにある場合に発生します。Error: Cannot import non-existent remote object While attempting to import an existing object to "aws_instance.web_server", the provider detected that no object exists with the given id. Only pre-existing objects can be imported; check that the id is correct and that it is associated with the provider's configured region.
・インポートIDのスペルミスや形式を確認する(EC2はインスタンスIDそのもの、S3はバケット名のみ等)
・Terraformのproviderブロックで指定しているregionとリソースが存在するregionが一致しているか確認する
・インポートIDの正確な形式はTerraformドキュメントの「Import」セクションで確認する
【エラー2】「Error: Resource already managed by Terraform」
すでにstateに登録済みのリソースを再度インポートしようとした場合に発生します。Error: Resource already managed by Terraform Terraform is already managing a remote object for aws_instance.web_server. To import to this address you must first remove the existing object from the state.
terraform state list でstateの内容を確認し、必要であれば terraform state rm で一度削除してから再インポートします。【エラー3】importブロック使用時の「-generate-config-out」書き込みエラー
すでに同名のファイルが存在する場合、上書きを拒否してエラーになります。生成先ファイル名を変更するか、既存ファイルを削除してから実行してください。【落とし穴】planでNo changesにならない状態が続く
インポートが成功してもterraform plan で差分が出続けることがあります。主な原因は次の2つです。・HCLの設定値が実態と異なる:
instance_type、タグ、セキュリティグループIDなどを実際の値に合わせて修正する・Terraformが管理しない属性の差分:
lifecycle { ignore_changes = [...] } で無視する設定が有効な場合に発生することがあるインポート後は必ず
terraform plan でNo changesを確認してからstateをコミットしてください。【重要】インポート操作はstateファイルを直接書き換えます。チーム運用時は必ずS3バックエンド+DynamoDBロック構成にしてから作業し、インポート前後のstateを必ずバックアップしてください。stateを誤って壊した場合のリカバリーは非常に困難です。
terraform importとterraform state操作の使い分け
似ているようで目的が異なる3つの操作を整理します。| 操作 | 目的 | 実インフラへの影響 | 典型的なユースケース |
|---|---|---|---|
terraform import / importブロック |
既存リソースをstateに取り込む | なし(読み取りのみ) | 手作業作成リソースのIaC化 |
terraform state mv |
state内でリソースのアドレスを変更する | なし(state操作のみ) | モジュール化・リファクタリング時のアドレス変更 |
terraform state rm |
stateからリソースを削除する | なし(管理対象から外すだけ) | Terraformの管理対象から外したいが削除はしない場合 |
terraform import はAWSに実際に存在するリソースをstateへ「取り込む」操作です。一方、state mv と state rm はすでにstate管理下にあるリソースの「アドレス変更」や「管理解除」です。実際の現場では、次のような順序で使い分けることが多いです。
・既存リソースをIaC管理したい → terraform import / importブロック
・モジュール化でリソースパスが変わった → terraform state mv
・このリソースはもうTerraformで管理しない → terraform state rm
本記事のまとめ
| やりたいこと | コマンド / 方法 |
|---|---|
| 既存EC2をstateに取り込む(旧方式) | terraform import aws_instance.xxx i-0abcdef1234567890 |
| importブロックで宣言的にインポート(推奨) | .tfファイルに import { to = ... id = ... } を記述 |
| HCLを自動生成しながらplan確認 | terraform plan -generate-config-out=generated.tf |
| インポート後のドリフト確認 | terraform plan でNo changesになるまでHCLを修正 |
| state内のリソース一覧確認 | terraform state list |
| インポートIDのフォーマット確認 | Terraformドキュメントの各リソース「Import」セクション参照 |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 前のページへ:Terraformのremote_stateでモジュール間データを受け渡す方法|tfstate分離設計とoutput参照の実践
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます