この記事にはアフィリエイト広告が含まれています。
冒頭まとめ
Terraformのエラーを検索して1件ずつ直しているのに、翌日は別のエラーで止まる。この繰り返しから抜けるには、覚える順序を変える必要があります。
Terraformのエラーの多くは、4つの境界のどこかで起きています。設定ファイルと状態ファイルの境界、状態ファイルと実際のインフラの境界、計画時に確定している値と適用後に決まる値の境界、そして自分の作業と他人の作業の境界です。エラー文はこの境界のどれで止まったかを示していますが、境界の存在を知らないと文言が読めません。
DockerやKubernetesとの最大の違いは、Terraformが3つの世界を突き合わせている点にあります。あなたが書いた設定、Terraformが記録している状態、そして実際に存在するインフラです。この3つがずれたときにエラーになります。どれとどれがずれているのかを特定できれば、直す場所は自動的に決まります。
学ぶ順序は、初期化とプロバイダー、状態ファイル、計画と適用、参照と依存関係、変数とモジュール、トラブルシューティングの6段階です。各段階には「次へ進む目安」を置きました。飛ばした段階は、後の段階のエラーとして別の顔で現れます。
個別に直すだけでは理解しにくい理由
検索で見つかる対処は、多くの場合その環境で有効だった手順です。なぜ有効だったかは書かれていないことがあります。
たとえば、エラーが出たときに terraform state rm を実行したら通った、という手順があります。これはTerraformの管理下から対象を外す操作なので、確かにエラーは消えます。しかし実際のインフラは残ったままです。次に terraform apply を実行すると、Terraformはその資源が存在しないものとして作りに行き、名前の重複で失敗します。状態ファイルが何であるかを知らないまま実行すると、症状が別の形に移るだけになります。
同じことが -lock=false でも起きます。ロックを取得できないエラーを消すために付けるという手順は広く共有されていますが、これはロックの仕組みそのものを止める操作です。
エラー文も同じです。Terraformのエラーは、設定の文法が誤っているのか、値が計画時に決まらないのか、状態と実物がずれているのかを示しています。この区別は、次に説明する全体像を知っていれば読み取れます。
最初に理解するべきTerraformの全体像
先に3つの世界の関係を押さえます。ここを飛ばすと、後のすべての段階で判断がぶれます。
公式ドキュメントによれば、Terraformは管理下のインフラと設定について状態を保存しなければなりません。この状態は、実世界の資源を設定へ対応付けること、メタデータを追跡すること、大規模なインフラでの性能を改善することに使われます。既定では terraform.tfstate という名前のローカルファイルに保存されます。そしてTerraformは、どの変更を加えるかを決めるために状態を使い、あらゆる操作の前に更新処理を行って状態を実際のインフラへ合わせます(State)。
つまり流れはこうです。あなたが設定を書く。Terraformが状態を読み、実物と突き合わせて更新する。設定と状態を比較して差分を出す。その差分を適用する。
terraform plan の説明も同じ構造です。公式ドキュメントによれば、計画の作成時にTerraformは、既存の遠隔オブジェクトの現在の状態を読んで状態が最新であることを確認し、現在の設定と以前の状態を比較して差異を記録し、適用すれば遠隔オブジェクトが設定と一致するようになる変更操作の集合を提案します(terraform plan)。
この3つの世界を分けて考えられるようになると、エラーの読み方が変わります。設定の書き間違いなら設定を直す。状態と実物がずれているなら状態を合わせる。実物が想定と違うなら実物を確認する。混ぜると迷います。
まずは手元の環境を確認してください。
terraform version
terraform providers
学習ステップ1:初期化とプロバイダー
何を理解する段階か:Terraform本体が何もしないこと、実際の操作はプロバイダーが行うことです。
なぜエラー解決に必要か:エラー文の多くはプロバイダーが返しています。Terraform本体の問題とプロバイダーの問題を分けられないと、調べる先を間違えます。
最低限覚える概念:公式ドキュメントによれば、terraform init はTerraformの設定ファイルを含む作業ディレクトリを初期化するコマンドで、新しい設定を書いた後、あるいはバージョン管理から既存の設定を複製した後に最初に実行すべきものです。何度実行しても安全で、既存の設定や状態を削除することはありません。
ここで押さえるべきは、プロバイダーがバージョンを持つことです。同じ設定でも、プロバイダーの版が違えば挙動が変わります。ロックファイルが版を固定するのはこのためです。
実際に試すコマンド:
# 作業ディレクトリを初期化する(プロバイダーの取得と設定の準備)
terraform init
# 使われているプロバイダーと版を確認する
terraform providers
# プロバイダーを更新する(ロックファイルの内容が変わる)
terraform init -upgrade
# 設定の文法と内部整合を検査する(遠隔操作を伴わない)
terraform validate
terraform validate は遠隔への問い合わせをしません。文法の誤りや、存在しない引数名を書いた場合は、ここで先に見つかります。計画を走らせる前に通しておくと切り分けが速くなります。
次の段階へ進む目安:エラー文を見て、Terraform本体が出したものかプロバイダーが出したものかを区別できることです。
関連して発生しやすいエラー:Unsupported argument は、その位置に存在しない引数名を書いた場合です。プロバイダーの版を上げた後に出ることがあり、その場合は該当の版の資料を確認します。
学習ステップ2:状態ファイル
何を理解する段階か:状態ファイルが何を記録していて、なぜ手で編集してはいけないのかです。
なぜエラー解決に必要か:Terraform固有のエラーの多くは、この層で起きています。ここを飛ばすと、以降のすべての対処が当てずっぽうになります。
最低限覚える概念:状態は設定と実物を結び付ける対応表です。設定に書いたアドレスと、実際に作られた資源の識別子が組になって記録されています。この対応が切れると、Terraformは「設定にはあるが状態にない」ものを新規作成しようとし、「状態にはあるが設定にない」ものを削除しようとします。
状態を操作する手段も用意されています。公式ドキュメントによれば、terraform state の各コマンドは高度な状態管理を可能にするもので、状態を直接編集する代わりにこれらを使います(terraform state)。副コマンドには list、mv、pull、replace-provider、rm、show があります。
もう1つ、状態には資源の属性値がそのまま記録されます。データベースの初期パスワードのような値を扱う設定では、状態ファイル自体が機密情報を含みます。ローカルに置いたまま共有リポジトリへ入れない、という基本はここから来ます。
実際に試すコマンド:
# 状態に記録されているアドレスを一覧する
terraform state list
# 特定の資源の記録内容を確認する
terraform state show <アドレス>
# 状態の中身をそのまま出力する(確認用。編集して書き戻さない)
terraform state pull > /tmp/state-backup.json
# アドレスを変更する(資源を作り直さずに付け替える)
terraform state mv <旧アドレス> <新アドレス>
terraform state rm はTerraformの管理下から外すだけで、実際の資源は削除されません。逆に terraform destroy は実物を削除します。この2つを取り違えると、消したくないものを消すか、消したはずのものが課金され続けるかのどちらかになります。実行前に必ず terraform state pull で控えを取ってください。
次の段階へ進む目安:設定、状態、実物のどれとどれがずれているかを、エラー文から言えることです。
関連して発生しやすいエラー:already exists の形のエラーは、実物はあるのに状態に無い場合に出ます。この場合は作り直すのではなく、既存の資源を状態へ取り込む方向で検討します。
学習ステップ3:計画と適用
何を理解する段階か:差分の読み方と、計画時に確定していない値の扱いです。
なぜエラー解決に必要か:適用してから問題に気づくと、戻す手間が大きくなります。計画の出力を読めることが最大の防御になります。
最低限覚える概念:計画の出力は、資源ごとに操作の種類と、変わる属性を示します。作成、削除、その場での更新、作り直しの4つを区別してください。作り直しは削除と作成の組み合わせなので、データを持つ資源では特に注意が要ります。どの属性が引き金になっているかは出力に示されます。
もう1つが未確定の値です。他の資源を作ってみないと決まらない属性は、計画の時点では値が入りません。この状態の値を、資源の個数やキーの決定に使おうとすると、Terraformは計画を作れずに失敗します。
公式ドキュメントは、plan コマンド単体では提案された変更を実行しないこと、適用する前に提案された変更が期待どおりかを確認したり、チームに共有して広くレビューしたりするために使えることを述べています。
実際に試すコマンド:
# 差分を確認する
terraform plan
# 計画をファイルへ保存し、その計画をそのまま適用する
terraform plan -out=tfplan
terraform apply tfplan
# 保存した計画の内容を機械可読な形で確認する
terraform show -json tfplan | head -40
# 依存先だけを先に適用する(切り分け用の一時的な手段)
terraform plan -target=<アドレス>
terraform plan -out で保存した計画を apply に渡すと、確認した内容とまったく同じものが適用されます。plan と apply を別々に実行すると、その間に実物が変われば内容がずれます。継続的インテグレーションでは保存した計画を使うのが基本です。
-target は切り分けのための一時的な手段です。恒常的に使うと、設定全体としての整合が取れなくなります。
次の段階へ進む目安:計画の出力を見て、作り直しが起きる資源とその引き金になった属性を指摘できることです。
関連して発生しやすいエラー:Invalid count argument や Invalid for_each argument は、資源の個数やキーが計画時に決まらない場合に出ます。適用後にしか決まらない値をキー側に使っているのが典型です。
学習ステップ4:参照と依存関係
何を理解する段階か:資源どうしの順序がどう決まるかです。
なぜエラー解決に必要か:順序の誤りは、適用の途中で失敗する形で現れます。作られていないものを参照している、という状況です。
最低限覚える概念:Terraformは実行順序を明示的に書かせません。ある資源の属性を別の資源から参照すると、その参照関係から順序が決まります。これが基本です。
参照だけでは表せない依存もあります。値のやり取りは無いが順序だけ守ってほしい、という場合に depends_on を使います。逆に言えば、参照で表せる依存に depends_on を書く必要はありません。書き過ぎると並列度が落ち、適用が遅くなります。
循環参照も起こります。互いを参照し合う設定は順序を決められないため、Terraformは適用前に失敗します。
実際に試すコマンド:
# 依存関係をグラフとして出力する
terraform graph
# 特定の資源が何を参照しているかを設定側で探す
grep -rn "aws_vpc.main" *.tf
# 式の評価結果を対話的に確認する
terraform console
terraform console は、変数や参照が実際にどんな値になるかを試せます。型の取り違えを疑ったときに、設定を書き換えずに確認できます。
次の段階へ進む目安:ある資源を削除したときに、どの資源が巻き込まれるかを言えることです。
関連して発生しやすいエラー:Cycle を含むエラーは循環参照です。参照の向きを1方向に整理するか、間に別の資源を挟んで解きます。
学習ステップ5:変数、出力、モジュール
何を理解する段階か:設定を再利用可能な単位に切り分ける仕組みです。
なぜエラー解決に必要か:モジュールを使い始めると、エラーの発生場所と原因の場所が別のファイルになります。呼び出し側と呼び出され側のどちらを直すかを判断する必要があります。
最低限覚える概念:公式ドキュメントによれば、モジュールはTerraformがまとめて管理する資源の集まりです。すべてのワークスペースはルートディレクトリに設定ファイルを含み、これをルートモジュールと呼びます。module ブロックで設定するものは子モジュールと呼ばれ、適用時にルートモジュールが子モジュールを呼び出します。ルートモジュールは同じ設定の中で同じ子モジュールを複数回呼び出すこともでき、子モジュールがさらに入れ子の子モジュールを呼ぶこともできます(Modules overview)。
モジュールの読み込み元は、ローカルのファイルシステム、Terraformレジストリ、バージョン管理リポジトリなど複数あります。公開レジストリには広く使われるモジュールが多数あり、module ブロックで適切な読み込み元と版を指定すれば自動で取得されます。
変数と出力は、この境界を越える唯一の経路です。子モジュールの内部を外から直接参照することはできません。必要な値は出力として明示的に外へ出します。
機密の値については、変数に印を付けて画面表示を伏せられます。ただし状態ファイルには値が記録されるため、表示を伏せることと安全に保管することは別の話です。
実際に試すコマンド:
# 変数の値を確認する(対話的に評価する)
terraform console
# 出力値を確認する
terraform output
terraform output -json
# モジュールを含めた状態のアドレスを一覧する
terraform state list | grep "^module\."
# モジュールの取得と更新
terraform init -upgrade
terraform state list の出力で module. から始まるアドレスを見ると、どのモジュールが何を管理しているかが分かります。エラーのアドレスがモジュール配下なら、直す先は子モジュールか、それに渡している変数です。
次の段階へ進む目安:エラーに出てくるアドレスから、どのファイルを開けばよいかを判断できることです。
関連して発生しやすいエラー:モジュールに渡していない変数を参照している場合、変数が未定義であるという内容のエラーになります。呼び出し側で値を渡すか、子モジュール側で既定値を持たせるかを選びます。
学習ステップ6:失敗時の確認順序
何を理解する段階か:エラーが出たときに、どの順番で何を見るかです。
なぜエラー解決に必要か:ここまでの5段階は、この順序を実行するための前提知識です。順序が決まっていれば、初めて見るエラーでも調べる範囲を絞れます。
確認の順序:
第一に、実行したコマンドと作業ディレクトリを確認します。別の環境の設定に対して実行していないか、指定した変数ファイルが意図したものかを見ます。
pwd
terraform workspace show
第二に、エラー全文を読みます。Terraformのエラーは、該当するファイル名と行番号、そして問題のある式を示します。この位置情報が出ているかどうかで、設定の問題か実行時の問題かが分かれます。
第三に、文法と内部整合だけを先に確認します。
terraform validate
terraform fmt -check
ここで通れば、設定の書き方そのものは成立しています。
第四に、計画の差分を確認します。適用時に失敗した場合でも、まず計画を取り直して現在の差分を見ます。
terraform plan
第五に、状態と実物を確認します。差分が想定と違う場合、この2つがずれています。
terraform state list
terraform state show <アドレス>
第六に、ロックとプロバイダー側の状態を確認します。
terraform providers
terraform version
ロックの取得に失敗している場合、他の実行が動いているか、前回の実行が異常終了してロックが残っています。前者なら待ちます。後者の場合、terraform force-unlock を使う前に、本当に他の実行が動いていないことを確認してください。動いている最中に解除すると状態が壊れます。
詳細な記録が必要な場合は、環境変数で出力を増やせます。
TF_LOG=DEBUG terraform plan 2> tf-debug.log
この記録には認証情報が含まれることがあります。そのまま共有しないでください。
次の段階へ進む目安:初めて見るエラーに対して、この6段階のどこから調べるかを即座に決められることです。
避けるべき対処:-lock=false を常用する、確認せずに terraform force-unlock を実行する、状態ファイルを手で編集する、terraform state rm で実物を残したまま管理から外して放置する、-auto-approve を計画の確認なしに使う、といった手順は、症状を消しても原因を残します。特に状態の直接編集と強制解除は、後から戻せない不整合を生むことがあります。
独学と動画講座の使い分け
ここまでの6段階は、公式ドキュメントと手元の環境だけでも進められます。実際、この記事で参照した仕様はすべて公式ドキュメントに書かれています。
独学が向いているのは、目的が明確な場合です。特定のエラーを直す、特定のリソースの引数を確認する、といった作業は、公式ドキュメントとプロバイダーの資料を直接読むのが最短です。
一方でTerraformは、独学の初期に費用の問題が出ます。手を動かして確かめるには実際のクラウド資源を作る必要があり、消し忘れると課金が続きます。この不安があると、試す回数が減ります。
動画講座は、学ぶ順序と演習用の構成がまとまっている点が違います。何をどこまで作って確認するかが決まっていれば、消し忘れも減らせます。反面、自分に必要な部分だけを選んで進めるのは難しくなります。
どちらが適しているかは、いま何に時間を取られているかで決まります。仕様が分からなくて止まっているなら公式ドキュメント、何から手を付けるか決められなくて止まっているなら講座、という切り分けが実際的です。
学習後に自力で確認できるようにしたいこと
到達点を具体的に置いておきます。以下を自分の環境で確認できるようになっていれば、この記事の範囲は終わりです。
エラー文を見て、Terraform本体が出したものかプロバイダーが出したものかを区別できる。設定、状態、実物のどれとどれがずれているかを言える。計画の出力から、作り直しが起きる資源とその引き金になった属性を指摘できる。terraform state rm と terraform destroy の違いを説明できる。ある資源を削除したときに巻き込まれる資源を、依存関係から言える。エラーに出てくるアドレスから、開くべきファイルを判断できる。初めて見るエラーに対して、コマンドと作業ディレクトリ、エラー全文、文法検査、計画、状態、ロックとプロバイダーの順で調べられる。
これらは暗記ではなく、手を動かして確認する操作です。読んだだけでは身に付かない部分なので、費用のかからない範囲から1つずつ試してください。
まとめ
Terraformのエラーが繰り返し起きるのは、引数の書き方を知らないからではなく、境界を知らないからです。設定と状態、状態と実物、計画時に決まる値と適用後に決まる値、そして自分の作業と他人の作業。この4つの境界を押さえると、エラー文の読み方が変わります。
学ぶ順序は、初期化とプロバイダー、状態ファイル、計画と適用、参照と依存関係、変数とモジュール、トラブルシューティングです。それぞれに「次へ進む目安」を置いたのは、飛ばした段階が後から別の顔で現れるからです。
公式ドキュメントは仕様の確認先として最も確実です。一方で、学ぶ順序や演習用の環境を自分で組み立てる負担が大きいと感じる場合は、順序と演習がまとまった教材を使う選択肢もあります。
Terraformを体系的に学べるUdemy講座を確認する(PR)
内容や価格、対象範囲はリンク先のページで確認してください。自分がいまどの段階で止まっているかを踏まえて、必要な範囲が含まれているかを見るのが選び方の基準になります。
参考資料
免責事項:本記事の内容は、執筆時点の公開情報をもとに作成したものです。ソフトウェアや講座の内容、価格、提供条件は予告なく変更されることがあります。最新の情報はTerraform公式ドキュメントおよびリンク先の講座ページをご確認ください。本記事の情報を利用した結果生じたいかなる損害についても、著者および運営者は責任を負いかねます。