Terraformのplan・applyエラーを調査・解決する方法|TF_LOG・Stateロック・ドリフトを実践的に切り分ける

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)Terraform > Terraformのplan・applyエラーを調査・解決する方法|TF_LOG・Stateロック・ドリフトを実践的に切り分ける
「terraform applyを実行したらエラーで止まった。でも何が原因か分からない」
Terraformを本番運用し始めると、こうした状況に必ず一度は直面します。エラーメッセージは英語で長く、どの行が本当の原因なのか見つけにくい。CI/CDに組み込んでいれば、パイプラインが突然止まって復旧に時間がかかることもあります。

この記事では、Terraformのplan・applyエラーを体系的に調査・解決するための実践手順を解説します。デバッグログ(TF_LOG)の活用、DynamoDBのStateロック解除、ドリフト検出とリカバリ、よくあるエラーパターンの切り分けまで、実際のコマンド出力を交えて説明します。

この記事のポイント

・TF_LOG=DEBUG 設定だけでAPIレベルのエラー原因を特定できる
・DynamoDB Stateロック詰まりはforce-unlockコマンドで安全に解除できる
・terraform plan -refresh-onlyでドリフト(コンソール手動変更)を検出できる
・planエラーの多くは「権限不足」「依存関係の循環」「プロバイダー設定ミス」の3パターンに分類できる


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

Terraformのplan・applyエラーを3種類に分類する

Terraformのエラーは大きく3種類に分類できます。この分類を最初に把握しておくと、調査の手順が明確になります。

クラウドプロバイダーのAPIエラー:権限不足(AccessDenied)、リソース上限超過、リージョン指定ミスなど。AWSとTerraformの通信段階で起きる問題です
tfstate整合性エラー:ローカルのtfstateと実際のAWSリソースの状態が食い違っている(ドリフト)場合や、Stateロックが残っている場合です
HCLコードの構文・依存エラー:変数の未定義、リソース間の循環依存、プロバイダーバージョンの不整合など、コード自体に問題がある場合です

エラーメッセージの先頭にある Error: の直後の文言を見て、どの種類かを判断するのが最初のステップです。

調査の優先順位は「APIエラー → tfstate整合性 → HCLコード」の順です。APIエラーから始めると、権限やネットワークの問題が素早く解消できます。

TF_LOGでデバッグログを有効にしてエラーを追う

Terraformには組み込みのデバッグログ機能があります。TF_LOG 環境変数を設定するだけで、APIリクエスト・レスポンスの詳細をすべてコンソールに出力できます。

1. TF_LOGの設定方法(ログレベル別の使い分け)

TF_LOG は以下の5段階で設定できます。

ERROR:致命的なエラーのみ(デフォルトの挙動に近い)
WARN:警告を追加表示
INFO:Terraformの動作フローを確認するのに適する
DEBUG:APIリクエスト/レスポンスを確認できる。権限エラーの調査に最も使う
TRACE:内部処理の全ログ(量が多いため、限定した場面で使う)

権限エラーや予期しないAPI動作の調査には DEBUG が適切です。

# TF_LOG を設定して terraform plan を実行する export TF_LOG=DEBUG terraform plan

2. TF_LOG_PATHでログをファイルに保存する

デバッグログはコンソールに大量に出力されるため、ファイルに書き出して後から解析するのが実用的です。

# ログをファイルに書き出す export TF_LOG=DEBUG export TF_LOG_PATH=/tmp/terraform-debug.log terraform plan # エラー行だけを抽出して原因を絞り込む grep -i 'error\|access denied\|unauthorized' /tmp/terraform-debug.log

3. APIエラーと権限問題をログで読む

以下は、IAM権限不足のリソース作成時に実際に出力されたデバッグログの抜粋です(アカウントID・リソース名はマスク済み)。

2026-06-15T14:23:07.441+0900 [DEBUG] provider.terraform-provider-aws_v5.xx: Response body: {"Code":"AccessDenied","Message":"User: arn:aws:iam::XXXXXXXXXXXX:user/terraform-ci is not authorized to perform: ec2:CreateSubnet on resource: arn:aws:ec2:ap-northeast-1::subnet/*"} 2026-06-15T14:23:07.441+0900 [ERROR] provider: Plugin called logger after being closed: logger: error="dial tcp: lookup provider.registry.terraform.io: no such host"

このログから「terraform-ci というIAMユーザーが ec2:CreateSubnet 権限を持っていない」と即座に特定できます。TF_LOGなしの通常エラー出力では「AccessDenied」の一行しか表示されず、どのリソース・どのアクションで失敗したか見えません。

調査後は必ず unset TF_LOG でログ出力を無効にしてください。大量のデバッグログがCI/CDのログに残り続けると、本番の機密情報(API応答)が記録されるリスクがあります。

Stateロックが解除されない時の対処

