Skip to navigation

Conversion, errors, and sensitivity

Conversions are explicit. Resource fields and external inputs retain their strict declared types.

FunctionResult and rules
tostring(value)String from a string, finite number, or boolean; null stays null.
tonumber(value)Finite number from a number or numeric string; integer spelling preserves integer precision; null stays null.
tobool(value)Boolean from a boolean or exactly true/false string; null stays null.
tolist(value)List from list or set, with primitive element unification; null stays null.
tomap(value)Map with primitive value unification; null stays null.
toset(value)Deterministically sorted and deduplicated set from list or set with primitive unification; null stays null.
convert(value, type)Recursive conversion using the constraints below.
try(expressions...)Evaluate left to right until one succeeds; all failures produce a safe error. Failed attempts retain observed annotations.
can(expression)Boolean indicating whether evaluation succeeds; attempted dependencies and sensitivity are preserved.
sensitive(value)Recursively mark the value, elements, and keys sensitive; retain dependencies.
nonsensitive(value)Explicitly remove sensitivity recursively; retain dependencies.
issensitive(value)Public boolean reporting sensitivity; retain dependencies without revealing the value.

tolist, tomap, toset, and coalesce unify primitive elements: when a string is present, numbers and booleans become strings; incompatible mixtures fail. Nested collection shapes keep Rivet’s existing behavior rather than implementing Terraform’s complete type unification.

convert(value, type) accepts string, number, bool, any, list(T), set(T), map(T), tuple([T, ...]), and object({ field = T, ... }). Object conversion discards extra attributes, rejects missing required attributes, and accepts optional(T) or optional(T, default). Constraints are validated even for empty or null inputs; optional defaults evaluate and convert once per constraint before reuse. These constraints belong to convert; any and two-argument optional defaults do not extend variable and output declarations.

try and can take explicit unevaluated expressions and do not accept final argument expansion. Ordinary functions evaluate arguments eagerly: lookup(map, key, fallback) is not a lazy fallback. Both error helpers conservatively retain dependencies and sensitivity from attempted subexpressions, including failed attempts. A conditional evaluates only its selected branch.

nonsensitive is an explicit declassification operation. Resource meta.sensitive = false and output policies do not clear inherited sensitivity; password-hash resource fields reassert their intrinsic sensitivity. RSA decryption marks its result sensitive initially, and explicit nonsensitive can remove that value annotation. SOPS-supplied variables are automatically sensitive. Bundles can contain plaintext values regardless of redaction.

Example

locals {
private = sensitive("example-value")
conversions = {
text = tostring(8080)
number = tonumber("8080")
boolean = tobool("true")
list = tolist(toset(["ops", "app"]))
map = tomap({ app = 1, web = "2" })
set = toset(["app", "app", "ops"])
object = convert({ port = "8080", extra = "discarded" }, object({ port = number, enabled = optional(bool, true) }))
fallback = try(tonumber("invalid"), 8080)
valid = can(tonumber("8080"))
marked = issensitive(local.private)
public = nonsensitive(local.private)
}
}
output "conversions" {
type = string
value = jsonencode(local.conversions)
}

All calls obey the function limits and annotation rules.