# YAML Tags

To use the custom tags with your preferred editor, you must make the editor aware of the custom tags.

For VS Code, for example, add these entries to your `settings.json`:

```json
{
    "yaml.customTags": [
        "!Condition sequence",
        "!Context scalar",
        "!Enumerate sequence",
        "!Env scalar",
        "!File scalar",
        "!File sequence",
        "!Find sequence",
        "!Format sequence",
        "!If sequence",
        "!Index scalar",
        "!KeyOf scalar",
        "!Value scalar",
        "!AtIndex scalar"
    ]
}
```

#### `!KeyOf`

Example:

```yaml
policy: !KeyOf my-policy-id
```

Resolves to the primary key of the model instance defined by id _my-policy-id_.

If no matching entry can be found, an error is raised and the blueprint is invalid.

#### `!Env`

Example:

```yaml
password: !Env my_env_var
```

Returns the value of the given environment variable. Can be used as a scalar with `!Env my_env_var, default` to return a default value.

#### `!File`

Example:

```yaml
password: !File /path/to/file
```

Returns the contents of the file at the given path. Can be used as a scalar with `!File /file/to/path, default` to return a default value.

#### `!Find`

Examples:

{/* prettier-ignore-start */}

```yaml
configure_flow: !Find [authentik_flows.flow, [slug, default-password-change]]
```

```yaml
configure_flow:
    !Find [
        authentik_flows.flow,
        [!Context property_name, !Context property_value],
    ]
```

{/* prettier-ignore-end */}

Looks up any model and resolves to the matches' primary key.
First argument is the model to be queried, remaining arguments are expected to be pairs of key=value pairs to query for.

#### `!FindObject`:ak-version[2025.8]

Examples:

{/* prettier-ignore-start */}

```yaml
flow_designation:
    !AtIndex [
        !FindObject [authentik_flows.flow, [slug, default-password-change]],
        designation,
    ]
```

{/* prettier-ignore-end */}

Looks up any model and resolves to the matches' serialized data.
First argument is the model to be queried, remaining arguments are expected to be pairs of key=value pairs to query for.

#### `!Context`

Example:

{/* prettier-ignore-start */}


```yaml
configure_flow: !Context foo
```

{/* prettier-ignore-end */}

Find values from the context. Can optionally be called with a default like `!Context [foo, default-value]`.

#### `!Format`

Example:

```yaml
name: !Format [my-policy-%s, !Context instance_name]
```

Format a string using python's % formatting. First argument is the format string, any remaining arguments are used for formatting.

#### `!If`

Minimal example:

```yaml
# Short form
# !If [<condition>]
required: !If [true]
```

```yaml
# Longer form
# !If [<condition>, <when true>, <when false>]
required: !If [true, true, false]
```

Full example:

```yaml
attributes: !If [
        !Condition [...], # Or any valid YAML or custom tag. Evaluated as boolean in Python
        {
            # When condition evaluates to true
            dictionary:
                {
                    with: { keys: "and_values" },
                    and_nested_custom_tags: !Format ["foo-%s", !Context foo],
                },
        },
        [
            # When condition evaluates to false
            list,
            with,
            items,
            !Format ["foo-%s", !Context foo],
        ],
    ]
```

Conditionally add YAML to a blueprint.

Similar to a one-line if, the first argument is the condition, which can be any valid yaml or custom tag. It will be evaluated as boolean in python. However, keep in mind that dictionaries and lists will always evaluate to `true`, unless they are empty.

The second argument is used when the condition is `true`, and the third - when `false`. The YAML inside both arguments will be fully resolved, thus it is possible to use custom YAML tags and even nest them inside dictionaries and lists.

#### `!Condition`

Minimal example:

```yaml
required: !Condition [OR, true]
```

Full example:

```yaml
required: !Condition [
    AND, # Valid modes are: AND, NAND, OR, NOR, XOR, XNOR
    !Context instance_name,
    !Find [authentik_flows.flow, [slug, default-password-change],
    "My string",
    123
]
```

Converts one or more arguments to their boolean representations, then merges all representations together.
Requires at least one argument after the mode selection.

If only a single argument is provided, its boolean representation will be returned for all normal modes and its negated boolean representation will be returned for all negated modes.

Normally, it should be used to define complex conditions for use with an `!If` tag or for the `conditions` attribute of a blueprint entry (see [the blueprint file structure](./structure.md)). However, this is essentially just a boolean evaluator so it can be used everywhere a boolean representation is required.

#### `!Enumerate`, `!Index` and `!Value`

These tags collectively make it possible to iterate over objects which support iteration. Any iterable Python object is supported. Such objects are sequences (`[]`), mappings (`{}`) and even strings.

1. `!Enumerate` tag:

This tag takes 3 arguments:

```yaml
!Enumerate [<iterable>, <output_object_type>, <single_item_yaml>]
```

- **iterable**: Any Python iterable or custom tag that resolves to such iterable
- **output_object_type**: `SEQ` or `MAP`. Controls whether the returned YAML will be a mapping or a sequence.
- **single_item_yaml**: The YAML to use to create a single entry in the output object

