Built-in functions
Tales extends HCL2 with a small set of helper functions tuned for integration testing: looking up env vars, generating data, encoding payloads, signing requests, and pulling values out of text.
This page documents the helpers that are not matchers. For assertion matchers (contains, optional, is_string, etc.), see Matchers.
env(name, default?)
Section titled “env(name, default?)”Reads an environment variable.
config { base_url = env("BASE_URL", "http://localhost:1337") api_key = env("API_KEY") // no default, empty string if unset}| Argument | Type | Notes |
|---|---|---|
name | string | Env var name. |
default | any | Optional fallback when the var is unset or empty. Omit for "". |
Common patterns:
url = "${env("API_URL", "http://localhost:1337")}/users"debug = env("DEBUG", "0") == "1"generate(name)
Section titled “generate(name)”Calls a generator by name and returns its value. See Generators for declaration and types.
generate("user_email") // stringgenerate("demo_person").first_name // stringOutput is deterministic given (seed, scenario, step, generator name, expression path). Two calls in the same step return different values, the expression path differs. Capture or store in vars to reuse.
jsonencode(value)
Section titled “jsonencode(value)”Serialises any HCL value to a canonical JSON string. Object keys are sorted alphabetically; sets are sorted by encoded form; numbers preserve json.Number precision.
vars { body = jsonencode({ id = "evt-1" type = "ping" })}The deterministic key ordering matters for any signing scheme, the same payload always produces the same byte sequence, so HMAC and SHA-256 signatures stay stable across runs.
url_encode(s)
Section titled “url_encode(s)”URL-encodes a string (component-style encoding, suitable for query parameters or path segments).
url = "${config.base_url}/search?q=${url_encode("hello world")}"// → "hello%20world"now_unix()
Section titled “now_unix()”Returns the current Unix timestamp (seconds, integer) as observed at evaluation time. Non-deterministic by design.
vars { ts = now_unix() // 1716822000}now_rfc3339()
Section titled “now_rfc3339()”Same idea, RFC3339 UTC string format.
vars { created_at = now_rfc3339() // "2026-05-27T18:30:00Z"}Hash functions
Section titled “Hash functions”Seven sha*_hex variants are exposed, each hashing the UTF-8 bytes of its single string argument and returning the lowercase hex digest. Errors never embed the input.
| Function | Algorithm | Digest length (hex chars) |
|---|---|---|
sha1_hex | SHA-1 | 40 |
sha224_hex | SHA-224 | 56 |
sha256_hex | SHA-256 | 64 |
sha384_hex | SHA-384 | 96 |
sha512_hex | SHA-512 | 128 |
sha512_224_hex | SHA-512/224 | 56 |
sha512_256_hex | SHA-512/256 | 64 |
vars { digest = sha256_hex("hello") // digest = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}HMAC functions
Section titled “HMAC functions”The same seven variants are exposed as keyed HMACs. Each takes (secret, message) (both strings) and returns the lowercase hex digest. Secrets and messages are never embedded in error messages.
| Function | Algorithm |
|---|---|
hmac_sha1_hex | HMAC-SHA-1 |
hmac_sha224_hex | HMAC-SHA-224 |
hmac_sha256_hex | HMAC-SHA-256 |
hmac_sha384_hex | HMAC-SHA-384 |
hmac_sha512_hex | HMAC-SHA-512 |
hmac_sha512_224_hex | HMAC-SHA-512/224 |
hmac_sha512_256_hex | HMAC-SHA-512/256 |
vars { sig = hmac_sha256_hex(config.webhook_secret, "${vars.ts}.${vars.body}")}
request { headers = { X-Signature = "t=${vars.ts},v1=${vars.sig}" } body { raw = vars.body }}hmac_sha256_hex is the recommended default for new signing code. hmac_sha1_hex is kept available because RFC 6238 TOTP and a few legacy signing schemes still mandate it. Pair any HMAC with jsonencode for a stable canonical signed payload, see the Signing webhooks guide for a full recipe.
base64url_encode(value)
Section titled “base64url_encode(value)”Encodes the UTF-8 bytes of the input string using the RFC 4648 URL-safe alphabet (- and _ instead of + and /) without padding.
vars { segment = base64url_encode("hello") // segment = "aGVsbG8"}pkce_challenge(verifier, options?)
Section titled “pkce_challenge(verifier, options?)”Derives an RFC 7636 PKCE code_challenge from a code_verifier.
vars { verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" challenge = pkce_challenge(vars.verifier) // challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"}pkce_challenge(verifier) defaults to the S256 method. Supply an options object to change behaviour:
| Option | Type | Default | Notes |
|---|---|---|---|
method | string | "S256" | Either "S256" or "plain". Unknown methods error. |
For S256, the function computes base64url(sha256(ASCII(verifier))), encoding the raw 32 hash bytes, not the hex string. plain returns the verifier unchanged. Unknown option keys and unsupported methods fail explicitly without embedding the verifier.
The code_verifier JSON field name is treated as sensitive by Tales’ report masking; bodies that send it under that key will not leak the value into JSONL / HTML / console reports.
totp(secret_base32, options?)
Section titled “totp(secret_base32, options?)”Computes an RFC 6238 TOTP code from a Base32-encoded secret.
vars { ts = now_unix() code = totp(config.mfa_secret, { period = 30 digits = 6 algorithm = "SHA1" timestamp = vars.ts })}totp(secret) without options is equivalent to passing every option at its default value:
| Option | Type | Default | Notes |
|---|---|---|---|
period | number | 30 | Window size in seconds; must be > 0. |
digits | number | 6 | Code length; must be > 0 and <= 10. |
algorithm | string | "SHA1" | Only SHA1 is supported in V1; other values raise an explicit error. |
timestamp | number | now_unix() | Unix seconds; must be >= 0. |
The default timestamp reads the wall clock, so two calls inside the same expression may straddle a window boundary. Capture now_unix() into a step-local vars.ts and pass it as timestamp whenever stability matters, exactly as in the example above.
The secret is Base32; the function normalizes user-friendly variants (lower/upper case, spaces, hyphens, missing padding). Invalid secrets fail with an opaque invalid TOTP base32 secret message, the raw input is never echoed.
regex_find(value, pattern, group?)
Section titled “regex_find(value, pattern, group?)”Extracts the first regex match from a string. With group = 0 (default) returns the full match; with group = N returns the N-th capture group.
capture { // body contains "verification code is A1B2C3" code = regex_find(response.body, "verification code is ([A-Z0-9]{6})", 1)}| Argument | Type | Notes |
|---|---|---|
value | string | Subject string to search. |
pattern | string | Go regexp syntax (RE2). Compiled at evaluation time. |
group | number | Optional capture group index (default 0, the full match). |
Returns an error if the pattern doesn’t match the value. Wrap in can(...) (see Matchers) if you want a soft probe.
Standard HCL functions
Section titled “Standard HCL functions”In addition to the helpers above, Tales exposes the standard HCL2 function set: format, formatlist, join, split, lower, upper, trim, length, concat, merge, keys, values, range, tonumber, tostring, jsondecode, coalesce, try, and more. See the Terraform language docs for a comprehensive reference, they share the same go-cty function library.