terraform importで既存AWSリソースをコード化する方法|importブロックで手作業インフラをIaC管理下に置く手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > terraform importで既存AWSリソースをコード化する方法|importブロックで手作業インフラをIaC管理下に置く手順
「既存のAWSリソースがTerraformで管理されていない。コンソールで手作業で作ったEC2やVPCをIaC化したいが、どこから手をつければいいかわからない」
こうした状況は、クラウド運用の現場でよく見かけます。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パターン


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

なぜ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.

「Import successful!」と表示されればstateへの登録は完了です。

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" }

複数リソースを一括インポートできるのも importブロックの強みです。

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)は削除してスッキリさせる
変数化amiinstance_typevar.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.

apply完了後、imports.tf 内の importブロックは自動的に削除しても構いません(stateにすでに登録済みのため)。gitにコミットする前に削除しておくと、次回の terraform plan で混乱が起きません。

AWSリソース別インポートID早見表

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、terraform importによる既存リソースのIaC化から、importブロックを使ったCI/CD対応のインポートワークフローまで、実務で使えるTerraformスキルをハンズオン形式で学べるセミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら
AWSリソースをインポートする際の「インポートID」はリソース種別によって異なります。AWSコンソールやCLIで確認できる値を使います。
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'
インポートIDの正確なフォーマットはTerraformの公式プロバイダードキュメント(registry.terraform.io)の各リソースページ「Import」セクションに記載されています。IDフォーマットが間違っていると後述するエラーになります。

よくある失敗パターンとトラブルシューティング

【エラー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 mvstate 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」セクション参照
手作業インフラをIaC管理に移行するのは、最初は手間に感じるかもしれません。しかし、importブロックを使えばPRとしてレビューを経た上でインポートでき、チーム全員が経緯を把握できます。一歩ずつ「terraform planでNo changes」を確認しながら進めることが、安全なIaC移行のコツです。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、terraform importとimportブロックを実務レベルで使いこなし、手作業インフラをゼロから体系的なIaC管理に移行するための実践セミナーを開催しています。
>> Terraform実践セミナーの詳細はこちら

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。