S3バックエンド+DynamoDB State Lockingを使用している環境では、terraform apply が途中でプロセス強制終了した場合や、ネットワーク切断が発生した場合に、ロックが残ったままになることがあります。

1. Stateロック詰まりのエラーを確認する

ロックが残っている場合、次の terraform planterraform apply を実行すると以下のエラーが出ます。

$ terraform apply Acquiring state lock. This may take a few moments... ╷ │ Error: Error acquiring the state lock │ │ Error message: ConditionalCheckFailedException: The conditional request failed │ Lock Info: │ ID: a8f3c2d1-xxxx-xxxx-xxxx-7b9e43f1a2c4 │ Path: s3://my-tfstate-bucket/env/prod/terraform.tfstate │ Operation: OperationTypePlan │ Who: terraform@ci-runner-02 │ Version: 1.7.5 │ Created: 2026-06-15 03:17:42.831 +0000 UTC │ Info: ╵

エラー内の ID: フィールドが、DynamoDBに残っているロックIDです。このIDを使って手動でロックを解除します。

2. force-unlockコマンドで安全に解除する

# 上記エラーのLock IDを指定して解除する terraform force-unlock a8f3c2d1-xxxx-xxxx-xxxx-7b9e43f1a2c4 # 確認プロンプトが出る。本当にロックされていないか確認してから yes を入力 Do you really want to force-unlock? Terraform will remove the lock on the remote state. This will allow local Terraform commands to modify this state, even though it may still be in use. Only 'yes' will be accepted to confirm. Enter a value: yes Terraform state has been successfully unlocked!

重要な注意点:force-unlock は、他のエンジニアがまさに terraform apply を実行中の場合に使うと、同時実行による状態破損が起きます。「誰も実行していない」ことをチームに確認してから実行してください。CI/CD環境では、パイプラインジョブが死んでいることをCI管理画面で確認します。

3. ロックを残さないCI/CDの設計パターン

CI/CDでTerraformを実行する場合、パイプラインのタイムアウトとプロセスキルによってロックが残りやすい構造になっています。以下の設計でリスクを下げられます。

タイムアウトを長めに設定する:大規模インフラ変更では30分を超えることがある。CI側のジョブタイムアウトをTerraformの想定実行時間より長くする
ロック取得後の強制終了を防ぐ:シグナルトラップ(trap)でプロセス終了時に terraform force-unlock を自動実行するスクリプトを用意する(ただし実行中の場合を除く)
DynamoDB TTLを設定する:DynamoDBのロックテーブルにTTL(例:24時間)を設定しておけば、異常終了したロックが自動削除されてオペレーターが気づかないまま放置される事態を防げる

これら3つの設計を事前に組み込んでおくことで、CI/CDパイプラインのロック詰まりによる作業停止リスクを大幅に下げられます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Terraformのエラー調査・Stateロック設計・チーム運用のスキルをハンズオン形式で習得できるセミナーを開催しています。
Terraformマニュアルを無料で受け取る >>

ドリフト検出と修正(コンソール手動変更の影響を確認する)

「先週のterraform planではNo changesだったのに、今日実行したら差分が大量に出た」
これはドリフトと呼ばれる状態です。AWSコンソールや他のツールがTerraform管理外でリソースを変更すると、tfstateと実際のリソースの状態が食い違います。

1. terraform plan -refresh-onlyで現状差分を把握する

terraform plan -refresh-only(Terraform 0.15.4以降)は、リソースの変更や追加を提案せず、「現在のAWSリソースの状態をtfstateに反映するとどうなるか」だけを表示します。

$ terraform plan -refresh-only Terraform will perform the following actions: # aws_security_group.web_sg will be updated in-place ~ resource "aws_security_group" "web_sg" { ~ description = "managed by Terraform" -> "Temp change for test" id = "sg-0a1b2c3d4e5f67890" # (5 unchanged attributes hidden) } Plan: 0 to add, 0 to change, 0 to destroy. Changes to Outputs: 0 changes to Outputs. Note: This plan only includes refreshing the Terraform state (not making any changes to actual infrastructure).

このコマンドを実行することで、誰がいつコンソールでセキュリティグループの説明を変更したかが分かります。出力に が付いたリソースがドリフトしている対象です。

2. ignore_changesで吸収するかimportで取り込むかを判断する

ドリフトが判明したら、2つの対処法のどちらかを選びます。

Terraform管理に戻す:コンソール変更を元に戻すか、Terraformコードを変更後のリソース状態に合わせてから terraform apply する
ドリフトを許容する:コンソール側の変更を正として認めたい属性に ignore_changes を設定して、Terraformが差分として検出しないようにする

設定上の変更はコンソール変更を許容するということです。チーム全体でどちらの方針を採るか、インフラの「真実の情報源(Single Source of Truth)」をどこに置くかを明確にすることが重要です。

