Tera Function & Filter Reference
Custom functions and filters added by rescile to the standard Tera templating engine.
rescile extends Tera with custom logic for infrastructure modeling. These can be used in any templated string
value across models/, compliance/, and output/ files. For an overview of how templating fits into the
overall data context, see Templating & Data.
Functions
counter(key=value)
Returns a stateful, zero-based incrementing integer for a given unique key. Each unique key has its own
independent counter, reset on every importer run. This is more powerful than the built-in
origin_resource_counter variable when you need independent counters per group.
| Parameter | Description |
|---|---|
key |
A value uniquely identifying the counter. Can be a string, number, boolean, an object with a name property, or an array of such types (which will be sorted and joined). |
Example — allocate /24 subnets per region, restarting the index for each:
origin_resource = "department"
[[create_resource]]
resource_type = "subnet"
relation_type = "HAS_SUBNET"
[create_resource.properties]
# For departments in '10.0.0.0/16', counter goes 0, 1, 2...
# For departments in '10.1.0.0/16', counter independently goes 0, 1, 2...
subnet_cidr = "{{ origin_resource.region[0].cidr | cidr_nth_subnet(prefix=24, nth=counter(key=origin_resource.region)) }}"
calculate_cidr(ips=[...])
Calculates the smallest possible CIDR block that contains a given list of IP addresses.
| Parameter | Description |
|---|---|
ips |
An array of strings, each a valid IPv4 or IPv6 address. |
Example — summarize server IPs into a containing CIDR:
origin_resource = "application"
[[create_resource]]
resource_type = "network_summary"
name = "summary_for_{{ origin_resource.name }}"
[create_resource.properties]
# map(attribute='ip_address') extracts each server's IP, then calculate_cidr wraps them.
# For ips=["10.1.5.10", "10.1.5.25"] → "10.1.5.0/27"
summary_cidr = "{{ calculate_cidr(ips=origin_resource.server | map(attribute='ip_address')) }}"
allocate_subnets(cidr="string", host_map={...})
Dynamically plans a subnet layout. Takes a parent CIDR block and a map of subnet names to required host counts. Calculates the smallest possible subnet for each entry and allocates them sequentially. Keys are sorted deterministically before allocation.
| Parameter | Description |
|---|---|
cidr |
The parent CIDR block from which to allocate. |
host_map |
A dictionary mapping subnet names to required host counts. |
Example — auto-plan a three-tier network layout:
origin_resource = "network"
required_subnets = { dmz = 10, app = 100, db = 50 }
[[create_resource]]
match_on = [{ property = "name", value = "core_network" }]
resource_type = "network_layout"
name = "layout_for_{{ origin_resource.name }}"
[create_resource.properties]
# Keys are sorted (app, db, dmz) before allocation for determinism.
# For cidr="10.0.0.0/16":
# → { "app": "10.0.0.0/25", "db": "10.0.0.128/26", "dmz": "10.0.0.192/28" }
allocated_cidrs = "{{ allocate_subnets(cidr=origin_resource.cidr, host_map=required_subnets) }}"
The result is a JSON object that can be used in subsequent models to create the actual subnet resources.
Filters
cidr_nth_subnet(prefix=int, nth=int)
Extracts a specific zero-indexed subnet from a parent CIDR block. The prefix parameter is the
absolute prefix length of the desired subnet, not the number of bits to add.
| Parameter | Description |
|---|---|
prefix |
Absolute target prefix length (e.g., 24 for /24 subnets). Must be larger than or equal to the parent CIDR’s prefix. |
nth |
Zero-based index of the subnet to select. |
Example — allocate ordered /24 subnets from a /16 block:
origin_resource = "department"
[[create_resource]]
resource_type = "subnet"
relation_type = "HAS_SUBNET"
[create_resource.properties]
# origin_resource_counter is 0 for the first department, 1 for the second, etc.
# Result: "10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24", ...
subnet_cidr = "{{ '10.0.0.0/16' | cidr_nth_subnet(prefix=24, nth=origin_resource_counter) }}"
cidr_split_n(n=int)
Splits a CIDR block into n equal-sized subnets. The count is rounded up to the next power of two.
Returns an array of CIDR strings.
| Parameter | Description |
|---|---|
n |
Desired number of subnets. Rounded up to next power of two. |
Example — divide a /16 into 4 equal /18 subnets:
origin_resource = "vpc"
[[create_resource]]
resource_type = "network_layout"
name = "layout_for_{{ origin_resource.name }}"
[create_resource.properties]
# For origin_resource.cidr = "10.10.0.0/16":
# → ["10.10.0.0/18", "10.10.64.0/18", "10.10.128.0/18", "10.10.192.0/18"]
regional_subnets = "{{ origin_resource.cidr | cidr_split_n(n=4) }}"
jmespath(query="string")
Executes a JMESPath query against a JSON-compatible value (object, array, or string). Returns the query result. If no match is found, returns an empty value (falsy).
This is useful both for extracting nested data in templates and for filtering in match_on expressions.
See Matching on JSON with jmespath for the match_on usage pattern.
| Parameter | Description |
|---|---|
query |
A valid JMESPath expression string. |
Example — extract a nested property:
[create_resource.properties]
# origin_resource.config = { "storage": { "class": "premium-ssd" } }
storage_class = "{{ origin_resource.config | jmespath(query='storage.class') }}"
# → "premium-ssd"
Example — pre-filter JSON data in the file header:
# Load SLAs and select only the first critical-level policy before the loop starts.
critical_sla = { "json!" = "slas.json", jmespath = "policies[?level == 'critical'] | [0]" }
[[create_resource]]
[create_resource.properties]
backup_frequency = "{{ critical_sla.backup_frequency }}"
Example — match_on expression with jmespath:
[[create_resource]]
match_on = [
{
expression = """
{%- set tags = origin_resource.config
| jmespath(query="tags[?key == 'env' && value == 'prod']") -%}
{% if tags %}true{% endif %}
"""
}
]
resource_type = "backup_policy"
name = "backup_policy_for_{{ origin_resource.name }}"
select(from="string", match="key=value")
Filters an array of objects by a condition and extracts a specific property from each matching object.
Can also operate on a single object, returning the property value if the condition matches or null
otherwise.
| Parameter | Description |
|---|---|
from |
Property name to extract from matching objects. Supports dot-notation (e.g., "config.name"). |
match |
Condition in "key.path=value" format. Dot-notation supported. If the target property is an array, the filter checks whether it contains the value. |
Example — extract names of active servers:
[create_resource.properties]
# origin_resource.server = [{name: "srv1", status: "active"}, {name: "srv2", status: "inactive"}]
# select extracts 'name' where status=active → ["srv1"]
# join converts the array → "srv1"
active_servers = "{{ origin_resource.server | select(from='name', match='status=active') | join(sep=',') }}"
Example — single object:
# origin_resource.primary_db = { name: "db1", role: "primary" }
primary_db_name = "{{ origin_resource.primary_db | select(from='name', match='role=primary') }}"
# → "db1"
intersect(with=[...])
Returns a new array containing only elements present in both the input array and the with array.
Duplicates are removed from the result.
| Parameter | Description |
|---|---|
with |
The second array to intersect with. |
Example — find common tags:
[create_resource.properties]
# application.tags = ["frontend", "web", "prod"]
# server.tags = ["web", "api", "prod"]
# → ["web", "prod"]
common_tags = "{{ origin_resource.tags | intersect(with=origin_resource.server[0].tags) }}"
regexp(expr="s/pattern/replacement/flags")
Applies a sed-style regular-expression substitution to a string. The delimiter is the character
immediately following the leading s and can be any character (commonly / or |).
| Parameter | Description |
|---|---|
expr (alias: pattern) |
A sed substitution expression, e.g. s/foo/bar/gi. |
Flags:
| Flag | Meaning |
|---|---|
g |
Global — replace all occurrences, not just the first. |
i / I |
Case-insensitive matching. |
Replacement syntax:
| Token | Expands to |
|---|---|
\1–\9 |
Captured group N. |
& |
Entire match. |
\n \t \r |
Newline, tab, carriage return. |
\\ |
Literal backslash. |
\<delim> |
Literal delimiter character. |
Example — strip a domain suffix:
[create_resource.properties]
# "WebServer01.corp.example.com" → "webserver01"
short_name = "{{ origin_resource.fqdn | regexp(expr='s/\\.corp\\.example\\.com$//i') | lower }}"
Example — backreferences:
[create_resource.properties]
# "10.0.0.0/16" → "net_10.0.0.0_prefix16"
label = "{{ origin_resource.cidr | regexp(expr='s/([0-9.]+)\\/([0-9]+)/net_\\1_prefix\\2/') }}"
Example — custom delimiter to avoid escaping slashes:
[create_resource.properties]
# "a/b/c" → "a-b-c"
safe_path = "{{ origin_resource.path | regexp(expr='s|/|-|g') }}"
Tip: When your pattern or replacement contains the
/character, use an alternative delimiter (like|,#, or@) to keep the expression readable:s|old/path|new/path|ginstead ofs/old\/path/new\/path/g.
sha256
Computes the SHA-256 hash of a value. Strings are hashed directly; complex types (arrays, objects) are first serialized to JSON. Returns a lowercase hexadecimal string.
Example — create a unique, deterministic ID:
[create_resource.properties]
# Combine address and port into a stable hash to avoid name collisions.
unique_id = "{{ [origin_resource.address, origin_resource.port] | json_encode | sha256 }}"
base64_encode
Encodes a value using standard Base64. Strings are encoded directly; complex types are serialized to JSON first.
Example — encode a startup script for cloud-init:
[create_resource.properties]
user_data = "{{ origin_resource.startup_script | base64_encode }}"
hmac_sha256(key="secret", encoding="hex"|"base64")
Computes HMAC-SHA256 of a value using the given key. Returns a hex string by default; pass
encoding="base64" for Base64 output.
| Parameter | Description |
|---|---|
key |
The secret key for the HMAC computation. Supports {{ env.VAR }} expansion. |
encoding |
Output encoding: "hex" (default) or "base64". |
This filter is primarily used in proxy.toml for request signing, but is available everywhere.
Example — sign a payload for API authentication:
[create_resource.properties]
signature = "{{ origin_resource.payload | hmac_sha256(key=env.API_SECRET, encoding=\"base64\") }}"
Example — Exoscale API request signing in proxy.toml:
# app/proxy.toml
[[route]]
path = "/api/exoscale"
target = "https://api-ch-dk-2.exoscale.com/v2"
forward_headers = ["Accept", "User-Agent"]
[route.inject_headers]
"Content-Type" = "application/json"
"Authorization" = """
{%- set expires = request.timestamp + 600 -%}
{%- set msg = request.method ~ " " ~ request.path ~ "\n" ~ request.body ~ "\n" ~ request.query_values ~ "\n\n" ~ expires -%}
{%- set sig = msg | hmac_sha256(key=env.EXOSCALE_API_SECRET, encoding="base64") -%}
EXO2-HMAC-SHA256 credential={{ env.EXOSCALE_API_KEY }},expires={{ expires }},signature={{ sig -}}
"""
For the full proxy configuration reference, see proxy.toml Reference.
Standard Tera Features Used in rescile
In addition to the custom extensions above, rescile configurations make heavy use of standard Tera features. The full list is in the Tera documentation.
Commonly Used Built-in Filters
| Filter | Example | Description |
|---|---|---|
upper / lower |
{{ name | upper }} |
Change string case. |
replace(from, to) |
{{ name | replace(from=".", to="-") }} |
Replace substrings. |
default(value) |
{{ x | default(value="n/a") }} |
Fallback when value is undefined. |
split(pat) |
{{ v | split(pat=".") | first }} |
Split a string and pick an element. |
join(sep) |
{{ arr | join(sep=",") }} |
Join array elements into a string. |
map(attribute) |
{{ servers | map(attribute="ip") }} |
Extract one property from each object in an array. |
json_encode |
{{ obj | json_encode | safe }} |
Serialize to JSON string (use safe to avoid escaping). |
length |
{{ items | length }} |
Count items in an array or string. |
first / last |
{{ arr | first }} |
Pick first or last array element. |
Conditionals and Loops
[create_resource.properties]
# Inline conditional
sla = "{% if origin_resource.environment == 'prod' %}Gold{% else %}Silver{% endif %}"
# Loop to build a comma-separated list
tag_list = "{% for t in origin_resource.tags %}{{ t | upper }}{% if not loop.last %},{% endif %}{% endfor %}"
# Default filter to avoid undefined errors
region = "{{ origin_resource.region | default(value='us-east-1') }}"
Accessing Related Resources
Traversing relationships in templates uses dot notation. Multi-valued relations are arrays:
[create_resource.properties]
# First related database's version
db_version = "{{ origin_resource.database[0].version }}"
# Property on the connecting edge
connection_type = "{{ origin_resource.database[0]._relation.label }}"
# Collect all volumes from related applications
volumes = "{{ origin_resource.application | map(attribute='volume') | json_encode | safe }}"
Quick Reference
| Name | Type | Signature | Description |
|---|---|---|---|
counter |
Function | counter(key=value) |
Stateful per-key incrementing integer. |
calculate_cidr |
Function | calculate_cidr(ips=[...]) |
Smallest CIDR containing given IPs. |
allocate_subnets |
Function | allocate_subnets(cidr="...", host_map={...}) |
Dynamic subnet planning from host counts. |
cidr_nth_subnet |
Filter | | cidr_nth_subnet(prefix=int, nth=int) |
Extract the Nth subnet of a given prefix size. |
cidr_split_n |
Filter | | cidr_split_n(n=int) |
Split CIDR into N equal subnets. |
jmespath |
Filter | | jmespath(query="string") |
JMESPath query against a JSON structure. |
select |
Filter | | select(from="prop", match="k=v") |
Filter array and extract a property. |
regexp |
Filter | | regexp(expr="s/pat/repl/flags") |
Sed-style regex substitution. |
intersect |
Filter | | intersect(with=[...]) |
Common elements of two arrays. |
sha256 |
Filter | | sha256 |
SHA-256 hex hash. |
base64_encode |
Filter | | base64_encode |
Base64 encoding. |
hmac_sha256 |
Filter | | hmac_sha256(key="...", encoding="hex"|"base64") |
HMAC-SHA256 signature. |