| title | Template Substitution |
|---|---|
| sidebar_label | Template Substitution |
Template substitution allows you to reference values from the planning context inside step configuration (With) values during planning. Conditions and Preconditions use the condition DSL and path resolution and do not support {{...}} templates.
Templates are data-only and safe.
No ScriptBlocks or dynamic PowerShell expressions are supported.
Template substitution resolves values from: Request.*
It replaces template placeholders with actual values before execution.
Think of template substitution as value resolution, not logic execution.
It simply reads values from the context and inserts them into configuration fields.
:::warning Do not confuse these concepts
Context Resolvers populate Request.Context.* during planning.
Template Substitution consumes allowlisted Request.* values to build strings.
Conditions decide step applicability during planning (NotApplicable).
Preconditions guard step behavior during execution (Blocked / Fail / Continue).
:::
Templates can reference:
| Root | Description |
|---|---|
Request.Intent.* |
Caller-provided action inputs |
Request.Context.* |
Read-only associated context (host/resolver-provided) |
Request.IdentityKeys.* |
Identifiers of the target identity |
Request.LifecycleEvent |
Lifecycle event type (e.g. Joiner) |
Request.CorrelationId |
Stable correlation identifier |
Request.Actor |
Originator of the request |
@{
Name = 'Create UPN'
Type = 'IdLE.Step.EnsureAttributes'
With = @{
UserPrincipalName = '{{Request.IdentityKeys.FirstName}}.{{Request.IdentityKeys.LastName}}@example.com'
}
}If:
- FirstName = John
- LastName = Doe
The resolved value becomes:
John.Doe@example.com
A value containing only a single placeholder preserves the resolved type (bool, int, datetime, guid, string):
# Resolves to the actual [bool] value, not the string "True"
Enabled = '{{Request.Intent.IsEnabled}}'DisplayName = '{{Request.IdentityKeys.FirstName}} {{Request.IdentityKeys.LastName}}'A value with surrounding text always produces a string:
Description = 'Provisioned during {{Request.LifecycleEvent}}'Backslash (\) is a literal character in template strings and requires no escaping.
Windows-style paths and domain-qualified names work as-is:
# \ is kept as-is; only the placeholder is substituted
IdentityKey = 'DOMAIN\{{Request.IdentityKeys.sAMAccountName}}'
# → e.g. 'DOMAIN\jdoe'To include a literal {{ in the output, prefix it with \. The escape is applied whenever
\{{ is not immediately followed by a valid allowed-root template path and }}:
# \{{ not followed by a valid path+}} → literal {{ in output
Value = 'Literal \{{ braces here'
# → 'Literal {{ braces here'
# \{{ followed by an invalid/disallowed path → also escaped (literal {{ in output)
Value = '\{{Request.InvalidRoot}}'
# → '{{Request.InvalidRoot}}'Summary of backslash behaviour:
| Input | Result |
|---|---|
DOMAIN\{{Request.IdentityKeys.sAMAccountName}} |
DOMAIN\jdoe — \ literal, valid template resolved |
Literal \{{ braces here |
Literal {{ braces here — escape applied |
\{{Request.InvalidRoot}} |
{{Request.InvalidRoot}} — invalid root, escape applied |
Literal \{{ and {{Request.Intent.Name}} |
Literal {{ and TestName — escape + template |
During plan build, IdLE validates every template value:
- Unbalanced braces — mismatched
{{/}}pairs throw a syntax error. - Invalid path — paths must use dot-separated identifiers (letters, numbers, underscores).
- Disallowed root — paths outside the allowlist throw a security error.
- Null or missing value — if the resolved path does not exist, an error is thrown.
- Non-scalar value — resolving to a hashtable or array is not allowed.
- Verify the path exists on the request object (allowed
Request.*roots only). - Ensure correct casing and full path (for example,
Request.Context.*).
- The referenced path may be
$null. - Validate the request preparation logic before execution.