# コンソール変更の description を Terraform が変更しないよう ignore_changes で吸収する resource "aws_security_group" "web_sg" { name = "web-sg" description = "managed by Terraform" lifecycle { ignore_changes = [description] } }

よくあるplanエラーのパターンと解決策

1. 「Error: Cycle detected」依存関係の循環

リソースAがリソースBを参照し、リソースBもリソースAを参照するような循環依存が発生すると、このエラーが出ます。

│ Error: Cycle: aws_security_group.app_sg, aws_security_group.db_sg

解決策:aws_security_group_rule リソースを使ってセキュリティグループルールを分離することで循環を解消できます。aws_security_group 内の ingress / egress ブロックを削除し、別リソースとして定義します。

2. 「Error: Provider configuration not present」

バックエンド設定が見つからない場合や、terraform init が完了していない状態で plan を実行した場合に発生します。

│ Error: Provider configuration not present │ │ To work with aws_instance.web its original provider configuration at │ provider["registry.terraform.io/hashicorp/aws"] is required, but it has been │ removed. This occurs when a provider configuration is removed while objects │ created by that provider still exist in the state.

解決策:terraform init を再実行します。CI/CD環境ではプロバイダープラグインがキャッシュされていない場合に頻発します。-reconfigure フラグを付けて強制的に再初期化することで解消できます。

# バックエンド設定を含めて再初期化する terraform init -reconfigure

3. 「Error: creating resource already exists」リソース重複エラー

Terraformの管理外で手動作成されたリソースと同じ名前のリソースをTerraformで作ろうとした時に発生します。

│ Error: creating S3 Bucket (my-app-config-bucket): BucketAlreadyExists: │ The requested bucket name is not available. │ status code: 409, request id: XXXXXXXXXXXX

解決策:既存リソースをTerraform管理下に取り込む (terraform import) か、コード側のリソース名を変更して別名で作成します。Terraform 1.5以降では import ブロックをHCLに書いてplanの一部として実行できます。

applyが途中で止まった時のリカバリ手順

terraform apply が途中でCTRL+Cや強制終了で止まった場合、Terraformは「途中まで適用した状態」をtfstateに書き込んでいます。この状態からの回復は次の手順で行います。

1. 現在のtfstateを確認する

# 現在 Terraform が管理しているリソースの一覧を表示する terraform state list # S3バックエンドの場合、現在のtfstateをローカルにダウンロードしてバックアップ terraform state pull > /tmp/tfstate-backup-20260708-100711.json

2. Stateロックを確認・解除してから再実行する

applyが強制終了した場合、Stateロックが残っている可能性があります。前述の force-unlock でロックを解除してから、terraform apply を再実行してください。

Terraformは「tfstateにすでに存在するリソース」はスキップして「まだ適用されていないリソース」だけを処理します。途中から再開できる仕組みになっているため、通常は再実行で問題なく完了します。

3. 一部のリソースが壊れた状態になっていた場合

まれに、applyが途中で止まったことで「半壊」したリソースが残る場合があります(RDSのクラスター作成中に止まった等)。この場合は以下の手順を取ります。

terraform state list で問題のリソースを特定する
・AWSコンソールで当該リソースの実際の状態を確認する
・リソースが中途半端に作成されている場合、AWSコンソールで手動削除してから terraform state rm でtfstateから除外する
・その後 terraform apply で再作成する

本記事のまとめ

Terraformのplan・applyエラー調査で使うコマンドと手順をまとめます。
問題のパターン 使うコマンド・設定
エラーの原因が分からない export TF_LOG=DEBUG
ログが多すぎてファイルに保存したい export TF_LOG_PATH=/tmp/terraform-debug.log
Stateロックが残っている terraform force-unlock <LOCK_ID>
コンソール変更との差分を確認したい terraform plan -refresh-only
applyが途中で止まってしまった force-unlock後にterraform applyを再実行
planで循環依存エラーが出た aws_security_group_ruleでルールを分離
initが未完了またはキャッシュが壊れた terraform init -reconfigure
Terraformのエラー調査で最も重要なのは「分類から始める」ことです。APIエラーならTF_LOG、Stateならforce-unlockまたはplan -refresh-only、HCLコードなら構文チェックと依存関係の整理という3ルートに分けるだけで、原因特定の時間が大幅に短縮できます。

関連記事もあわせてご覧ください。
Terraformのtfstate管理とS3バックエンド設定|チーム運用で壊さないための基礎
Terraformのstate操作コマンド実践ガイド|state list・state mv・state rmで既存インフラを安全に管理する方法
Terraformのlifecycleブロックで本番リソースを誤削除から守る方法
TerraformのIaC設計知識をLinuxマスターの無料マニュアルで体系的に学べます。plan・applyのトラブルシュート・state設計・module化まで、現役エンジニアが現場の実践知識をまとめています。
Terraformマニュアルを無料で受け取る >>

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

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

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

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

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

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

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

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

この記事を書いた人

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

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

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