2. `!Index` tag:

:::info
This tag is only valid inside an `!Enumerate` tag
:::

This tag takes 1 argument:

```yaml
!Index <depth>
```

- **depth**: Must be >= 0. A depth of 0 refers to the `!Enumerate` tag this tag is located in. A depth of 1 refers to one `!Enumerate` tag above that (to be used when multiple `!Enumerate` tags are nested inside each other).

Accesses the `!Enumerate` tag's iterable and resolves to the index of the item currently being iterated (in case `!Enumerate` is iterating over a sequence), or the mapping key (in case `!Enumerate` is iterating over a mapping).

For example, given a sequence like this - `["a", "b", "c"]`, this tag will resolve to `0` on the first `!Enumerate` tag iteration, `1` on the second and so on. However, if given a mapping like this - `{"key1": "value1", "key2": "value2", "key3": "value3"}`, it will first resolve to `key1`, then to `key2` and so on.

3. `!Value` tag:

:::info
This tag is only valid inside an `!Enumerate` tag
:::

This tag takes 1 argument:

```yaml
!Value <depth>
```

- **depth**: Must be >= 0. A depth of 0 refers to the `!Enumerate` tag this tag is located in. A depth of 1 refers to one `!Enumerate` tag above that (to be used when multiple `!Enumerate` tags are nested inside each other).

Accesses the `!Enumerate` tag's iterable and resolves to the value of the item currently being iterated.

For example, given a sequence like this - `["a", "b", "c"]`, this tag will resolve to `a` on the first `!Enumerate` tag iteration, `b` on the second and so on. If given a mapping like this - `{"key1": "value1", "key2": "value2", "key3": "value3"}`, it will first resolve to `value1`, then to `value2` and so on.

Minimal examples:

{/* prettier-ignore-start */}

```yaml
configuration_stages: !Enumerate [
        !Context map_of_totp_stage_names_and_types,
        SEQ, # Output a sequence
        !Find [
            !Format [
                "authentik_stages_authenticator_%s.authenticator%sstage",
                !Index 0,
                !Index 0,
            ],
            [name, !Value 0],
        ], # The value of each item in the sequence
    ]
```

{/* prettier-ignore-end */}

The above example will resolve to something like this:

```yaml
configuration_stages:
    - !Find [
          authentik_stages_authenticator_<stage_type_1>.authenticator<stage_type_1>stage,
          [name, <stage_name_1>],
      ]
    - !Find [
          authentik_stages_authenticator_<stage_type_2>.authenticator<stage_type_2>stage,
          [name, <stage_name_2>],
      ]
```

Similarly, a mapping can be generated like so:

```yaml
example: !Enumerate [
        !Context list_of_totp_stage_names,
        MAP, # Output a map
        [
            !Index 0, # The key to assign to each entry
            !Value 0, # The value to assign to each entry
        ],
    ]
```

The above example will resolve to something like this:

```yaml
example:
    0: <stage_name_1>
    1: <stage_name_2>
```

Full example:

:::caution
Note that an `!Enumerate` tag's iterable can never be an `!Index` or `!Value` tag with a depth of `0`. Minimum depth allowed is `1`. This is because a depth of `0` refers to the `!Enumerate` tag the `!Index` or `!Value` tag is in, and an `!Enumerate` tag cannot iterate over itself.
:::

{/* prettier-ignore-start */}

```yaml
example: !Enumerate [
        !Context sequence, # ["foo", "bar"]
        MAP, # Output a map
        [
            !Index 0, # Use the indexes of the items in the sequence as keys
            !Enumerate [
                # Nested enumeration
                # Iterate over each item of the parent enumerate tag.
                # Notice depth is 1, not 0, since we are inside the nested enumeration tag!
                !Value 1,
                SEQ, # Output a sequence
                !Format [
                    "%s: (index: %d, letter: %s)",
                    !Value 1,
                    !Index 0,
                    !Value 0,
                ],
            ],
        ],
    ]
```

{/* prettier-ignore-end */}

The above example will resolve to something like this:

```yaml
"0":
    - "foo: (index: 0, letter: f)"
    - "foo: (index: 1, letter: o)"
    - "foo: (index: 2, letter: o)"
"1":
    - "bar: (index: 0, letter: b)"
    - "bar: (index: 1, letter: a)"
    - "bar: (index: 2, letter: r)"
```

#### `!AtIndex`

Minimal example:

```yaml
value_in_sequence: !AtIndex [["first", "second"], 0] # Resolves to "first"
value_in_mapping: !AtIndex [{ "foo": "bar", "other": "value" }, "foo"] # Resolves to "bar"
```

Example with default value:

```yaml
default_value: !AtIndex [["first"], 100, "default"]  # Resolves to "default"
default_value: !AtIndex [["first"], 100]  # Throws an error
```

Resolves to the value at a specific index in a sequence or a mapping.

If no value can be found at the specified index and no default is provided, an error is raised and the blueprint is invalid.
