Bluelink
Blueprints

Spec Core Functions

The core functions that should be supported by all implementations of the blueprint specification

v2025-11-02

This document contains the definitions of the core ${..} substitution utility functions that should be supported by all implementations of the spec.

fromjson

The fromjson function is used to extract values from a serialised json string. Implementations should use json pointer notation to allow for the extraction of values from complex serialised structures. This only works for extracting values when the root of the json string is an object.

Parameters:

  1. string - A valid string literal, reference or function call yielding the json string to extract values from.
  2. string - A valid json pointer expression to extract the value from the json string.

Returns:

any

The value extracted from the json string. This could be a primitive value, an array, or a mapping.

Examples:

With a reference:

${fromjson(variables.cacheClusterConfig, "/host")}

With a function call:

${fromjson(trim(variables.cacheClusterConfig), "/host")}

With a string literal:

${fromjson("{\"host\":\"localhost\"}", "/host")}

fromjson_g

The fromjson_g is a composable version of the fromjson function that takes a json pointer expression as a static argument and returns a function that takes the json string to extract values from. Like with fromjson, implementations should use json pointer notation to allow for the extraction of values from complex serialised structures.

Parameters:

  1. string - A valid json pointer expression to extract the value from the json string.

Returns:

func (string) -> any

A function that takes a json string and extracts a value from it using the pre-configured json pointer expression.

Examples:

${map(values.cacheClusterConfigDefs, fromjson_g("/host"))}

jsondecode

The jsondecode function is used to decode a serialised json string into a primitive value, array or mapping.

Parameters:

  1. string - A valid string literal, reference or function call yielding the json string to decode.

Returns:

any

The decoded json string. This could be a primitive value, an array, or a mapping.

Example:

${jsondecode(values.cacheClusterConfig)}

len

The len function is used to get the length of a string, array, or mapping.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string, array, or mapping to get the length of.

Returns:

integer

The length of the string, array, or mapping. For a string, the length is the number of characters. For a mapping, the length is the number of key value pairs.

Example:

${len(values.cacheClusterConfig.endpoints)}

substr

The substr function is used to get a substring from a string.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to get the substring from.
  2. integer - The index of the first character to include in the substring.
  3. integer (optional) - The index of the last character to include in the substring. If not provided the substring will include all characters from the start index to the end of the string.

Returns:

string

The substring from the string.

Example:

${substr(values.cacheClusterConfig.host, 0, 3)}

substr_g

substr_g is a composable version of the substr function that takes the start and end indexes as static arguments and returns a function that takes the string to get the substring from.

Parameters:

  1. integer - The index of the first character to include in the substring.
  2. integer (optional) - The index of the last character to include in the substring. If not provided the substring will include all characters from the start index to the end of the string.

Returns:

func (string) -> string

A function that takes a string and returns the substring from the string using the pre-configured start and end positions.

Example:

${map(values.cacheClusterConfig.hosts, substr_g(0, 3))}

replace

The replace function is used to replace all occurrences of a substring in a string with another substring.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing an input string that contains a substring that needs replacing.
  2. string - The "search" substring to replace.
  3. string - The substring to replace the "search" substring with.

Returns:

string

The input string with all occurrences of the "search" substring replaced with the "replace" substring.

Example:

${replace(values.cacheClusterConfig.host, "http://", "https://")}

replace_g

replace_g is a composable version of the replace function that takes the search and replace substrings as static arguments and returns a function that takes the string to replace the substrings in.

Parameters:

  1. string - The "search" substring to replace.
  2. string - The substring to replace the "search" substring with.

Returns:

func (string) -> string

The input string with all occurrences of the "search" substring replaced with the "replace" substring.

Example:

${map(values.cacheClusterConfig.hosts, replace_g("http://", "https://"))}

trim

The trim function is used to remove all leading and trailing whitespace from a string.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to trim.

Returns:

The input string with all leading and trailing whitespace removed.

Example:

${trim(values.cacheClusterConfig.host)}

Handling none Values: If the input is none, returns none (propagates). This applies to all string manipulation functions.

trimprefix

The trimprefix function is used to remove a prefix from a string.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to remove the prefix from.
  2. string - The prefix to remove from the string.

Returns:

The input string with the prefix removed.

Example:

${trimprefix(values.cacheClusterConfig.host, "http://")}

trimprefix_g

trimprefix_g is a composable version of the trimprefix function that takes the prefix as a static argument and returns a function that takes the string to remove the prefix from.

Parameters:

  1. string - The prefix to remove from the string.

Returns:

func (string) -> string

A function that takes a string and returns the input string with the prefix removed.

Example:

${map(variables,cacheClusterConfig.hosts, trimprefix_g("http://"))}

trimsuffix

The trimsuffix function is used to remove a suffix from a string.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to remove the suffix from.
  2. string - The suffix to remove from the string.

Returns:

The input string with the suffix removed.

Example:

${trimsuffix(values.cacheClusterConfig.host, ":3000")}

trimsuffix_g

trimsuffix_g is a composable version of the trimsuffix function that takes the suffix as a static argument and returns a function that takes the string to remove the suffix from.

Parameters:

  1. string - The suffix to remove from the string.

Returns:

func (string) -> string

A function that takes a string and returns the input string with the suffix removed.

Example:

${map(values.cacheClusterConfig.hosts, trimsuffix_g("/config"))}

split

The split function is used to split a string into an array of substrings based on a delimiter.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to split.
  2. string - The delimiter to split the string by.

Returns:

array

An array of substrings that have been split by the delimiter.

Example:

${split(values.cacheClusterConfig.hosts, ",")}

