Terraform エラー修正:同じIDのリソースが既に存在する

intermediate🏗️ Terraform2026-03-26| Terraform 1.x、AWS / GCP / Azure、macOS / Linux / Windows

Error Message

Error: A resource with the ID already exists
#terraform#import#state#resource

エラーの内容

terraform apply を実行すると、処理が止まります:

Error: A resource with the ID "my-s3-bucket" already exists

  on main.tf line 12, in resource "aws_s3_bucket" "my_bucket":
  12: resource "aws_s3_bucket" "my_bucket" {

何が起きているか:Terraformがクラウドアカウントにすでに存在するリソースを作成しようとしています。そのリソースはステートファイルに存在しないため、Terraformはその存在を把握できていません。作成を試みますが、プロバイダーに拒否されます。

根本原因

クラウドプロバイダー上に存在するものと、Terraformが追跡しているものの間にズレが生じています。以下のようなケースで発生します:

  • 誰かが手動でリソースを作成した — AWSコンソールの操作、aws cli コマンド、GCPのUIなど。
  • 別のツールが管理している:別のTerraformワークスペース、Pulumi、CloudFormation、カスタムスクリプトなど。
  • ステートファイルが削除または破損した。リソースは動き続けているが、Terraformの記録は消えた。
  • terraform destroy が途中で失敗した。ステートは消去されたが、リソースは残った。
  • 新しいバックエンドに切り替えた際、ステートの移行手順をスキップした。

修正方法1:既存リソースをステートにインポートする(推奨)

リソースをインポートします。ステートに追加されると、Terraformはその存在を認識し、新規作成を試みなくなります。

ステップ1:クラウドプロバイダーでリソースIDを確認する

AWS S3の場合:

aws s3 ls | grep my-bucket-name

EC2インスタンスの場合:

aws ec2 describe-instances --filters "Name=tag:Name,Values=my-instance" \
  --query 'Reservations[*].Instances[*].InstanceId' --output text

GCPの場合:

gcloud compute instances list --filter="name=my-instance"

ステップ2:terraform import を実行する

構文:

terraform import <resource_type>.<resource_name> <provider_resource_id>

実際の使用例:

# AWS S3バケット — バケット名がIDになる
terraform import aws_s3_bucket.my_bucket my-actual-bucket-name

# AWS EC2インスタンス — インスタンスID(i-...)を使用
terraform import aws_instance.web i-0abc1234def56789

# AWSセキュリティグループ
terraform import aws_security_group.app sg-0123456789abcdef0

# GCPコンピュートインスタンス — リソースのフルパス
terraform import google_compute_instance.vm projects/my-project/zones/us-central1-a/instances/my-instance

# Azureリソースグループ — ARM完全リソースID
terraform import azurerm_resource_group.rg /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-rg

ステップ3:terraform plan を実行してドリフトを修正する

インポート後、.tf の設定が実際のリソースと完全に一致しない場合があります。以下を実行してください:

terraform plan

差分が表示されます — Terraformが変更しようとしている属性です。実際の値に合わせて設定を更新してください:

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-actual-bucket-name"
  # 不要な変更を抑制するために既存の設定を反映する
}

planの出力が以下になるまで調整を続けてください:

No changes. Infrastructure is up-to-date.

修正方法2:importブロックを使用する(Terraform 1.5以降)

Terraform 1.5では宣言型の import ブロックが追加されました。CLIコマンドは不要で、設定ファイルに記述するだけです:

import {
  to = aws_s3_bucket.my_bucket
  id = "my-actual-bucket-name"
}

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-actual-bucket-name"
}

その後:

terraform plan   # インポート内容をプレビュー
terraform apply  # インポートを実行

applyが成功するとブロックは消えます。オンボードしたリソースの記録としてバージョン管理に残すチームもあります。

修正方法3:リソースを削除してTerraformに再作成させる

リソースに重要なデータがなく、一時的なダウンが許容できる場合は、削除してTerraformにゼロから作成させましょう。

# 空のS3バケットを削除
aws s3 rb s3://my-actual-bucket-name

# 通常通りapplyを実行
terraform apply

**データベース、データが入ったバケット、または重要なリソースには使用しないでください。**空のバケットやテスト用セキュリティグループなど、本当に使い捨てのリソースにのみ適用してください。

修正方法4:別の名前を使用する

競合しない名前を選ぶのが最善の場合もあります。.tf ファイルの bucketname、または id 引数を変更してください:

resource "aws_s3_bucket" "my_bucket" {
  bucket = "my-bucket-name-v2"  # 一意な名前で競合なし
}

新しいリソースが必要で、既存のものを気にしない場合に有効です。

確認方法

インポート後、ステートに正しく反映されているか確認します:

# 追跡中のすべてのリソースを表示
terraform state list

# 特定のリソースを詳しく確認
terraform state show aws_s3_bucket.my_bucket

最終的なplanを実行します。変更がゼロであれば完了です:

terraform plan
# 期待される出力:
# No changes. Infrastructure is up-to-date.

まだ変更が表示される場合、.tf の設定にインポートしたリソースと一致しない属性があります。差分がなくなるまで調整を続けてください。

予防策

  • **Terraformが管理するリソースを手動で作成しない。**コンソール操作やTerraform外でのCLIコマンドが、まさにこの問題を引き起こします。
  • ロック機能付きのリモートステートを使用する — S3 + DynamoDBまたはTerraform Cloud。ローカルのステートファイルは、チームで共有する環境では消失・破損・上書きのリスクがあります。
  • **既存インフラをオンボードする前にインポート計画を立てる。**すでに存在するリソースに対して新しく resource ブロックを書いてそのままapplyするのは絶対に避けてください。
  • **常に terraform apply の前に terraform plan を実行する。**すでに存在するはずのものを新規作成しようとしていたら、それが早期警告サインです。
  • **リソースに managed-by = terraform タグを付ける。**チームメンバーが手を加えてはいけないリソースを一目で把握できます。

Related Error Notes