Expression reference
Orchard uses HCL expressions with a cty type system. This page covers the reference namespaces, type expressions, and available functions.
Reference namespaces
Five reference namespaces are available inside expressions:
| Form | Scope | Available when |
|---|---|---|
var.<name> | Scenario (or component) variables. | Immediately — resolved up front. |
action.<type>.<name>.<output> | Action outputs. | After the referenced action completes. |
component.<type>.<name>.<output> | Component instance outputs. | After the component resolves. |
var.<name> (inside a component) | The component’s own variables. | Immediately, scoped to the component. |
self.<output> | The current action’s own outputs. | Inside lifecycle { teardown { } } blocks only. |
Variable references
provider "postgres" {
dsn = var.dsn
}
action "postgres_query" "insert" {
query = "INSERT INTO t (name) VALUES ('${var.name}')"
}Variable references are resolved at plan time. They cannot participate in the dependency graph — if you need ordering, reference an action or component output instead.
Action references
action "postgres_query" "next" {
query = "SELECT * FROM users WHERE id = ${action.postgres_query.previous.user_id}"
}
output "merchant_id" {
value = action.postgres_query.create_merchant.id
}action.<type>.<name>.<output> — all three segments are required. The
<output> segment depends on what the provider’s action schema declares.
Component references
action "postgres_query" "attach" {
query = "INSERT ... VALUES (${component.merchant.acme.id})"
}component.<type>.<name>.<output> — where <type> matches the component’s
declared name in its source file and <name> matches the instance label in
the scenario.
self (teardown only)
Inside a lifecycle { teardown { } } block, self.<field> refers to the
action’s own recorded outputs. This lets authors write teardowns without
repeating the node name:
action "postgres_query" "create_merchant" {
query = "INSERT INTO merchants (name) VALUES ('${var.name}') RETURNING id"
lifecycle {
teardown {
query = "DELETE FROM merchants WHERE id = ${self.id}"
}
}
}self is only populated when orchard teardown <record> evaluates the body;
the values come from the record’s per-node outputs captured at run time. Inside
the action’s main body (during orchard run), self is not in scope.
Type expressions
Types are written as cty type expressions:
| Expression | Meaning |
|---|---|
string | String. |
number | Number (integer or float). |
bool | Boolean. |
list(T) | List of T. |
set(T) | Set of T. |
map(T) | Map from string to T. |
object({ k = T, ... }) | Object with fixed keys. |
tuple([T1, T2, ...]) | Tuple with positional types. |
any | Any cty value (dynamic type). |
Examples:
variable "count" {
type = number
default = 1
}
variable "tags" {
type = list(string)
default = ["demo", "dev"]
}
variable "config" {
type = object({
retries = number
timeout = string
})
}Omitting type is equivalent to declaring type = any.
String interpolation
HCL supports ${ ... } interpolation inside strings:
query = "INSERT INTO merchants (name) VALUES ('${var.name}')"Quote carefully when interpolating into SQL, shell commands, or HTTP URLs — Orchard does not escape values for you.
Operators
Standard HCL operators are available:
- Arithmetic:
+,-,*,/,% - Comparison:
==,!=,<,<=,>,>= - Logical:
&&,||,! - Conditional:
cond ? a : b
variable "environment" { default = "dev" }
action "postgres_query" "seed" {
query = var.environment == "prod" ? "SELECT 1" : "INSERT INTO ..."
}Functions
See the functions reference for the complete list of all available functions with signatures, descriptions, parameter tables, and examples. The reference is auto-generated from Orchard’s function registry so it stays in sync with the binary.
Random values and seeds
Every random function (uuid, timerandom, timesequence, and the rest
of the random and identity functions) draws from the run’s seed. Pass
--seed <n> to orchard run or orchard plan, or pin one in the
scenario with seed = <n>; the flag wins. Without either, each run picks a
random seed, prints it as seed: <n>, and stores it in the run record, so
any run can be repeated:
orchard run scenarios/demo.hcl # seed: 1742…
orchard run scenarios/demo.hcl --seed 1742… # the same random values againEach attribute of each action, each variable default, each provider
setting and each output draws from its own stream, derived from the seed
and that place’s address. So the values one action gets don’t change when
you add or remove other actions, or run with --parallelism, and
orchard plan --show-values --seed <n> shows the values the run with that
seed uses.
A seed fixes the random part only. now(), env(), and the timestamp in
time-ordered IDs such as ulid() still change between runs. And two runs
with the same seed generate the same uuid() keys, so a second run before
tearing down the first can hit unique constraints.
Reference syntax in depends_on
depends_on accepts references as bare traversals — not strings:
action "postgres_query" "b" {
depends_on = [action.postgres_query.a, component.merchant.acme]
query = "..."
}These references don’t pull any data into scope; they only create graph edges.