precondition / postconditionは、リソースやデータソースなどに対して「満たすべき条件」を宣言的に埋め込む仕組みです。手続き的な検査ではなく、計画と適用のライフサイクルに沿って自動評価されます。
資格試験(上位レベルを想定)では、評価タイミングの違い、unknown値の扱い、variableのvalidationやcheckブロックとの使い分けが頻出です。実務でも「壊れないIaC」を作るための守りの仕掛けとして有効です。
preconditionは実行前(plan〜apply前段)に検証し、構成や入力が前提条件を満たしているかを保証します。postconditionは実行後(apply後段やread後)に検証し、作成・更新・取得された結果が期待と一致するかを保証します。
これらは宣言的アサーションであり、状態を変更する副作用はありません。条件に違反するとTerraformは計画もしくは適用を失敗として扱い、明示的なerror_messageをユーザーに返します。
最小例(概念)
resource "some_resource" "app" {
# ... 引数の宣言など
precondition {
condition = contains(["dev", "stg", "prod"], var.env)
error_message = "env は dev/stg/prod のいずれかである必要があります"
}
postcondition {
condition = self.id != null
error_message = "作成後にIDが返りませんでした(providerの応答を確認してください)"
}
}
preconditionはplan時点で評価され、式がtrueであること(またはunknownでapply時に再評価されること)が必要です。式がfalseと確定すればplanは失敗します。unknown(計画時に未確定)の場合、計画には「apply時に検証する」旨が含まれ、applyで再評価されます。
postconditionは対象のcreate/update/readの直後に評価されます。plan時に最終値が既知でfalseと判定できる場合はplanで失敗しますが、多くの場合はapply時の実値に対して評価されます。違反してもTerraformはロールバックしません。必要に応じて構成や環境を直し、再適用します。
Plan/Applyにおけるpre/postの評価位置
unknownに強い書き方(can/tryの活用)
resource "some_resource" "svc" {
name = var.name
precondition {
# var.labels may be null/unknown。missing key参照をcan()で保護
condition = can(var.labels["team"]) && length(try(var.labels["team"], "")) > 0
error_message = "labels.team を必須にしてください"
}
}
各ブロックでprecondition/postconditionをネストし、conditionとerror_messageを指定します。conditionは真偽値に評価される必要があります。error_messageは違反時にそのままユーザーに表示されるため、改善アクションが分かる具体的な文言にしましょう。
resourceやdataのpostcondition内ではselfがそのオブジェクトの最終値を指します。precondition内のselfは計画中の値(unknownを含む)です。module呼び出しやoutputでも条件を宣言できますが、参照できるものや評価タイミングは対象ブロックに従います。
resource・outputでの宣言例
# resourceの例(概念)
resource "some_resource" "db" {
name = var.name
version = var.version
precondition {
condition = length(var.name) <= 20 && can(regex("^[a-z0-9-]+
NicheeLab を読み込み中…
quot;, var.name)) error_message = "name は20文字以内の小文字英数字とハイフンのみ許可" } postcondition { condition = self.id != null && length(tostring(self.id)) > 0 error_message = "作成後にIDが返らないのは不正です(provider・権限を確認)" } } # outputの例(URL形式の前提を宣言) output "service_url" { value = var.base_url precondition { condition = can(regex("^https?://", var.base_url)) error_message = "base_url は http(s) スキームである必要があります" } }
環境ポリシーの強制: 本番は必ず暗号化やタグ付けを有効にする、といった前提をpreconditionで早期に弾きます。入力の組み合わせ(例: prodなら冗長化必須)もここで表現可能です。
結果検証: providerや外部システムの最終状態が必須条件を満たすかをpostconditionで検証します。たとえば実際に割り当てられたCIDRが許容範囲か、得られたエンドポイントが期待する形式かなどです。
環境ポリシーと結果検証の例(概念)
# 環境ポリシー(本番は冗長化必須)
resource "some_resource" "api" {
replicas = var.replicas
env = var.env
precondition {
condition = var.env != "prod" || (var.env == "prod" && var.replicas >= 2)
error_message = "prod 環境では replicas >= 2 が必須です"
}
# 例: 割り当てられた識別子が所定の接頭辞を持つことを検証
postcondition {
condition = can(regex("^api-", tostring(self.id)))
error_message = "作成結果のIDがポリシーに反する(prefix 'api-' 必須)"
}
}
似た目的の仕組みとしてvariableのvalidationやcheckブロックがあり、適用範囲と評価タイミングが異なります。試験では「どこで、いつ、何を検証するか」を軸に選択肢を切り分けます。
特にpostconditionは「実際にできあがったもの」を検証できる点でuniqueです。入力だけで保証できない不変条件に適しています。
| 機能 | 宣言先 | 評価タイミング | self等の参照 |
|---|---|---|---|
| variable validation | variable ブロック | plan時(入力評価時) | selfなし(var.*) |
| precondition | resource/data/module/output | plan時(unknownはapplyで再評価) | resource/dataでself可 |
| postcondition | resource/data/module/output | apply直後(read直後) | resource/dataでself可 |
| check ブロック | ルートモジュール | plan時(unknownはapplyで再評価) | selfなし(任意の式) |
checkブロック(横断アサーションの例)
check "naming_convention" {
assert {
condition = can(regex("^proj-[a-z0-9-]+
NicheeLab を読み込み中…
quot;, var.project_id))
error_message = "project_id は 'proj-' から始まる必要があります"
}
}
postcondition違反時にTerraformは自動ロールバックしません。状態や実リソースが中途半端になり得る点を理解し、エラーメッセージに対処法(再適用、設定修正)を含めると実務で役立ちます。
unknownの取り扱いに注意。計画時に確定できない式はcan()/try()で守る、もしくはpostcondition側で検証して計画差分を安定化させます。
localsで条件を分割して可読性向上
locals {
is_valid_env = contains(["dev", "stg", "prod"], var.env)
is_replica_valid = var.env != "prod" || var.replicas >= 2
}
resource "some_resource" "svc" {
replicas = var.replicas
env = var.env
precondition {
condition = local.is_valid_env && local.is_replica_valid
error_message = "env/replicas の組み合わせがポリシーに違反"
}
}
Pro
問題 1
あるチームは、本番環境のserviceリソースについて「適用後に実際の結果として冗長化が有効であること」を必ず検証したい。入力値のチェックだけでは不十分で、providerの最終応答に対して評価したい。Terraformのどの仕組みをどこに書くのが最も適切か?
正解: A
実際の結果(providerが返す最終値)に基づく検証はpostconditionが適任。resource内postconditionでselfの実測値を評価できる。variableのvalidationは入力値しか見えず、checkは横断アサーション向け。prevent_destroyは削除抑止であり要件に合致しない。
precondition/postconditionはどのブロックで使えますか?
resource、data、module呼び出し、outputの各ブロック内で宣言できます。条件式はtrue/falseを返し、違反時はerror_messageとともに処理が失敗します。
plan時にunknownな値が含まれる条件はどうなりますか?
plan時に評価不能(unknown)の場合、計画には「apply時に検証する」旨が表示され、applyで再評価されます。不安定さを避けるにはcan()/try()で守るか、postcondition側で最終値を検証します。
postconditionが失敗したらロールバックされますか?
自動ロールバックは行われません。エラーでapplyは終了します。必要に応じて設定や環境を修正し、再適用します(providerや外部システム側の状態は手動で調整が必要な場合があります)。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Terraform HCL 構文の基礎:Block / Attribute / Expression を正しく使い分ける
Terraform Associate で頻出の HCL 構文を、ブロック・属性・式の3視点で整理。実務で迷いがちな書き...
Terraform Authoring & Ops Pro: 上位資格の範囲と対策
上位レベルを想定したTerraformの設計・運用ドメインを整理し、実務で通用する対策を提示。モジュール設計、ステート運...
Terraform Providers の基本: プラグイン型アーキテクチャを正しく使いこなす
Associate レベルで押さえるべき Provider の基礎、インストール、バージョニング、認証、エイリアス運用を...
Terraform Resourceブロック徹底ガイド: 最小単位のリソース定義
Associateレベルで押さえるべきResourceブロックの構造、依存関係、メタ引数、ライフサイクル制御を実務目線で...
Terraform Data Source徹底理解:既存リソースの参照で壊さず足す
Terraform Associate向けに、Data Sourceを用いた既存リソース参照の基本、選択基準、評価順序、...