Terraform未宣言リソースの対処法

冒頭まとめ terraform planやterraform validateで次のエラーが出た場合、参照先のリソースが現在のモジュールに宣言されていません。 Error: Reference to undeclared resource A managed resource "aws_security_group" "main" has not been declared in the root module. 最初に、説明文の型名aws_security_groupとラベル名mainを確認してください。同じモジュール内にresource "aws_security_group" "main"という宣言があるかを探します。ラベルのタイプミス、data.の付け忘れ、別モジュールのリソースを直接参照していることが主な確認点です。 クラウド上やstateにリソースが存在していても、設定内の宣言の代わりにはなりません。参照している式と、その式から見える宣言を確認する必要があります。 エラーメッセージの意味 通常の管理対象リソースは<型名>.<ラベル名>.<属性名>で参照します。たとえばaws_security_group.main.idでは、aws_security_groupが型、mainがresourceブロックのラベル、idが属性です。 ラベルはTerraformの設定内で使う名前です。AWS側の名前を設定するname = "web-sg"などの値とは別なので、そこが一致していても参照は成立しません。 Terraformの参照の公式文書は、管理対象リソース、データソース、モジュール出力を別の形式として説明しています。 参照先 参照の形式 管理対象リソース aws_instance.web.id データソース data.aws_ami.ubuntu.id 子モジュールの出力 module.network.vpc_id 入力変数 var.instance_count ローカル値 local.common_tags 本体のevaluate_valid.goでは、参照しているモジュールの設定からリソース宣言を探し、見つからなければこの診断を作ります。stateに記録されているかを調べることで、未宣言の参照を有効にする処理ではありません。 最初に型名・ラベル名・読込範囲を確認する エラーに表示されたファイルと行は、問題の参照が書かれた場所です。そこから参照先の宣言を探してください。 resource "aws_security_group" "web"しかないのにaws_security_group.main.idを参照していれば、ラベル名が違っています。エディターの検索で、型名とラベル名をそれぞれ確認できます。 宣言が別ファイルにある場合は、そのファイルの場所も確認します。同じディレクトリの.tf・.tf.jsonは一つのモジュールとして読み込まれますが、サブディレクトリのファイルは自動では取り込まれません。この範囲は設定ファイルの公式文書に明記されています。 たとえば、main.tfと同じ場所のresources.tfへ宣言を移すだけなら同じモジュールです。modules/network/main.tfへ移した場合は別モジュールになるため、元の場所から同じ参照を続けることはできません。 CLIでは通常、コマンドを実行したディレクトリがルートモジュールです。ローカルとCIで結果が違う場合は、実行ディレクトリや-chdirの指定、対象ファイルがCIに含まれているかも確認してください。 タイプミスとdata.の付け忘れを直す タイプミスなら、参照側を実際の宣言に合わせます。次は組み込みリソースterraform_dataを使った説明用の例です。 resource "terraform_data" "web" { input = "example" } output "value" { # 誤り:mainというラベルの宣言がない value = terraform_data.main.output } 宣言をそのまま使う場合は、outputの参照を次のように直します。 ...

2026年10月3日 · ErrorLog

final plan不整合の原因と対処法

冒頭まとめ terraform planは成功したのに、terraform applyの途中で次のエラーが出ることがあります。 Error: Provider produced inconsistent final plan When expanding the plan for aws_example.main to include new values learned so far during apply, provider "registry.terraform.io/hashicorp/aws" produced an invalid new value for .id: was known, but now unknown. This is a bug in the provider, which should be reported in the provider's own issue tracker. このエラーは、apply中に新しく確定した値を使って計画を更新したところ、providerが最初のplanと両立しない結果を返したことを示します。エラー本文にもあるとおり、基本的にはprovider側の不整合として扱います。 ただし、エラーに表示されたリソースが不具合の発生元とは限りません。そのリソースが参照している上流リソースの値がapply中に変わり、下流で不整合として検出される場合があります。 失敗後は同じapplyをすぐに繰り返さず、次の順番で確認してください。 terraform version terraform providers terraform plan applyの途中まで変更が反映されている可能性があります。取り直したplanを確認したうえで、providerの既知の不具合、直前のバージョン変更、外部からのインフラ変更を調べます。 Provider produced inconsistent final planとは Terraformのplanには、作成時にAPIから発行されるIDやIPアドレスなど、applyするまで確定しない値が含まれることがあります。planでは、このような値が(known after apply)と表示されます。 ...

2026年9月26日 · ErrorLog

Terraform countエラーの対処法

冒頭まとめ terraform planで次のエラーが出る場合、countに渡した値をリソース数として確定できません。 Error: Invalid count argument この見出しだけでは原因は分かりません。直後の説明文を確認してください。主な原因は、適用後でないと決まらない値、null、一時値、整数に変換できない値、負数です。 最も多いのは、別のリソースを作成した後で決まる属性からcountを計算している場合です。countは計画時にインスタンス数を確定する必要があるため、入力変数やローカル値など、計画時に分かる値から計算する形へ変更します。 Invalid count argumentの意味 countは、同じリソースやモジュールを指定した数だけ作るためのメタ引数です。たとえばcount = 3なら、example[0]からexample[2]までの3インスタンスが計画されます。 Terraform公式のcount文書によると、countが受け付けるのは整数です。さらに、一般的な引数と違い、Terraformが遠隔のリソースを操作する前に値が確定していなければなりません。 現在のTerraform実装では、Invalid count argumentの見出しが複数の検査で共通して使われています。説明文には次のような違いがあります。 The "count" value depends on resource attributes that cannot be determined until apply... The given "count" value is derived from an ephemeral value... The given "count" argument value is null. An integer is required. The given "count" argument value is unsuitable: <変換時のエラー>. The given "count" argument value is unsuitable: must be greater than or equal to zero. 一時値については処理経路の違いにより、ほぼ同じ内容の説明文が2種類あります。どちらも、計画と適用の間で保存できない値をcountに使ったことを示します。正確な文面はTerraformの版によって変わる可能性があるため、見出しではなく説明の内容で判断してください。 ...

2026年9月24日 · ErrorLog

Terraformを学ぶ:6段階ロードマップ

この記事にはアフィリエイト広告が含まれています。 冒頭まとめ 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 は、その位置に存在しない引数名を書いた場合です。プロバイダーの版を上げた後に出ることがあり、その場合は該当の版の資料を確認します。 ...

2026年8月9日 · ErrorLog

Invalid for_each argument:原因と解決策

冒頭まとめ Invalid for_each argument が拒む理由は3系統です。apply の後でないと決まらない値、map でも文字列の集合でもない値、機密の印が付いた値。別々の制限に見えますが、由来は1つです。 for_each に渡した値は、最終的に文字列をキーとする対応表へ変換されます。そしてそのキーが、そのままインスタンスのアドレスになります。公式ドキュメントは、Terraform がインスタンスを map のキーまたは集合の要素で識別すること、アドレスが <種別>.<名前>[<キー>] の形になることを明記しています。aws_iam_user.the-accounts["Todd"] の角括弧の中身が、渡した値から来ているという意味です。 アドレスには3つの条件が要ります。計画の時点で確定していること、並び順に左右されないこと、画面と記録に平文で出せることです。未確定値は1つ目を、list は2つ目を、機密値は3つ目を満たしません。制限を1つずつ避けようとすると行き詰まりますが、キーの条件として捉えると3つとも同じ方向で解けます。 つまりこのエラーは、値の書き方を咎めているのではありません。インスタンスの名前として使えない値が渡された、と言っています。直す先は式ではなく、何をキーにするかという設計です。 エラーの概要 出力は3つの部分でできています。先頭の要約、対象の位置、そして理由を述べる本文です。次は実際に報告された表示です。 Error: Invalid for_each argument on .terraform/modules/aks_primary/resource-tls.tf line 7, in resource "tls_private_key" "this": 7: for_each = var.ssh_key == null ? { "key" = {} } : {} ├──────────────── │ var.ssh_key has a sensitive value Sensitive values, or values derived from sensitive values, cannot be used as for_each arguments. If used, the sensitive value could be exposed as a resource instance key. 罫線の部分は補助情報で、式の中のどの変数が問題なのかを示します。ここに名前が出ていれば、追う対象はその変数です。 ...

2026年8月7日 · ErrorLog

Terraform Unsupported argument:原因と解決策

冒頭まとめ An argument named "..." is not expected here. を見たとき、多くの人は指し示された行の書き方が間違っていると考えます。しかし、この文言は設定ファイルのボディをあらかじめ決められたスキーマと照合した結果として出るものです。エラーが指すのは「引数名を書いた位置」であり、その引数を受け付けない側の情報は行番号のどこにも現れません。 したがって、直すべき対象は行ではなく、期待される引数の集合を決めている側です。これは大きく3系統に分かれます。resource / data / provider ブロックなら、いま初期化済みの provider が持つスキーマ。module ブロックなら、子 module 側に書かれた variable 宣言。terraform や lifecycle のような固定ブロックなら Terraform 本体です。 この区別を飛ばすと、典型的な失敗に入ります。module 呼び出しで拒否されたのに、呼び出し側(ルート)の variables.tf へ同名の変数を追加してしまう例が繰り返し報告されています。宣言が必要なのは子 module の側なので、これでは通りません。また、公式ドキュメントどおりに書いたのに拒否されるという報告も多く、この場合はドキュメントの版と実際にインストールされている provider の版がずれています。 判断の起点は、エラー出力の on ... line N, in ... の行です。ファイルパスと、その後ろのブロック種別を読めば、どの系統が拒否したかがほぼ決まります。末尾に Did you mean が付いているかどうかも、そのまま分岐材料になります。 エラーの概要 エラーは次の形で出ます。まず、module 呼び出しで未宣言の入力を渡した場合です。 Error: Unsupported argument on main.tf line 25, in module "sh": 25: num = 4 An argument named "num" is not expected here. 読むべきは3箇所です。1つ目は on に続くファイルパス。2つ目は in に続くブロック種別と名前。ここが in module "sh" なので、期待集合を決めているのは子 module の variable 宣言であり、provider は無関係です。3つ目は最後の行のサフィックス。この例では何も付いていません。 ...

2026年8月6日 · ErrorLog

Terraformロックファイル:原因と解決策

冒頭まとめ Inconsistent dependency lock file は、現在のTerraform設定が必要とするプロバイダーと、.terraform.lock.hcl に記録された選択が一致しないときに出ます。 Error: Inconsistent dependency lock file The following dependency selections recorded in the lock file are inconsistent with the current configuration: - provider registry.terraform.io/hashicorp/aws: locked version selection 5.100.0 doesn't match the updated version constraints "~> 6.0" To update the locked dependency selections to match a changed configuration, run: terraform init -upgrade この場合は、required_providers や子モジュールの変更に対して、ロックファイルが古いままです。設定変更を行った作業環境で次を実行し、差分を確認して .terraform.lock.hcl も一緒に版管理へ入れます。 terraform init -upgrade git diff -- .terraform.lock.hcl ただし、CIで発生するロックファイル関連の失敗が、すべて init -upgrade で直るわけではありません。開発環境とCIのOSやCPUが違い、対象環境の検査値がロックファイルにない場合は、次のような別のエラーになります。 ...

2026年8月5日 · ErrorLog

Terraform の 422 エラー:原因と解決策

冒頭まとめ Terraform で 422 Unprocessable Entity を受け取ったとき、まず押さえるべきことがあります。このエラーは Terraform が作ったものではありません。Terraform 本体には、要求の内容を検証して 422 を返す仕組みがありません。目にする 422 は、必ずどこかから中継されたものです。 出どころは2つに分かれます。プロバイダが相手先の窓口を叩いた結果として返ってきたものと、HCP Terraform や Terraform Enterprise の窓口を直接叩いた結果として返ってきたものです。前者の場合、応答の中身は相手先の流儀に従います。GitHub の窓口なら GitHub の書式、別のサービスならそのサービスの書式です。したがって、読み方は相手先の規則で決まります。Terraform の文書をいくら読んでも、応答の中身の意味は書かれていません。 もう1つ、共通する性質があります。Terraform の各層は、422 を再試行しません。レジストリ向けの通信で使われている仕組みは、再試行の対象を429と、501を除く500番台に限っています。AWS 向けの実装が再試行の対象として定義しているのも 500・502・503・504 です。どこにも 422 は含まれていません。これは怠慢ではなく設計です。要求を直さない限り結果が変わらないエラーなので、送り直す意味がありません。 エラーの概要 プロバイダ経由の場合、相手先の応答がそのまま、あるいは整形されて表示されます。GitHub を相手にした場合の例です。 Error: PATCH https://api.github.com/repos/example/sample: 422 Validation Failed [{Resource:Repository Field:default_branch Code:invalid Message:Cannot update default branch for an empty repository.}] 同じ相手先でも、詳細が空のことがあります。この場合、手がかりは短い文言だけです。 { "message": "Validation Failed", "errors": [], "documentation_url": "https://docs.github.com/rest/reference/repos#update-a-repository" } 別のサービスを相手にした場合は、書式が変わります。 Bad response statusCode [422]. Status [422 Unprocessable Entity]. Body: [baseType=error, code=InvalidBodyContent, message=must have at least one node pool] HCP Terraform の窓口を直接叩いた場合は、また別の形になります。整形の規約に沿った構造で、status と title と detail が並びます。 ...

2026年7月29日 · ErrorLog

Terraform の 502 エラー:原因と解決策

冒頭まとめ 502 Bad Gateway は、要求を取り次いだ中継役が、その先から正常な応答を得られなかったことを示します。同じ 5xx でも、待ちきれずに諦めた場合は 504 です。応答が得られなかったのか、待ち時間が尽きたのかという違いで、疑うべき場所も変わります。 Terraform では、502 の扱いに1つ特徴があります。本体のソースを読むと、レジストリへの問い合わせの再試行回数を設定する処理の説明に、「502 のような再試行可能なエラーに対して行う再試行の回数」と書かれています。つまり Terraform は、502 を再試行で吸収すべきものとして名指しで想定しています。 ところが、その既定値は1回です。合計2回で諦める設計になっており、失敗時の文言に「2回試した」と出るのはこのためです。想定しているにもかかわらず、既定では吸収できる幅が非常に狭い、という状態になっています。この回数は環境変数で増やせます。 もう1つ、読み方の注意点があります。エラー文に現れる URL は、要求の宛先であって、502 を作った相手ではありません。社内のプロキシがその先へ繋げずに 502 を返している場合でも、文言にはレジストリの URL が並びます。「レジストリが落ちている」と判断する前に、応答を作ったのが誰かを確かめてください。 エラーの概要 terraform init の段階では、再試行の回数を含む形になります。 Error: Failed to query available provider packages Could not retrieve the list of available versions for provider example/example: the request failed after 2 attempts, please try again later: 502 Bad Gateway returned from https://registry.terraform.io/v1/providers/... 「2回試した」という数字は、既定の再試行回数が1回であることに対応します。この数字が2以外になっていれば、環境変数で回数が変更されているということです。 プロバイダのファイルを取得する段階でも起きます。この場合、宛先はレジストリではなく配布元です。 Error: Failed to install provider Error while installing example/example v1.2.3: unsuccessful request to https://releases.example.com/terraform-provider-example_1.2.3_linux_amd64.zip: 502 Bad Gateway terraform plan や terraform apply の途中で出る場合は、プロバイダがクラウドの窓口を叩いた結果です。この場合、どの資源の処理で起きたかが示されます。応答の本文が各社のエラー形式ではなく、簡素な HTML であれば、作ったのは窓口ではなく手前の中継役です。 ...

2026年7月29日 · ErrorLog

Terraform の 504 エラー:原因と解決策

冒頭まとめ 504 Gateway Timeout は、要求を取り次いだ中継役が、その先からの応答を待ちきれずに返すエラーです。Terraform で目にする場合、出どころは3つに分かれます。本体がレジストリや配布元へ問い合わせたとき、プロバイダがクラウドの窓口へ要求したとき、そして経路上のプロキシや振り分け装置が返したときです。どの段階で出たかによって、調整できる場所も、調整すべき値も変わります。 先に押さえるべき性質が1つあります。504 は「失敗した」という通知ではありません。「待っていた側が待つのをやめた」という通知です。要求を受け取った先が処理を続けている可能性は残ります。作成の操作でこれを受けると、クラウド側には資源が出来上がっているのに Terraform は失敗として扱う、という食い違いが起きます。したがって、504 を受けたあとに最初にやるべきは再実行ではなく、実際に出来ているかどうかの確認です。 再試行の作法も段階ごとに違います。本体がレジストリへ問い合わせる部分は、既定で1回しか再試行しません。合計2回で諦める設計で、失敗時の文言に「2回試した」と出るのはこのためです。この回数は環境変数で増やせます。一方、プロバイダがクラウドの窓口を叩く部分は、各社の実装に従います。たとえば AWS の実装では 500・502・503・504 が再試行の対象と定義され、既定の試行回数は3回です。AWS のプロバイダはこれを25回まで引き上げています。 もう1つ、待ち時間の作法にも癖があります。共通して使われている再試行の仕組みでは、待ち時間は1秒から始めて倍々に増え、上限は30秒です。応答に含まれる待機の指示は 429 と 503 のときだけ読み取られ、504 では読み取られません。 エラーの概要 段階ごとに出方が違います。まず、terraform init の段階でレジストリや配布元から返る場合です。 Error: Failed to install provider Error while installing example/example v1.2.3: unsuccessful request to https://releases.example.com/terraform-provider-example_1.2.3_linux_amd64.zip: 504 Gateway Timeout レジストリへの問い合わせ自体が繰り返し失敗した場合は、再試行の回数を含む形になります。 Error: Failed to query available provider packages Could not retrieve the list of available versions for provider example/example: the request failed after 2 attempts, please try again later: 504 Gateway Timeout returned from https://registry.terraform.io/v1/providers/... この「2回試した」という数字は、既定の再試行回数が1回であることに対応しています。ソースでは、この文言を組み立てる処理が再試行の上限に達したときに呼ばれ、試行回数を添えるようになっています。 ...

2026年7月28日 · ErrorLog