split_g

split_g is a function that takes the delimiter as a static argument and returns a function that takes the string to split.

Parameters:

  1. string - The delimiter to split the string by.

Returns:

func (string) -> array

A function that takes a string and returns an array of substrings that have been split by the delimiter.

Example:

${flatmap(values.cacheClusterConfig.multiClusterHosts, split_g(","))}

join

The join function is used to join an array of strings into a single string using a delimiter.

Parameters:

  1. array - A reference or function call yielding a return value representing an array of strings to join together.
  2. string - The delimiter to join the strings with.

Returns:

string

A single string that is the result of joining the array of strings with the delimiter.

Example:

${join(values.cacheClusterConfig.hosts, ",")}

Type Coercion:

Non-string array elements are automatically converted to strings before joining:

# Integers are converted to strings
ports: ${join([8080, 8081, 8082], ",")}
# Result: "8080,8081,8082"

# Booleans are converted to strings
flags: ${join([true, false, true], "|")}
# Result: "true|false|true"

Handling none Values:

  • If the array itself is none, the result is none
  • Array elements that are none are filtered out before joining
# Elements with none are skipped
tags: ${join(["tag1", none, "tag2", none, "tag3"], ",")}
# Result: "tag1,tag2,tag3"

index

The index function is used to get the first index of a substring in a given string.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to search for the substring in.
  2. string - The substring to search for in the string.

Returns:

integer

The index of the first occurrence of the substring in the string. This will be -1 if the substring is not found in the string.

Example:

${index(values.cacheClusterConfig.host, ":3000")}

last_index

The last_index function is used to get the last index of a substring in a given string.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to search for the substring in.
  2. string - The substring to search for in the string.

Returns:

integer

The index of the last occurrence of the substring in the string. This will be -1 if the substring is not found in the string.

Example:

${last_index(values.cacheClusterConfig.host, ":3000")}

to_upper

The to_upper function converts all characters of a string to upper case.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to convert to upper case.

Returns:

string

The input string with all characters converted to upper case.

Example:

${to_upper(values.cacheClusterConfig.hostName)}

to_lower

The to_lower function converts all characters of a string to lower case.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to convert to lower case.

Returns:

string

The input string with all characters converted to lower case.

Example:

${to_lower(values.cacheClusterConfig.hostId)}

has_prefix

The has_prefix function is used to check if a string starts with a given substring.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to check.
  2. string - The prefix to check for at the start of the string.

Returns:

bool

True, if the string starts with the prefix, false otherwise.

Example:

${has_prefix(values.cacheClusterConfig.host, "http://")}

has_prefix_g

has_prefix_g is a composable version of the has_prefix function that takes the prefix as a static argument and returns a function that takes the string to check.

Parameters:

  1. string - The prefix to check for at the start of the string.

Returns:

func (string) -> bool

A function that takes a string and returns true if the string starts with the prefix, false otherwise.

Example:

${filter(
  values.cacheClusterConfig.hosts,
  has_prefix_g("http://")
)}

has_suffix

The has_suffix function is used to check if a string ends with a given substring.

Parameters:

  1. string - A valid string literal, reference or function call yielding a return value representing the string to check.
  2. string - The suffix to check for at the end of the string.

Returns:

bool

True, if the string ends with the suffix, false otherwise.

Example:

${has_suffix(values.cacheClusterConfig.host, "/config")}

has_suffix_g

has_suffix_g is a composable version of the has_suffix function that takes the suffix as a static argument and returns a function that takes the string to check.

Parameters:

  1. string - The suffix to check for at the end of the string.

Returns:

func (string) -> bool

A function that takes a string and returns true if the string ends with the suffix, false otherwise.

Example:

${filter(
  values.cacheClusterConfig.hosts,
  has_suffix_g("/config")
)}

contains

The contains function is used to check if a string contains a given substring or if an array contains a given value.

Parameters:

  1. string | array - A valid string literal, reference or function call yielding a return value representing a string or array to search.
  2. string | ArrayElem - The substring or value to search for in the string or array.

Returns:

bool

True, if the substring or value is found in the string or array, false otherwise.

Example:

${contains(values.cacheClusterConfig.host, "bluelink.dev")}

contains_g

contains_g is a composable version of the contains function that takes the substring or value as a static argument and returns a function that takes the string or array to check.

Parameters:

  1. string | ArrayElem - The substring or value to search for in the string or array.

Returns:

func (string | array) -> bool

A function that takes a string or array and returns true if the substring or value is found in the string or array, false otherwise.

Example:

${filter(
  values.cacheClusterConfig.hosts,
  contains_g("bluelink.dev")
)}

list

Creates a list of values from arguments of the same type.

Parameters:

N arguments of the same type that will be used to create a list.

Returns:

array

An array of values that have been passed as arguments.

Example:

${list(
  "item1",
  "item2",
  "item3",
  "item4"
)}

object

Creates an object from named arguments.

Parameters:

N named arguments that will be used to create an object/mapping. When no arguments are passed, an empty object should be returned.

Caution

Named function arguments are a special case that are only used in the object function. It is not syntactically invalid to use named arguments in other functions but the names will be ignored and the values passed in will be treated as positional arguments.

Returns:

object

An object containing attributes that have been passed as named arguments.

Example:

${object(
  id = "subnet-1234",
  label = "Subnet 1234"
)}

keys

Produces an array of keys from a mapping or attribute names from an object.

Parameters:

  1. mapping - A mapping to extract keys from. (This can also be an object with known attributes, the two are interchangeable up to the point of validating parameters and return values)

Returns:

array

