Terraform adds controlled recovery for uncertain input shapes
Terraform 0.12.20 adds try and can for dynamic expression errors. Their useful boundary is input normalization, with ordinary configuration mistakes still reported as errors.
External data often has a less predictable structure than the configuration consuming it. A decoded object may omit a name or supply a collection in a different form. Repeating defensive expressions at every reference makes the rest of the module describe the input format as much as the infrastructure. Terraform 0.12.20 adds try and can to put that handling in a smaller, explicit part of the configuration.
The January 22 release describes both functions as support for data structures whose shape is unknown statically. That qualification matters. They handle errors that arise while evaluating an otherwise valid expression. They do not make a misspelled reference to an undeclared local value acceptable, and they do not turn an invalid configuration into a valid one.
I value Terraform because its configuration language gives reusable structure to infrastructure APIs. Normalizing irregular input before it reaches that structure is a useful extension of the same idea. A module becomes easier to understand when its ordinary expressions can rely on a documented shape.
Choosing a value after an evaluation error
The versioned try documentation defines an ordered operation. Terraform evaluates argument expressions in sequence and returns the first result that produces no error. The ordering is part of the configuration, because an earlier successful expression wins even when a later expression might also succeed.
A small object makes the distinction concrete. This configuration handles a missing attribute without requiring a provider or a remote service.
locals {
raw = {
name = "worker"
}
normalized = {
name = tostring(try(local.raw.name, null))
groups = try(local.raw.groups, [])
}
}
The name reference succeeds, and tostring gives it an explicit conversion. The groups reference fails dynamically because that attribute is absent from the object. Its fallback is an empty collection. Expressions elsewhere in the module can refer to local.normalized.groups without repeating the decision about absence.
The fallback is a design choice in the module. It belongs in the module contract alongside the accepted input forms. An empty groups collection says that omission means there are no groups. That may be the right contract for optional metadata. It would be a poor contract for information whose absence should stop an infrastructure change. The function supplies recovery mechanics, while the author remains responsible for choosing the meaning of recovery.
A successful evaluation also does not promise the type the surrounding module needs. The groups expression above makes no conversion of a present attribute. If the input contract accepts several types, normalization must explicitly convert them or retain an error. Wrapping an attribute reference alone cannot establish that every possible value has the intended meaning.
Supporting more than one input form
The documentation also shows how try can normalize a single string and a list into one collection form. A conversion can fail even when the referenced value exists, so successive conversions provide alternatives.
variable "example" {
type = any
}
locals {
example = try(
[tostring(var.example)],
tolist(var.example),
)
}
For a string, the first expression produces a single-element collection. For a list that cannot convert to a string, the second expression provides the collection conversion. The documented counterexample is an empty object. Neither conversion succeeds, so Terraform reports an error containing the problems encountered in the attempted expressions.
That final failure preserves an important boundary. Accepting two input representations does not mean accepting every representation. A catch-all empty collection at the end would change that contract by turning malformed input into apparently valid absence. The versioned documentation recommends keeping these expressions in dedicated normalization locals, where that decision remains visible.
Turning an error into a predicate
The accompanying can documentation describes a different result. can returns a Boolean indicating whether its expression evaluated without errors. It is useful when the consumer needs a validity decision instead of a replacement value.
The documented intended use is custom variable validation. That feature is an opt-in experiment in this release, and the release notes advise against production use of experimental validation rules. Treating the functions and the validation experiment as equally settled would misrepresent the release. A configuration can use the new recovery functions without presenting experimental validation syntax as a production guarantee.
Both functions preserve errors Terraform can establish before dynamic evaluation. Referring to an undeclared local value still fails inside either function. This keeps an input-dependent failure separate from an expression that cannot be valid for any input. A fallback cannot repair a reference to a name the module never declared.
I prefer this restricted form of recovery to configuration that suppresses failures throughout a module. It gives uncertain input a place to be interpreted without requiring every downstream reference to repeat that interpretation. The practical result is a module with fewer competing definitions of what missing data means, while malformed references continue to produce diagnostics at their source.