Conditions
What are Conditions?
Conditions control step applicability during planning.
- Evaluated while the plan is being built
- If the condition evaluates to
false, the step becomesNotApplicable - Conditions shape the plan, not execution
Think of Conditions as a planning-time filter.
They decide whether a step becomes part of the executable plan.
⚠️ Context Resolvers vs Templates vs Conditions vs Preconditions
Context Resolvers populate Request.Context.* during planning.
Template Substitution consumes Request.* values to build strings.
Conditions decide step applicability during planning (NotApplicable).
Preconditions guard step behavior during execution (Blocked / Fail / Continue).
| Condition | Precondition |
|---|---|
| Planning time | Execution time |
Marks step NotApplicable | Controls runtime behavior |
| Affects plan shape | Affects execution flow |
Full Example
@{
Name = 'Provision EU Joiner'
Type = 'IdLE.Step.EmitEvent'
Condition = @{
All = @(
@{ Equals = @{ Path = 'Plan.LifecycleEvent'; Value = 'Joiner' } }
@{ In = @{ Path = 'Request.Context.Region'; Values = @('EU','DE') } }
@{ Exists = 'Request.IdentityKeys.EmployeeId' }
)
}
With = @{
Message = 'Provisioning for EU Joiner'
}
}
Explanation
The step is applicable only if:
- The lifecycle event is
Joiner - The region is
EUorDE - An
EmployeeIdexists
If any condition evaluates to false, the step is marked as NotApplicable during planning.
Conditions as a guard against plan-time validation
When a step's Condition evaluates to false, IdLE marks it NotApplicable and skips all remaining plan-time processing for that step, including:
Withtemplate resolutionWithSchemavalidation
This means a condition-guarded step will not cause a planning failure even if its With block references data that is absent or if required schema keys are missing. The step is simply excluded from the executable plan.
The Exists operator is specifically designed for this pattern: using Exists in a Condition does not require the referenced path to exist at plan time. If the path is absent, Exists evaluates to false and the step becomes NotApplicable.
A step that provisions an EU-region user can be safely guarded by a condition that checks for the Region attribute.
If the attribute is absent, Exists evaluates to false, the step is NotApplicable, and neither the condition path nor the With block causes a planning error.
@{
Name = 'Provision EU User'
Type = 'IdLE.Step.EnsureAttributes'
Condition = @{ Exists = 'Request.Context.Views.Identity.Profile.Attributes.Region' }
With = @{
IdentityKey = '{{Request.IdentityKeys.EmployeeId}}'
Attributes = @{ Region = '{{Request.Context.Views.Identity.Profile.Attributes.Region}}' }
}
}
If Region is absent, the condition evaluates to false, the step is NotApplicable, and no template errors are raised.
Applicable steps still undergo full template resolution and schema validation — this behavior is unchanged.
Conditions DSL
Preconditions use the same DSL as Conditions.
This section is the authoritative DSL reference.
Groups
All— all child conditions must be true (AND)Any— at least one child condition must be true (OR)None— none of the child conditions must be true (NOR)
Operators
Equals
@{ Equals = @{ Path = 'Plan.LifecycleEvent'; Value = 'Joiner' } }
NotEquals
@{ NotEquals = @{ Path = 'Request.Context.Tenant'; Value = 'DEV' } }
Exists
@{ Exists = 'Request.Context.ManagerUpn' }
In
@{
In = @{
Path = 'Plan.LifecycleEvent'
Values = @('Joiner','Mover')
}
}
Contains
For list membership evaluation (case-insensitive).
Pathmust resolve to a list/array- Returns
trueif the list contains the specified value - Throws an error if
Pathresolves to a scalar
# Check if a specific group DN is in the entitlements (using the global View populated by ContextResolvers)
@{
Contains = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Value = 'CN=BreakGlass-Users,OU=Groups,DC=example,DC=com'
}
}
Note: When
Request.Context.Views.Identity.Entitlementscontains objects (e.g.,@{ Kind = 'Group'; Id = '...' }), use.Idto extract Id values:Entitlements.Idreturns an array of all Id values.
NotContains
For list non-membership evaluation (case-insensitive).
Pathmust resolve to a list/array- Returns
trueif the list does not contain the specified value - Throws an error if
Pathresolves to a scalar
# Prevent execution if identity has a specific group (using the global View populated by ContextResolvers)
@{
NotContains = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Value = 'CN=BreakGlass-Users,OU=Groups,DC=example,DC=com'
}
}
Like
For wildcard pattern matching (case-insensitive).
- If
Pathresolves to a scalar: matches against the value directly - If
Pathresolves to a list: returnstrueif any element matches the pattern - Uses PowerShell's
-likeoperator (supports*and?wildcards)
# Scalar example: check if DisplayName matches a pattern (attributes are nested under Attributes key)
@{
Like = @{
Path = 'Request.Context.Views.Identity.Profile.Attributes.DisplayName'
Pattern = '* (Contractor)'
}
}
# List example: check if any entitlement Id matches the pattern (using the global View)
@{
Like = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Pattern = 'CN=HR-*'
}
}
Note: When checking entitlement Ids, use
.Idto extract property values from entitlement objects. The pathEntitlements.Iduses member-access enumeration to return an array of all Id values.
NotLike
For wildcard pattern non-matching (case-insensitive).
- If
Pathresolves to a scalar: returnstrueif the value does not match the pattern - If
Pathresolves to a list: returnstrueif no element matches the pattern - Uses PowerShell's
-notlikeoperator (supports*and?wildcards)
# Scalar example (attributes are nested under Attributes key)
@{
NotLike = @{
Path = 'Request.Context.Views.Identity.Profile.Attributes.DisplayName'
Pattern = '* (Contractor)'
}
}
# List example: ensure no HR groups in entitlements (using the global View)
@{
NotLike = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Pattern = 'CN=HR-*'
}
}
Comparison Semantics
- All comparisons are case-insensitive by default
- String-based comparisons for
Equals,NotEquals,In,Contains,NotContains - Pattern matching for
LikeandNotLikeuses PowerShell's-likeoperator - Deterministic evaluation
- Values are converted to string before comparison
Member-Access Enumeration
When a Path points to a list of objects, you can access properties of those objects using dot notation:
Request.Context.Views.Identity.Entitlements→ returns array of entitlement objectsRequest.Context.Views.Identity.Entitlements.Id→ returns array of allIdvalues
Note: These paths reference the global View populated by a
ContextResolversentry withIdLE.Entitlement.List. See Context Resolvers for details.
For provider-specific entitlements, use the scoped path:Request.Context.Providers.<ProviderAlias>.<AuthSessionKey>.Identity.Entitlements.Id(where<AuthSessionKey>is the auth session key;Defaultis used when noWith.AuthSessionNameis specified).
Example:
# Entitlements contains: @(
# @{ Kind = 'Group'; Id = 'CN=Users,...' }
# @{ Kind = 'Group'; Id = 'CN=Admins,...' }
# )
# Check if any entitlement Id matches a pattern
@{
Like = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Pattern = 'CN=HR-*'
}
}
This follows PowerShell's native member-access enumeration behavior.
List vs Scalar Behavior
| Operator | Scalar Path | List Path |
|---|---|---|
Contains | ❌ Error (must be list) | ✅ Check if value in list |
NotContains | ❌ Error (must be list) | ✅ Check if value not in list |
Like | ✅ Match against value | ✅ Match if any element matches |
NotLike | ✅ Check value doesn't match | ✅ Check no element matches |
Validation Rules
- Each node may contain exactly one operator or group
- Unknown keys cause planning-time errors
- Missing or empty
Pathcauses validation errors
Common Patterns
Only for a lifecycle event
Condition = @{ Equals = @{ Path = 'Plan.LifecycleEvent'; Value = 'Leaver' } }
Only if a request field exists
Condition = @{ Exists = 'Request.Context.ManagerUpn' }
Allowlist values (In)
Condition = @{ In = @{ Path = 'Request.Context.Region'; Values = @('EU','US') } }
Negation (NOT via None)
Condition = @{ None = @( @{ Equals = @{ Path = 'Request.Context.Tenant'; Value = 'DEV' } } ) }
Combine multiple checks (All / AND)
Condition = @{
All = @(
@{ Equals = @{ Path = 'Plan.LifecycleEvent'; Value = 'Joiner' } }
@{ Exists = 'Request.IdentityKeys.EmployeeId' }
)
}
Only if not member of a specific group (NotContains)
Condition = @{
NotContains = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Value = 'CN=BreakGlass-Users,OU=Groups,DC=example,DC=com'
}
}
Only if not member of any HR group (NotLike)
Condition = @{
NotLike = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Pattern = 'CN=HR-*'
}
}
Only for contractors (Like with scalar)
Condition = @{
Like = @{
Path = 'Request.Context.Views.Identity.Profile.Attributes.DisplayName'
Pattern = '* (Contractor)'
}
}
Guard destructive step (combine NotContains with lifecycle check)
Condition = @{
All = @(
@{ Equals = @{ Path = 'Plan.LifecycleEvent'; Value = 'Leaver' } }
@{
NotContains = @{
Path = 'Request.Context.Views.Identity.Entitlements.Id'
Value = 'CN=Protected-Accounts,OU=Groups,DC=example,DC=com'
}
}
)
}
Troubleshooting
Step is NotApplicable but you expected it to run
- Verify the used
Pathvalues are correct and exist at planning time (for exampleRequest.Context.*vsRequest.IdentityKeys.*). - Remember comparisons are string-based. Normalize boolean-like values in the request (for example use
'True'/'False'consistently).
Planning fails with “Unknown key … in condition node”
Each node may contain exactly one of:
- a group:
All,Any,None - an operator:
Equals,NotEquals,Exists,In,Contains,NotContains,Like,NotLike
Any additional keys cause a planning-time validation error.
Planning fails with “Missing or empty Path”
All operators require a non-empty Path.
For Exists, prefer the short form Exists = '…' to avoid shape errors.
Planning fails with "Contains operator requires Path to resolve to a list"
Contains and NotContains only work with list/array paths. If you need to check a scalar value, use Equals or Like instead.
Confusion about “Skipped”
Conditions do not “skip” execution. They decide applicability during planning and mark the step as NotApplicable.