An array of keys or attributes from the mapping or object.

Example:

${keys(datasources.network.subnets)}

vals

Produces an array of values from a mapping or object with known attributes.

This function is named vals to avoid conflicting with the values keyword used for blueprint values.

Parameters:

  1. mapping - A mapping or object to extract values from.

Returns:

array

An array of values extracted from the provided mapping or object.

Example:

${vals(datasources.network.subnets)}

map

Maps a list of values to a new list of values using a function.

Parameters:

  1. array - An array of items where all items are of the same type to map.
  2. func<Item, NewItem>(Item, integer?) -> NewItem - A function that will be applied to each item in the array, the function must match the type of the items in the array. A runtime error should be returned if the function does not match this type. This function can optionally take an index as a second argument.

Returns:

array

An array of values that have been transformed by the function.

Example:

${map(
  datasources.network.subnets,
  compose(to_upper, getattr("id"))
)}

Handling none Values:

  • If the input array is none, the result is none
  • If the mapping function returns none for an element, that element is filtered out from the result
# Elements that map to none are removed from output
result: ${map(items, if_g(shouldInclude, transform, none))}

filter

Filters a list of values based on a predicate function.

Parameters:

  1. array - An array of items where all items are of the same type to filter.
  2. func<Item>(Item) -> bool - A function that will be applied to each item in the array, the function must return a boolean value.

Returns:

array

An array of filtered values based on the predicate function.

Example:

${filter(
  datasources.network.subnets,
  has_prefix_g("subnet-402948-")
)}

reduce

Reduces a list of values to a single value using a function. There are no core functions that have the signature of a reducer function, the reduce function is in the core spec to provide the tools for implementations and end users to be able to apply more complex transformations of data in blueprints.

Parameters:

  1. array - An array of items where all items are of the same type to reduce over.
  2. func<Item, Accum>(Accum, Item, integer?) -> Accum - A function that will be applied to each item in the array, accumulating a single value. This function can optionally take an index as a third argument.
  3. Accum - The initial value to start the accumulation with.

Returns:

any

The accumulated value as a result of applying the reducer function to each item in the array.

Examples:

Summing array values (requires implementation-provided or custom reducer function):

# Sum up all memory allocations across instances
totalMemory: |
  ${reduce(
    datasources.instances.items,
    sum_memory_reducer,
    0
  )}

Building a map from an array (requires implementation-provided or custom reducer function):

# Create a map of instance IDs to instance names
instanceMap: |
  ${reduce(
    datasources.instances.items,
    build_id_to_name_map,
    object()
  )}

Aggregating configuration from multiple sources:

# Combine all tags from multiple resources into a single tag set
allTags: |
  ${reduce(
    list(
      datasources.vpc.tags,
      datasources.subnet.tags,
      variables.customTags
    ),
    merge_tags_reducer,
    object()
  )}

Note: The reduce function requires custom or implementation-provided reducer functions that match the signature func<Item, Accum>(Accum, Item, integer?) -> Accum. The core specification does not include built-in reducer functions, allowing implementations and users to define domain-specific reducers as needed.

sort

Sorts a list of values based on a comparison function. There are no core functions that have the signature of a comparison function, the sort function is in the core spec to provide the tools for implementations and end users to sort arrays of values based on a custom comparison function.

Parameters:

  1. array - An array of items where all items are of the same type to sort.
  2. func<Item>(Item, Item) -> integer - The comparison function that is expected to return a negative integer if the first item should come before the second item, a positive integer if the first item should come after the second item, and zero if the items are equal.

Returns:

array

An array of sorted values based on the comparison function.

Examples:

Sorting instances by size (requires implementation-provided or custom comparison function):

# Sort instances in ascending order by size
sortedInstances: |
  ${sort(
    datasources.instances.items,
    compare_instance_size
  )}

Sorting availability zones (requires implementation-provided or custom comparison function):

# Sort availability zones alphabetically
sortedZones: |
  ${sort(
    datasources.region.availabilityZones,
    compare_strings_alphabetically
  )}

Sorting resources by priority (requires implementation-provided or custom comparison function):

# Sort resources by priority field in descending order
prioritizedResources: |
  ${sort(
    datasources.resources.items,
    compare_priority_desc
  )}

Note: The sort function requires custom or implementation-provided comparison functions that match the signature func<Item>(Item, Item) -> integer. The comparison function should return:

  • Negative integer (e.g., -1) if first item should come before second item
  • Positive integer (e.g., 1) if first item should come after second item
  • Zero (0) if items are equal in sort order

The core specification does not include built-in comparison functions, allowing implementations and users to define domain-specific comparators as needed.

flatmap

Maps a list of values to a new list of values using a function and flattens the result.

One example where this is useful would be where you have a list of strings where each string is a comma separated list of values and you want to split each string into a list of values and then flatten the result into a single output list.

To illustrate this, for input:

values.hosts = ["host1,example.com:3049", "host2,example.com:4095"]

Applying the flat map function like so:

${flatmap(
  values.hosts,
  split_g(",")
)}

Would result in the following output:

["host1", "example.com:3049", "host2", "example.com:4095"]

Parameters:

  1. array - An array of items where all items are of the same type to map.
  2. func<Item, NewItem extends array>(Item, integer?) -> NewItem - A function that will be applied to each item in the array, the function must match the type of the items in the array. A runtime error should be returned if the function does not match this type. This function can optionally take an index as a second argument.

Returns:

array

An array of values that have been transformed by the function and flattened into a one-dimensional array.

Example:

${flatmap(
  values.cacheClusterConfig.hosts,
  split_g(",")
)}

