variable validation lets you declaratively enforce business rules that type constraints alone cannot express, using the Terraform language itself. You can attach explicit error messages, which improves both module reusability and safety.
The Associate exam focuses on basic syntax and evaluation timing, while the Pro exam often covers collections, conditional checks, and unknown-value handling. In practice, rejecting bad input at the module boundary also lowers maintenance cost.
A validation block inside a variable block requires its condition to be true; if it is false, evaluation fails with the text of error_message. The condition is a Terraform language expression that references var.<name>.
Evaluation timing is the moment the variable's final value becomes known. Usually that is at plan time, and a false result stops the plan with an error. If the value is unknown (for example because it depends on a data source) and the condition result is indeterminate, evaluation is deferred until apply, unless it can be decisively determined to be false.
| Item | Role | Example |
|---|---|---|
| type constraint | Enforces type and basic constraints | string, number, list(string), object({...}) |
| default | Default value when no input is supplied | default = 443 |
| validation | Declares rules that types cannot express | AMI must start with "ami-", port in 1..65535, etc. |
Evaluation pipeline (conceptual)
Minimal example
variable "image_id" {
type = string
validation {
condition = can(regex("^ami-[0-9a-f]+
NicheeLab を読み込み中…
quot;, var.image_id)) error_message = "image_id must be a valid AMI ID starting with 'ami-'. Example: ami-0123abcd"; } }
A validation block consists of condition (required) and error_message (required). condition is a pure expression and can use functions, operators, and for expressions. Keep error messages short and specific enough that the consumer can fix the input immediately.
Guard fragile checks like regex and numeric ranges with can() so that malformed inputs do not error out the regex itself. Keep heavy computation and large collection traversals to a minimum, and aim for expressions that stay readable and maintainable.
| Function | Use case | Notes |
|---|---|---|
| regex, can | Format and pattern validation | regex returns false on no match but errors on a bad pattern → wrap with can |
| startswith, endswith | Simple prefix/suffix checks | More readable than regex for simple cases |
| contains | Element/value membership check | Great fit for checking against an allowed set |
| alltrue, anytrue | Aggregate boolean-array checks | Very powerful in combination with for expressions |
| length, distinct | Length and uniqueness checks | Commonly used to express "no duplicates" |
Designing error messages: key angles
Common ways to write guards
variable "account_id" {
type = string
validation {
condition = can(regex("^[0-9]{12}
NicheeLab を読み込み中…
quot;, var.account_id))
error_message = "account_id must be a 12-digit number.";
}
}
variable "port" {
type = number
validation {
condition = var.port >= 1 && var.port <= 65535
error_message = "port must be in the range 1-65535.";
}
}Lean on readability for simple patterns. Use startswith for prefix checks, regex for strict formats, and plain comparisons for ranges.
regex tends to hurt maintainability, so keep it just-enough and prefer startswith or length when an alternative exists.
| Goal | Example condition | Example error message |
|---|---|---|
| AMI format | can(regex("^ami-[0-9a-f]+
NicheeLab を読み込み中…
| ami must be a hex ID starting with 'ami-'. |
| Minimum name length | length(var.name) >= 3 | name must be at least 3 characters long. |
| Replica count range | var.replicas >= 2 && var.replicas <= 10 | replicas must be in the range 2-10. |
| Environment enumeration | contains(["dev","stg","prod"], var.env) | env must be one of dev/stg/prod. |
Guidance for choosing a pattern
Examples: port and AMI
variable "port" {
type = number
validation {
condition = var.port >= 1 && var.port <= 65535
error_message = "port must be in the range 1-65535.";
}
}
variable "ami" {
type = string
validation {
condition = can(regex("^ami-[0-9a-f]+
NicheeLab を読み込み中…
quot;, var.ami))
error_message = "ami must be a valid ID starting with 'ami-'.";
}
}For lists, maps, and objects, combine for expressions with alltrue/anytrue to validate every element. Use distinct to enforce no-duplicates.
Constraints on tags or on key length and character classes are easier to maintain when you break the condition into readable units.
| Pattern | Condition (excerpt) | Notes |
|---|---|---|
| Per-element format check | alltrue([for s in var.subnets : startswith(s, "subnet-")]) | More readable than regex for simple formats |
| No duplicates | length(distinct(var.ids)) == length(var.ids) | Use distinct to check uniqueness |
| Required keys present | alltrue([for k in ["Name","Owner"] : contains(keys(var.tags), k)]) | Combine keys(map) with contains |
Flow for validating elements with a for expression
Examples: subnet IDs and tags
variable "subnets" {
type = list(string)
validation {
condition = length(var.subnets) > 0 && alltrue([for s in var.subnets : startswith(s, "subnet-")])
error_message = "subnets must contain at least 1 item, and each element must be an ID starting with 'subnet-'.";
}
}
variable "tags" {
type = map(string)
validation {
condition = alltrue([for k, v in var.tags : length(k) <= 128 && length(v) <= 256])
error_message = "tags keys must be at most 128 chars and values at most 256 chars.";
}
}
variable "names" {
type = list(string)
validation {
condition = length(distinct(var.names)) == length(var.names)
error_message = "names must not contain duplicate values.";
}
}Conditional validation is most readable when expressed as implication. To apply a strict constraint only when env is prod, write env != "prod" || strictCondition.
When unknown values (values not yet resolved at plan time) are involved, Terraform will not error unless the condition can be decisively determined to be false. Wrap spots like regex where a malformed input could blow up evaluation itself in can() to keep things safe.
| Situation | Terraform behavior | Mitigation / how to write it |
|---|---|---|
| Strict only for prod | Condition resolves to a definite true/false | env != "prod" || strictCondition |
| Unknown value involved | If the result is unclear, plan proceeds and re-evaluates at apply | Guard with can() or other safe functions |
| Malformed pattern | Expression evaluation itself errors out | Pair things like regex with can() |
How unknown evaluation looks
Apply a stricter constraint only when env is prod
variable "env" {
type = string
validation {
condition = contains(["dev","stg","prod"], var.env)
error_message = "env must be one of dev/stg/prod.";
}
}
variable "az_count" {
type = number
validation {
condition = var.env != "prod" || var.az_count >= 2
error_message = "When env=prod, az_count must be at least 2.";
}
}Exams tend to test the role split between type constraints and validation, the difference vs precondition/postcondition, evaluation timing, and unknown-value handling. validation is a static check on input values and cannot reference resource attributes or data source results.
Common anti-patterns: complex regex that destroys readability, depending on external state, and error messages that are too abstract. Narrow with types first, then secure adequate safety with simple functions.
| Feature | Defined in | Execution timing | Primary use |
|---|---|---|---|
| type constraint | variable | At value resolution (pre-plan stage) | Basic type/schema constraints |
| validation | variable | When the value becomes known (mainly at plan time) | Declarative business-rule checks |
| pre/postcondition | resource or module call | State checks during plan/apply | Checks on resource attributes and module outputs |
Do/Don't notes
[OK] Use type for the shape → validation for the rules
[OK] Branch with implication driven by env
[NG] Reference resource attributes or data in validation
[NG] Overly complex regex that destroys readabilityAnti-pattern fix example
# Bad example (cannot reference resource attributes)
# variable "size" {
# validation {
# condition = var.size <= aws_instance.example.cpu_core_count # not allowed
# error_message = "...";
# }
# }
# Good example: validation for relationships between inputs, precondition for resource state
variable "size" {
type = number
validation {
condition = var.size >= 1 && var.size <= 64
error_message = "size must be in the range 1-64.";
}
}
# (Reference) Use precondition/postcondition on the resource side for state checks
# resource "aws_instance" "example" {
# # ...
# lifecycle {
# precondition {
# condition = self.cpu_core_count >= var.size
# error_message = "The instance type does not satisfy size.";
# }
# }
# }Associate / Pro
問題 1
A module has two variables, env and instance_count. You want to require instance_count to be at least 3 only when env=prod, and impose no restriction otherwise. Which validation is the best fit?
正解: A
The idiomatic way to express an env-driven conditional constraint is implication. env != "prod" || var.instance_count >= 3 unconditionally allows non-prod and imposes the lower bound only when prod. B always requires 3+, which is wrong. C wrongly restricts dev/stg. D is also wrong because input-value consistency belongs in variable validation (state checks are for pre/postcondition).
When is validation evaluated — at plan or apply time?
If the value is known, validation runs at plan time and a false result stops the plan with an error. If the value is unknown and the result is indeterminate, evaluation is deferred to the apply stage.
Can I define multiple validation blocks on a single variable?
Yes. But readability drops as conditions pile up, so in practice it is best to combine related checks into a single readable expression, or keep a small number of logically separated blocks.
Can validation reference resource attributes or data source values?
No. validation checks input variables only and cannot reference resource or data attributes. Use validation for relationships between inputs, and pre/postcondition for resource-state consistency.
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...