precondition / postcondition let you embed declarative "must hold" conditions directly inside resources, data sources, and other blocks. They are not procedural checks — Terraform evaluates them automatically along the plan / apply lifecycle.
On the upper-level Terraform certification, common topics include differences in evaluation timing, how unknown values are handled, and choosing between variable validation, precondition, postcondition, and check. In real codebases they are also a key guardrail for building IaC that does not silently break.
precondition runs before the operation (during plan and the early phase of apply) and guarantees that the configuration and inputs satisfy the assumed prerequisites. postcondition runs after the operation (after apply, or after a data read) and guarantees that the created, updated, or fetched result matches expectations.
These are declarative assertions with no state-changing side effects. When a condition is violated, Terraform fails the plan or apply and surfaces the explicit error_message to the user.
Minimal example (conceptual)
resource "some_resource" "app" {
# ... argument declarations
precondition {
condition = contains(["dev", "stg", "prod"], var.env)
error_message = "env must be one of dev / stg / prod"
}
postcondition {
condition = self.id != null
error_message = "No ID was returned after creation (check the provider response)"
}
}
precondition is evaluated at plan time. The expression must be true (or be unknown so it can be re-evaluated at apply). If it is definitively false, the plan fails. When the value is unknown at plan time, the plan notes that it will be re-checked at apply, and Terraform does so during apply.
postcondition is evaluated immediately after the target's create / update / read. If the final value is already known at plan time and is false, the plan fails; in most cases, though, it is evaluated against the actual value at apply. A violation does not trigger a rollback — you fix the configuration or environment and re-apply.
Where pre / post are evaluated during plan and apply
Writing for unknown-tolerance with can / try
resource "some_resource" "svc" {
name = var.name
precondition {
# var.labels may be null/unknown. Guard missing-key access with can()
condition = can(var.labels["team"]) && length(try(var.labels["team"], "")) > 0
error_message = "labels.team is required"
}
}
Nest precondition / postcondition inside the target block and specify condition and error_message. The condition must evaluate to a boolean. The error_message is shown directly to the user when violated, so make it concrete and tell the reader what action to take.
Inside postcondition on a resource or data block, self refers to the final value of that object. Inside precondition, self refers to the value during planning (which can be unknown). Module calls and outputs also support conditions, but what you can reference and when it is evaluated follow the surrounding block.
Declaration examples on resource and output
# resource example (conceptual)
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 must be <= 20 chars and only lowercase letters, digits, and hyphens" } postcondition { condition = self.id != null && length(tostring(self.id)) > 0 error_message = "Missing ID after creation — check the provider and permissions" } } # output example (asserts URL shape) output "service_url" { value = var.base_url precondition { condition = can(regex("^https?://", var.base_url)) error_message = "base_url must use an http(s) scheme" } }
Enforcing environment policy: catch "production must have encryption and tags enabled" rules early via precondition. Combinations of inputs (e.g. prod requires redundancy) can also be expressed here.
Result verification: use postcondition to verify that the final state from the provider or an external system meets your requirements — for example whether the assigned CIDR is in an allowed range, or whether the returned endpoint matches the expected format.
Environment-policy and result-verification example (conceptual)
# Environment policy (prod requires redundancy)
resource "some_resource" "api" {
replicas = var.replicas
env = var.env
precondition {
condition = var.env != "prod" || (var.env == "prod" && var.replicas >= 2)
error_message = "replicas >= 2 is required in the prod environment"
}
# Example: verify the assigned identifier carries the expected prefix
postcondition {
condition = can(regex("^api-", tostring(self.id)))
error_message = "Created ID violates policy (prefix 'api-' is required)"
}
}
Variable validation and the check block serve similar purposes but differ in scope and evaluation timing. On the exam, separate the options by asking "where, when, and what are we verifying?".
postcondition is unique in that it can verify "what was actually produced." It fits invariants that cannot be guaranteed from inputs alone.
| Feature | Declared in | Evaluation timing | self / other references |
|---|---|---|---|
| variable validation | variable block | At plan time (when inputs are evaluated) | No self (uses var.*) |
| precondition | resource/data/module/output | At plan time (re-evaluated at apply if unknown) | self is available on resource / data |
| postcondition | resource/data/module/output | Right after apply (or right after read) | self is available on resource / data |
| check block | Root module | At plan time (re-evaluated at apply if unknown) | No self (any expression) |
check block (cross-cutting assertion example)
check "naming_convention" {
assert {
condition = can(regex("^proj-[a-z0-9-]+
NicheeLab を読み込み中…
quot;, var.project_id))
error_message = "project_id must start with 'proj-'"
}
}
Terraform does not automatically roll back on a postcondition violation. Be aware that state and real resources can end up in a half-applied state, and include the next action (re-apply, fix the config) in the error message — it pays off in practice.
Mind unknown handling: guard plan-time-uncertain expressions with can() / try(), or move the check into postcondition to stabilize the plan diff.
Split conditions into locals for readability
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 = "The env / replicas combination violates policy"
}
}
Pro
問題 1
A team must verify that a production service resource actually has redundancy enabled after apply. Checking the input values alone is not enough — they want to evaluate the provider's final response. Which Terraform feature should they use, and where should they declare it?
正解: A
Verification based on the actual result (the final value returned by the provider) is exactly what postcondition is for. A postcondition inside the resource can evaluate self's observed values. variable validation only sees inputs, check is for cross-cutting assertions, and prevent_destroy only blocks deletion — none of those match the requirement.
Which blocks support precondition / postcondition?
You can declare them inside resource, data, module call, and output blocks. The condition expression must return true or false, and on a violation Terraform fails the operation and surfaces the error_message.
What happens when a condition involves a value that is unknown at plan time?
If the expression cannot be evaluated at plan time (it is unknown), the plan notes that the check will be re-evaluated at apply, and Terraform re-checks it during apply. To keep plans stable, guard with can() / try() or move the check to a postcondition that evaluates the final value.
Does Terraform roll back when a postcondition fails?
No, there is no automatic rollback. Apply just exits with an error. You need to fix the configuration or environment and re-apply (and you may need to manually reconcile state on the provider or external system side).
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
HCL Syntax: Terraform's Configuration Language (2026)
HCL2 fundamentals for Terraform — blocks, attributes, expres...
Terraform Authoring & Operations Pro: Complete Guide (2026)
Tactics for the Terraform Pro exam — module authoring, works...
Terraform Providers: Plugin Management Fundamentals (2026)
Provider mechanics — required_providers, versions, mirrors, ...
Terraform Resource Blocks: Declarative Infra Units (2026)
Resource block fundamentals — addresses, references, common ...
Terraform Data Sources: Read-Only External Data (2026)
Data source basics — declaration, refresh behavior, dependen...