compose

compose is a higher-order function that combines N functions into a single function, where the output of one function is passed in as the input to the previous function. The call order of the functions is from right to left.

Parameters:

N functions that will be composed together from right to left. The return type of each function must match the input type of the function to the left.

Returns:

func (any) -> any

A function that takes the input value of the right-most function and returns the output value of the left-most function.

Example:

${map(
  datasources.network.subnets,
  compose(to_upper, getattr("id"))
)}

pipe

pipe is a higher-order function that combines N functions into a single function, where the output of one function is passed in as the input to the next function. The call order of the functions is from left to right, which is generally seen as more intuitive than the right to left order of compose.

Parameters:

N functions that will be piped together from left to right. The return type of each function must match the input type of the function to the right.

Returns:

func (any) -> any

A function that takes the input value of the left-most function and returns the output value of the right-most function.

Example:

${map(
  datasources.network.subnets,
  pipe(getattr("id"), to_upper)
)}

getattr

A higher-order function that returns a function that extracts a named attribute from an object or a mapping. This is useful in situations where you want to map an array of objects to an array of values of a specific attribute such as IDs.

Parameters:

  1. string - The name of the attribute to extract from the object or mapping.

Returns:

func (object | mapping) -> any

A function that takes an object or mapping and returns the value of the named attribute.

Example:

${map(
  datasources.network.subnets,
  compose(getattr("id"), getattr("definition"))
)}

This example would take a list of subnets that would be of the following form:

[{
  "definition": {
    "id": "subnet-1234"
  }
}, {
  "definition": {
    "id": "subnet-5678"
  }
}]

And return a list of ids:

["subnet-1234", "subnet-5678"]

getelem

A higher-order function that returns a function that extracts an element from an array. This is useful in situations where you want to map a two-dimensional array to an array of values of a specific element.

Parameters:

  1. integer - The position of the element to extract from the array.

Returns:

func (array) -> any

A function that takes an array and returns the value at the provided position.

Example:

${map(
  datasources.network.subnets,
  compose(getattr("id"), getelem(0))
)}

This example would take a list of subnets that would be of the following form:

[[
  {
    "id": "subnet-1234",
    "label": "Subnet 1234"
  },
  "10.0.0.0/16"
], [
  {
    "id": "subnet-5678",
    "label": "Subnet 5678"
  },
  "172.31.0.0/16"
]]

And return a list of ids:

["subnet-1234", "subnet-5678"]

A function to retrieve the state of a link between two resources.

Parameters:

  1. string | ResourceRef - Resource A in the relationship, can be a string literal of the resource name or a reference to a resource.
  2. string | ResourceRef - Resource B in the relationship, can be a string literal of the resource name or a reference to a resource.

Returns:

object

An object containing all the information about the link between the two resources made available by the provider that powers the link.

Example:

Using resource references:

${link(resources.orderApi, resources.createOrderFunction)}

Using implict resource references (identifiers without a namespace are either resources or functions):

${link(orderApi, listOrdersFunction)}

Using string literals:

${link("orderApi", "deleteOrderFunction")}

and

A function that acts as a logical AND operator on two boolean values.

Parameters:

  1. boolean - The result of boolean expression A, the left-hand side of the AND operation.
  2. boolean - The result of boolean expression B, the right-hand side of the AND operation.

Returns:

boolean

The result of the logical AND operation on the two boolean values.

Example:

${and(resources.orderApi.spec.isProd, eq(variables.environment, "prod"))}

Handling none Values:

When any argument to and is none:

  • none is treated as falsy
  • If any argument is none, the result is none (propagates the absence)
# If isDev is none, entire condition evaluates to none (falsy)
shouldDeploy: ${and(isDev, eq(variables.region, "us-west-2"))}

or

A function that acts as a logical OR operator on two boolean values.

Parameters:

  1. boolean - The result of boolean expression A the left-hand side of the OR operation.
  2. boolean - The result of boolean expression B the right-hand side of the OR operation.

Returns:

boolean

The result of the logical OR operation on the two boolean values.

Example:

${or(resources.orderApi.spec.isDev, eq(variables.environment, "dev"))}

not

A function that negates a given boolean value.

Parameters:

  1. boolean - The result of a boolean expression to negate.

Returns:

boolean

The result of negating the provided boolean value.

Example:

${not(eq(variables.environment, "prod"))}

eq

A function that determines whether two values of the same type are equal.

Parameters:

  1. any - The left-hand side of the equality comparison.
  2. any - The right-hand side of the equality comparison.

Returns:

boolean

True, if the two values are equal, false otherwise.

Example:

${eq(variables.environment, "prod")}

gt

A function that determines whether a number is greater than another number.

Parameters:

  1. integer | float - "a" in the expression "a > b".
  2. integer | float - "b" in the expression "a > b".

Returns:

boolean

True, if the first number is greater than the second number, false otherwise.

Example:

${gt(len(datasources.network.subnets), 10)}

ge

A function that determines whether a number is greater than or equal to another number.

Parameters:

  1. integer | float - "a" in the expression a >= b.
  2. integer | float - "b" in the expression a >= b.

Returns:

boolean

True, if the first number is greater than or equal to the second number, false otherwise.

Example:

${ge(len(datasources.network.subnets), 10)}

lt

A function that determines whether a number is less than another number.

Parameters:

  1. integer | float - "a" in the expression a < b.
  2. integer | float - "b" in the expression a < b.

Returns:

boolean

True, if the first number is less than the second number, false otherwise.

Example:

${lt(len(datasources.network.subnets), 10)}

le

A function that determines whether a number is less than or equal to another number.

