TerraformのpreconditionはREPLACE_AT_BRINGUPのplaceholderを検知できない
※本記事にはアフィリエイトリンクを含む場合があります。内容は広告の有無に影響されません。
結論
Terraformのprecondition検査はimage変数が空文字でないかしか見ないため、REPLACE_AT_BRINGUPのような空でないplaceholderのimage tagはplan時に素通りし、実際の失敗はCloud Run Jobやリビジョンがイメージを取得する実行時まで先送りされる。
結論
Cloud Runモジュールの precondition は var.image != "" で非空かどうかしか見ていないため、ビルド前に仮置きした REPLACE_AT_BRINGUP のようなplaceholderのimage tagは空文字ではない以上 terraform plan を素通りする。 実際の失敗は plan では起きず、Cloud Run JobやリビジョンがそのタグでイメージをPullしようとする実行時まで先送りされる。この違いを、plan が置換漏れを止めてくれるという誤った前提で書かれていた本番Terraformのコードコメントを、正しい前提に書き直したコミットから読む。
症状
本番環境のCloud Run定義 infrastructure/terraform/envs/prod/main.tf には、もともと次のようなコメントが置かれていた。
# infrastructure/terraform/envs/prod/main.tf:18-19(修正前, sha: cc5385d2)
# web イメージの置換漏れ(REPLACE_AT_BRINGUP のまま)は、下の cloud_run モジュールの
# precondition(image != "")が plan 時に検知して fail-fast する。
このコメントは「image 変数にビルド前の仮の値を置いたまま apply しようとしても、precondition が plan の段階で止めてくれる」という前提を書いている。しかし image 変数にビルド前の仮置きとして REPLACE_AT_BRINGUP のような文字列を入れた場合、この文字列は空文字ではない。
原因
実際に評価される precondition の中身は、infrastructure/terraform/modules/cloud_run/main.tf にこう書かれている。
# infrastructure/terraform/modules/cloud_run/main.tf:395-398(sha: bf9bc4d7、ロジックは修正前後で不変)
precondition {
condition = !var.enabled || var.image != ""
error_message = "enabled = true のとき image は必須です(build/push 済の web イメージ、apps/web/Dockerfile)。"
}
この条件式は「enabled が true のとき image が空文字でないこと」しか検査していない。REPLACE_AT_BRINGUP という文字列は長さを持つ非空の文字列なので、この条件は true と評価され、precondition は通過する。precondition は変数の型や非空性のような「plan時点で静的にわかること」しか検査できず、「その文字列が実在するイメージのURLかどうか」はクラウド側にリクエストを送らない限り判定できない。plan はローカルでの計画生成であり、この時点でCloud Run側へのイメージ取得(pull)は発生しない。
つまり置換漏れを本当に検知できる場面は、terraform apply によってCloud Run JobやリビジョンがそのタグでイメージをPullしようとする実行時であり、plan の precondition では原理的に検知できない。
直す
このコミット(bf9bc4d7、2026-10-09、author: cometa-kaito)が変更したのはTerraformのロジックではなく、envs/prod/main.tf 側のコメントだけだった。precondition の条件式自体は修正前後で一文字も変わっていない。
# infrastructure/terraform/envs/prod/main.tf:18-19(修正後, sha: bf9bc4d7)
# web イメージの置換漏れ(REPLACE_AT_BRINGUP のまま)は、下の cloud_run モジュールの
# precondition(image != "")では止まらない(空文字チェックのみ・plan 時点でイメージの
# 実在は検証されない)。実際に失敗するのは Job/リビジョンがそのタグで image を取得する実行時。
同じコミットで、同ファイル内の別の箇所(167行目付近)にあった同種の記述も合わせて訂正されている。直した内容は「precondition が守ってくれる範囲」の認識そのものであり、置換漏れを plan の段階で止めるための新しい検査は、このコミットには追加されていない。
再発防止
このコミットの記録には、置換漏れをplan時点で機械的に止めるための新しい検査やCIガードは含まれていない。書けるのは、なぜ「precondition が守ってくれる」という誤解がコメントとして残っていたかという構造だけだ。
precondition という名前と、error_message に書かれた日本語の説明文(「image は必須です」)は、読み手に「ここでイメージの正当性が検証されている」という安心感を与えやすい。しかし条件式そのものは != "" という最小限の非空チェックに過ぎず、変数名が image であることや、エラーメッセージが具体的であることは、検査の範囲の広さとは無関係だ。precondition ブロックを読むときは、周辺のコメントや変数名から検査範囲を推測するのではなく、condition に書かれた式そのものが何を比較しているかを直接確認する必要がある。
よくある質問
Q1preconditionはどんな条件を見ているのですか?
cloud_runモジュールのprecondition は `!var.enabled || var.image != ""` という1行で、enabled=trueのときvar.imageが空文字でないことだけを検査する。文字列として非空であれば、それが実在するイメージのURLなのか、ビルド前の仮の文字列なのかは区別しない。
Q2placeholderのimage tagとは具体的に何ですか?
web/Dockerfileをビルドしてpushする前の雛形状態で、var.imageに実在しないイメージ参照(例としてREPLACE_AT_BRINGUPのような文字列)を仮置きしておくこと。空文字ではないので、precondition の非空チェックは通過する。
Q3このコミットはTerraformのコードを直したのですか?
いいえ。precondition自体のロジックは変更していない。直したのはenvs/prod/main.tf側のコードコメントで、『preconditionが置換漏れを止める』という誤った説明を『plan時のpreconditionでは止まらず、実行時のイメージ取得で失敗する』という正しい説明に書き換えた。
Q4なぜレビューで気づいたのですか?
コメントの記録によれば、plan時の挙動について指摘を受けて修正したとされている。Terraformのprecondition自体はplanコマンドの実行中に評価される仕組みで、その時点でクラウド側のイメージ取得(pull)は発生しない。空文字チェックという限定的な検査と、イメージが実在するかどうかの検査は別物であるという整理が、コメント修正の形で記録に残っている。
確認した環境
- Terraform required_version ">= 1.9.0, < 2.0.0" / google, google-beta provider "~> 6.0"
- 2026-10-09 にコードコメントを修正(commit bf9bc4d7)
この記事の根拠
- Terraformファイル 18〜19行目コミット cc5385d
- Terraformファイル 18〜19行目コミット bf9bc4d
- Terraformファイル 395〜398行目コミット bf9bc4d
本文の主張は、上の記録に書かれていることだけです。運用しているリポジトリは非公開のため リンクは張れませんが、どのファイルの何行目を、どのコミット時点で見て書いたかは 記事ごとに残しています。推測で書いた箇所はありません。