Parameters:

  1. integer | float - "a" in the expression a <= b.
  2. integer | float - "b" in the expression a <= b.

Returns:

boolean

True, if the first number is less than or equal to the second number, false otherwise.

Example:

${le(len(datasources.network.subnets), 10)}

if

A function that returns one of two values based on a boolean condition.

Parameters:

  1. boolean - A boolean expression that determines which value to return.
  2. any - The value to return if the condition is true.
  3. any - The value to return if the condition is false.

Returns:

any

The value corresponding to the boolean condition result.

Examples:

Simple conditional:

${if(eq(variables.environment, "production"), "us-east-1", "us-west-2")}

Nested conditionals:

${if(
  eq(variables.environment, "production"), 
  "large",
  if(eq(variables.environment, "staging"), "medium", "small")
)}

Complex object selection:

${if(
  eq(variables.environment, "production"),
  object(size="large", replicas=3, region="us-east-1"),
  object(size="small", replicas=1, region="us-west-2")
)}

Compositional Approach for Complex Conditionals:

For complex conditionals that would be difficult to read in a single expression, especially in JSON format, break them into smaller, composable pieces:

values:
  # Break down complex conditions into named boolean values
  isProduction:
    type: boolean
    value: ${and(eq(variables.environment, "production"), eq(variables.region, "us-east-1"))}
  
  isStaging:
    type: boolean
    value: ${eq(variables.environment, "staging")}
  
  # Define configuration objects separately
  productionConfig:
    type: object
    value: |
      ${object(
        size="large",
        replicas=3,
        region="us-east-1",
        backupEnabled=true,
        monitoringLevel="detailed"
      )}
  
  stagingConfig:
    type: object
    value: |
      ${object(
        size="medium",
        replicas=2,
        region="us-west-2",
        backupEnabled=true,
        monitoringLevel="standard"
      )}
  
  developmentConfig:
    type: object
    value: |
      ${object(
        size="small",
        replicas=1,
        region="us-west-1",
        backupEnabled=false,
        monitoringLevel="basic"
      )}
  
  # Compose the final conditional using the helper values
  deploymentConfig:
    type: object
    value: |
      ${if(
        values.isProduction,
        values.productionConfig,
        if(values.isStaging, values.stagingConfig, values.developmentConfig)
      )}

Truthiness Rules:

The if function accepts only boolean or none values for the condition parameter:

  • true - Evaluates to true, returns the true branch
  • false - Evaluates to false, returns the false branch
  • none - Treated as falsy, returns the false branch

Note: Unlike some programming languages, the if function does NOT support loose truthiness. Values like 0, "" (empty string), [] (empty array), or other "empty" values are NOT automatically converted to boolean. You must use explicit comparison functions like eq(), not(), etc., to produce boolean values.

Handling none Values:

The if function has special behavior with none values:

  1. none in the condition: Treated as falsy, returns the false branch
  2. none in branches: Can return none to conditionally omit fields

Condition is none:

# If customCondition is none → evaluates to false → returns "default"
result: ${if(customCondition, "custom", "default")}

Returning none for conditional field omission:

resources:
  instance:
    type: aws/ec2/instance
    spec:
      instanceType: t3.micro
      # monitoring field only present in production
      monitoring: ${if(eq(variables.env, "prod"), true, none)}

When variables.env is not "prod", the monitoring field is completely omitted from the materialised blueprint.

Chaining with coalesce() for complex defaults:

# If override exists use it, otherwise conditional default, with final fallback
timeout: |
  ${coalesce(
    variables.timeoutOverride,
    if(eq(variables.env, "prod"), 60, 30)
  )}

first

A function that returns the first non-empty value from a list of arguments.

Empty values (which are skipped by first):

  • none - Explicit absence of a value
  • "" - Empty string
  • [] - Empty array

Parameters:

N arguments of any type that will be evaluated in order to find the first non-empty value.

Returns:

any

The first non-empty value from the provided arguments. If all arguments are empty, the function will return the last argument (which will be empty).

Examples:

Basic usage with fallback values:

${first(variables.customHost, variables.defaultHost, "localhost")}

With data source fallbacks:

${first(
  datasources.primaryVpc.vpcId,
  datasources.fallbackVpc.vpcId,
  "vpc-default"
)}

Environment-specific defaults:

${first(
  variables.databaseHost,
  if(eq(variables.environment, "production"), "prod-db.example.com", "dev-db.example.com")
)}

Configuration with sensible defaults:

${first(variables.logLevel, "info")}

Handling none Values:

The first function treats none as an empty value and skips it when searching for the first non-empty value, just like it skips "" and [].

Examples demonstrating empty value skipping:

# All three empty values are skipped - returns "value"
result: ${first(none, "", [], "value")}

# Skips none, returns empty string (it's first non-none/non-[])
result: ${first(none, [], "", "value")}

# Only none values, returns last argument (none)
result: ${first(none, none, none)}

# Mixed with lookups that may return none
apiEndpoint: |
  ${first(
    lookup(datasources.config.overrides, "apiEndpoint"),
    variables.customEndpoint,
    "https://api.default.com"
  )}

This makes first particularly useful for fallback chains where earlier sources might not exist:

# Try multiple config sources, use first available non-empty value
timeout: |
  ${first(
    lookup(datasources.remoteConfig.settings, "timeout"),
    lookup(variables.localConfig, "timeout"),
    30
  )}

coalesce

A function that returns the first non-none value from a list of arguments. Unlike first, this function only skips none values - empty strings (""), empty arrays ([]), and other empty values are considered valid and will be returned.

Parameters:

N arguments of any type that will be evaluated in order to find the first non-none value.

Returns:

any

The first non-none value from the provided arguments. If all arguments are none, returns none.

Examples:

Providing defaults when values might be none:

timeout: ${coalesce(lookup(config, "timeout"), 30)}
endpoint: ${coalesce(variables.customEndpoint, "https://api.default.com")}

Empty strings are valid (unlike with first):

# If description is "" (empty string), it's returned (not skipped)
description: ${coalesce(lookup(config, "description"), "No description")}

# Compare with first() which would skip "" and return "No description"
description: ${first(lookup(config, "description"), "No description")}

Chaining multiple sources:

apiKey: |
  ${coalesce(
    lookup(datasources.secrets.prod, "apiKey"),
    lookup(variables.overrides, "apiKey"),
    "default-api-key"
  )}

Comparison with first:

FunctionSkips noneSkips ""Skips []Use Case
coalesceDefaults for none, keep other empty values
firstGet first "meaningful" value, skip all empties

Use coalesce when:

  • You want to provide defaults specifically for none values
  • Empty strings or empty arrays are valid values you want to preserve
  • Working with optional configuration that might not be set

Use first when:

  • You want to skip all kinds of empty values
  • Looking for the first "meaningful" or "useful" value
  • Empty strings/arrays are equivalent to "not set" in your context

lookup

A function that retrieves a value from a map/object using a key. Returns none if the key is not found.

Parameters:

  1. mapping - A map/object to look up the value from.
  2. string - The key to look up in the map/object.

Returns:

any

The value associated with the key in the map/object, or none if the key is not found.

Examples:

Basic lookup (returns none if key not found):

# Use with coalesce() to provide a default
timeout: ${coalesce(lookup(config, "timeout"), 30)}

Environment-specific resource sizing with default:

instanceSize: |
  ${coalesce(
    lookup(
      object(
        production="large",
        staging="medium",
        development="small"
      ),
      variables.environment
    ),
    "small"
  )}

Chaining lookups with first():

# Try multiple config sources
apiKey: |
  ${first(
    lookup(datasources.secrets.prod, "apiKey"),
    lookup(variables.overrides, "apiKey"),
    "default-api-key"
  )}

Handling none Values:

The lookup function returns none when:

  • The specified key does not exist in the map/object
  • The map/object itself is none

This makes lookup the primary way to safely access optional configuration values:

resources:
  function:
    spec:
      # These fields use coalesce() to provide defaults when keys don't exist
      timeout: ${coalesce(lookup(datasources.config.settings, "timeout"), 30)}
      memorySize: ${coalesce(lookup(datasources.config.settings, "memory"), 512)}

      # This field is omitted entirely if the key doesn't exist (lookup returns none)
      description: ${lookup(datasources.config.metadata, "description")}

Note: Use . or [] accessors when you know the key exists and want an error if it doesn't. Use lookup() when the key might not exist and you want to handle that with none, coalesce(), or first().

base64encode

A function that encodes binary data to Base64 format using the standard RFC 4648 encoding (including padding).

Parameters:

  1. binary - The binary data to encode to Base64.

Returns:

string

The Base64-encoded string using standard RFC 4648 encoding with padding.

Examples:

Encoding binary data from files:

${base64encode(file("certificates/server.pem"))}

Encoding binary data from network requests:

${base64encode(http_resource("https://example.com/app.zip"))}

Encoding string data (string is converted to UTF-8 bytes, then base64 encoded):

${base64encode("#!/bin/bash\necho 'Hello World'")}

base64decode

A function that decodes a Base64-encoded string back to binary data using the standard RFC 4648 encoding (including padding).

Parameters:

  1. string - The Base64-encoded string to decode.

Returns:

binary

The decoded binary data from the Base64 input using standard RFC 4648 encoding with padding.

Examples:

Decoding Base64-encoded data:

${base64decode("SGVsbG8gV29ybGQ=")}

Decoding and parsing JSON:

${jsondecode(utf8(base64decode(variables.encodedConfig)))}

Blueprint Element Field Usage:

When the output of base64decode() is used as the value of a blueprint element field (resource spec, data source export, etc.), it will be automatically UTF-8 encoded by default. This means the binary data will be converted to a UTF-8 string representation.

min

A function that returns the minimum value from a list of numbers.

Parameters:

N arguments of type integer or float to find the minimum value from.

Returns:

integer | float

The minimum value from the provided arguments.

Examples:

Finding minimum from multiple values:

${min(10, 5, 8, 3)}  # Returns 3

Dynamic resource sizing:

${min(variables.maxInstances, 10)}

max

A function that returns the maximum value from a list of numbers.

Parameters:

N arguments of type integer or float to find the maximum value from.

Returns:

integer | float

The maximum value from the provided arguments.

Examples:

Finding maximum from multiple values:

${max(10, 5, 8, 3)}  # Returns 10

Ensuring minimum resource allocation:

${max(variables.minInstances, 2)}

abs

A function that returns the absolute value of a number.

Parameters:

  1. integer | float - The number to get the absolute value of.

Returns:

integer | float

The absolute value of the input number.

Examples:

Getting absolute value:

${abs(-5)}  # Returns 5
${abs(3)}   # Returns 3

cidrsubnet

A function that calculates subnet addresses within a given CIDR block.

Parameters:

  1. string - The CIDR block to calculate subnets within (e.g., "192.168.0.0/16").
  2. integer - The number of additional bits to add to the prefix length.
  3. integer - The subnet number to calculate (0-based index).

Returns:

string

The calculated subnet CIDR block.

Examples:

Calculating subnets:

${cidrsubnet("192.168.0.0/16", 8, 1)}  # Returns "192.168.1.0/24"
${cidrsubnet("10.0.0.0/8", 4, 2)}      # Returns "10.2.0.0/12"

Dynamic subnet allocation:

${cidrsubnet(variables.vpcCidr, 4, variables.subnetIndex)}

file

A function that reads binary data from a file path (local or remote) and returns it as raw bytes.

Parameters:

  1. string - The path to the file to read (local path or URI).

Returns:

binary

The raw binary data from the file.

Examples:

Reading local files:

${file("certificates/server.pem")}
${file("dist/function.zip")}
${file("images/app.tar")}

Reading remote files:

${file("s3://my-bucket/certificates/server.pem")}
${file("gs://my-bucket/dist/function.zip")}

Remote File Sources:

The file() function supports remote file sources using URI schemes for object storage services. The specific schemes and their implementations are handled at the implementation level, which provides the remote source infrastructure.

Local Files:

  • file("path/to/local/file") - Local file system access (mandatory for all implementations)

Remote Object Storage:

  • file("scheme://path/to/file") - Remote object storage access (implementation-dependent)

Where scheme is a placeholder for the specific URI scheme supported by an implementation of the specification. The actual schemes supported (such as s3://, gs://, azure://, etc.) and their configuration are determined by the spec implementation. Consult the documentation for a specific spec implementation to see the supported remote file sources and their configuration requirements.

The following is an example of a remote file source:

${file("s3://my-bucket/certificates/server.pem")}

Note: For HTTP/HTTPS network requests, use the http_resource() function instead.

Blueprint Element Field Usage:

When the output of file() is used as the value of a blueprint element field (resource spec, data source export, etc.), it will be automatically UTF-8 encoded by default.

Examples:

resources:
  certificate:
    type: aws/acm/certificate
    spec:
      # This will be UTF-8 encoded automatically
      certificateBody: ${file("certificates/server.pem")}

Explicit Base64 Encoding:

If you need to explicitly base64-encode the binary data (for example, when the provider expects base64-encoded content), use base64encode():

resources:
  certificate:
    type: aws/acm/certificate
    spec:
      # Explicitly base64-encode for providers that expect it
      certificateBody: ${base64encode(file("certificates/server.pem"))}

http_resource

A function that fetches data from public HTTP/HTTPS URLs and returns it as raw bytes.

Parameters:

  1. string - The public HTTP/HTTPS URL to fetch data from.

Returns:

binary

The raw binary data from the HTTP response.

Examples:

Fetching public configuration data:

${http_resource("https://raw.githubusercontent.com/example/config/main/app.json")}

Fetching public release files:

${http_resource("https://github.com/example/app/releases/download/v1.0.0/app.zip")}

Fetching public documentation:

${http_resource("https://example.com/docs/api-schema.json")}

Blueprint Element Field Usage:

When the output of http_resource() is used as the value of a blueprint element field (resource spec, data source export, etc.), it will be automatically UTF-8 encoded by default.

Explicit Base64 Encoding:

If you need to explicitly base64-encode the binary data (for example, when the provider expects base64-encoded content), use base64encode():

resources:
  application:
    type: aws/ec2/instance
    spec:
      # Explicitly base64-encode for providers that expect it
      userData: ${base64encode(http_resource("https://raw.githubusercontent.com/example/scripts/main/init.sh"))}

utf8

A function that converts binary data to UTF-8 encoded text.

Parameters:

  1. binary - The binary data to convert to UTF-8 text.

Returns:

string

The UTF-8 encoded text representation of the binary data.

When to Use:

The utf8() function is only needed when the result is passed into another function call for processing before the final output is set to a blueprint element field. It is not needed if you are setting the result directly on a blueprint element field.

Examples:

When utf8() is needed (function chaining):

# JSON parsing - need utf8() because jsondecode() expects text
${jsondecode(utf8(file("config.json")))}

# String operations - need utf8() because replace() expects text
${replace(utf8(http_resource("https://example.com/template.txt")), "{{name}}", variables.appName)}

# Hash computation - need utf8() because sha256() expects text input
${sha256(utf8(file("data.txt")))}

When utf8() is NOT needed (direct assignment):

# Direct assignment to blueprint element field - automatic UTF-8 encoding
resources:
  application:
    type: aws/ec2/instance
    spec:
      # No utf8() needed - automatic UTF-8 encoding
      userData: ${file("scripts/init.sh")}
      config: ${http_resource("https://example.com/config.json")}

Summary:

  • Use utf8() when passing binary data to other functions
  • Don't use utf8() when setting binary data directly on blueprint element fields
  • Blueprint element fields automatically UTF-8 encode binary data

sha256

A function that computes the SHA-256 hash of the input data and returns it as a hexadecimal string.

Parameters:

  1. any - The data to compute the hash for.

Returns:

string

The SHA-256 hash as a hexadecimal string.

Examples:

Computing hash of a string:

${sha256("Hello World")}

Computing hash of binary data:

${sha256(base64decode(datasources.certificate.certificateBody))}

Using hash for resource naming:

my-bucket-${sha256(variables.environment)}

md5

A function that computes the MD5 hash of the input data and returns it as a hexadecimal string.

Parameters:

  1. any - The data to compute the hash for.

Returns:

string

The MD5 hash as a hexadecimal string.

Examples:

Computing MD5 hash:

${md5("Hello World")}

Using MD5 for file integrity:

${md5(file("config.json"))}

sha1

A function that computes the SHA-1 hash of the input data and returns it as a hexadecimal string.

Parameters:

  1. any - The data to compute the hash for.

Returns:

string

The SHA-1 hash as a hexadecimal string.

Examples:

Computing SHA-1 hash:

${sha1("Hello World")}

Using SHA-1 for data integrity:

${sha1(values.configData)}

uuid

A function that generates a version 4 UUID (random UUID) and returns it as a string.

Parameters:

None.

Returns:

string

A version 4 UUID string.

Examples:

Generating a UUID:

${uuid()}

Using UUID for unique resource names:

resource-${uuid()}

cwd

A function that returns the user's current working directory as set on the host system, by a tool implementing the spec or by the user.

Parameters:

This function does not take any parameters.

Returns:

string

The current working directory of the user.

Example:

${cwd()}/blueprints/core-infra.blueprint.yaml

datetime

A function that returns the current date/time as per the host system's clock in the specified format. All times are normalised to UTC.

The datetime function supports the following date/time formats:

  • unix - The number of seconds since the Unix epoch. (e.g. 1611312000)
  • rfc3339 - The date/time in RFC3339 format. (e.g. 2023-01-02T15:04:05Z07:00)
  • tag - The date/time in a format suitable for use as a tag. (e.g. 2023-01-02--15-04-05)
  • tagcompact - The date/time in a compact format suitable for use as a tag. (e.g. 20230102150405)

rfc3339 is a format derived from ISO 8601.

This function is useful for generating timestamps that can be used for tagging and versioning resources. (e.g. Docker image tags, S3 object keys, etc.)

Parameters:

  1. string - The date/time format to return the current date/time in. The format should be one of the valid formats defined above.

Returns:

string

A string representing the current time in the requested format.

Example:

${datetime("tag")}

Special Values

none

The none value is a special literal in the substitution language that represents the explicit absence of a value.

Syntax:

${none}

Behavior:

When a substitution expression evaluates to none:

  • The field containing that substitution is omitted from the materialised blueprint
  • In string interpolation, none is treated as an empty string ("")
  • Most functions propagate none (if any argument is none, the result is none)
  • Some functions handle none specially (e.g., or, if, first)

Comparison Table:

ValueMeaningField Presence in Output
${none}Explicit absenceOmitted (not present)
""Empty stringPresent with empty value
[]Empty arrayPresent with empty array
{}Empty objectPresent with empty object
0ZeroPresent with value 0
falseBoolean falsePresent with value false

String Interpolation:

In string interpolation, none is treated as an empty string (""):

# If domain is none → becomes "https:///api" (empty string in place of none)
url: "https://${domain}/api"

# Use coalesce() to provide a meaningful default instead of empty string
url: "https://${coalesce(domain, "localhost")}/api"

Arrays:

When none appears as an array element, it is filtered out:

items:
  - required-item
  - ${if(includeOptional, "optional-item", none)}
  - another-item

# When includeOptional is false:
# Result: ["required-item", "another-item"]

Objects:

When none appears as a value in an object, the key-value pair is omitted:

config:
  required: value
  optional: ${if(condition, "value", none)}

# When condition is false:
# Result: {required: "value"}

Function Behavior:

Functions fall into two categories regarding none:

  1. Propagating functions (most functions): Any none argument → none result

    • String functions: trim, replace, substr, to_upper, to_lower, etc.
    • Math functions: min, max, abs
    • Encoding functions: base64encode, sha256, etc.
  2. Handling functions: Treat none specially

    • if(cond, a, b) - Treats none condition as falsy
    • coalesce(...) - Returns first non-none value (preserves "", [], etc.)
    • first(...) - Skips none, "", and [] (all "empty values"), returns first non-empty
    • lookup(obj, key) - Returns none when key not found

Common Patterns:

Conditional field omission:

resources:
  instance:
    spec:
      monitoring: ${if(eq(variables.env, "prod"), true, none)}

Providing defaults with coalesce():

resources:
  function:
    spec:
      timeout: ${coalesce(lookup(config, "timeout"), 30)}
      memorySize: ${coalesce(variables.customMemory, 512)}

Chaining fallbacks (skipping all empty values):

apiUrl: |
  ${first(
    lookup(config, "customUrl"),
    variables.overrideUrl,
    "https://api.default.com"
  )}

See Also:

Clarification on _g functions

A lot of the core functions have two definitions in the spec, one for direct function calls and one for function composition. The _g1 suffix is used to denote a function to be used for composition that takes static arguments and produces a function that takes the dynamic value to be transformed.

A direct usage example would be:

${trimprefix(values.cacheClusterConfig.host, "http://")}

A composition usage example would be:

${map(values.cacheClusterConfig.hosts, trimprefix_g("http://"))}

Only some of the core string manipulation functions have a composable version, this is because they are the most likely to be used in a composition context. Implementations are free to add composable versions of other functions as they see fit. A plugin system for functions could also be useful in allowing users of a tool implementing the spec to add their own functions.

⚠️ Function usage caveats

Higher-order functions

Functions that are returned when calling named functions, such as the result of getattr("id"), can not be called directly.

For example, getattr("id")(resource.spec) is not a valid expression. In this example, you would instead use resource.spec.id to access the id attribute of the spec object.

Higher-order functions in the blueprint substitution language can only be used as arguments to other functions that accept functions as parameters. A valid usage of the getattr function would be as follows:

${map(datasources.network.subnets, getattr("id"))}

This example would map over a list of subnets and extract the id attribute from each subnet object, producing a list of IDs.

Footnotes

  1. The reasoning for the _g suffix for these functions is that g is commonly used in mathematics to denote a function in composition.