Description
IdLE template resolution currently coerces resolved placeholder values to string unconditionally.
This breaks scenarios where a step expects a typed scalar (e.g. bool) and the workflow uses a template placeholder like:
Enabled = '{{Request.DesiredState.Enabled}}'
Even if Request.DesiredState.Enabled is a real boolean $false, the template resolver returns "False" (a non-empty string). In PowerShell, casting a non-empty string to [bool] yields $true, so the user ends up enabled unexpectedly.
This is observed with IdLE.Step.CreateIdentity (AD provider), where Enabled is derived from Attributes['Enabled'] via [bool] conversion.
This is a fail-fast correctness bug: it silently flips user intent for boolean parameters.
Step-0 Analysis (Safe)
What happens today
-
Request contains a real boolean:
DesiredState.Enabled = $false
-
Workflow contains a template placeholder:
Enabled = '{{Request.DesiredState.Enabled}}'
-
The template resolver resolves placeholders and coerces to string (effectively [string]$resolvedValue).
-
The AD provider adapter reads the resolved value:
$enabled = [bool]$Attributes['Enabled']
In PowerShell:
[bool]"False" is $true (because it's a non-empty string).
Why this is not a user error
- Users are doing the correct thing: passing a boolean in the request.
- The workflow template language suggests typed data flows should work (especially for scalar placeholders).
- The engine should preserve the type when the value is a pure placeholder, otherwise typed step inputs cannot be safely templated.
Steps to Reproduce
-
Create a request:
$joinerReq = New-IdleLifecycleRequestObject -LifecycleEvent 'Joiner' -Actor $env:USERNAME `
-IdentityKeys @{ SamAccountName = 'foo.bar' } `
-DesiredState @{ Enabled = $false }
-
Use a workflow step that templates Enabled:
@{
Name = 'Create AD account'
Type = 'IdLE.Step.CreateIdentity'
With = @{
Provider = 'AD'
IdentityKey = '{{Request.IdentityKeys.SamAccountName}}'
Attributes = @{
Enabled = '{{Request.DesiredState.Enabled}}'
}
}
}
-
Execute the plan (any host runner).
Expected Behavior
Enabled should remain a boolean False after template resolution.
- The resulting AD account should be created disabled (or at least the provider should see
$enabled -eq $false).
Actual Behavior
- The template resolver returns
"False" (string).
- The AD provider converts it to boolean and receives
$true.
- The account is created enabled (or
Enabled behaves inverted).
Scope / Impact
- Affects any step/provider relying on typed scalar inputs (bool, int, datetime, guid, etc.) when values are supplied via templates.
- High risk: produces silent incorrect state and can cause security/process issues (e.g., accounts enabled when they should be disabled).
- Relevant for 1.0 because template-based workflows are a core feature.
Proposed Fix (Agent-Safe)
Fix rule
When a workflow value is exactly one template placeholder (no prefix/suffix text), the template engine must return the resolved value as-is (typed object) instead of coercing to string.
Examples:
Implementation outline
-
In the template resolution function (currently Resolve-IdleTemplateString or wherever the placeholder substitution is performed):
- Detect "pure placeholder" strings using a strict regex, e.g.
^\s*\{\{.+?\}\}\s*$
- If it is a pure placeholder:
- Resolve the placeholder and return the underlying value (
object) without converting to string.
- Else:
- Perform the current string interpolation behavior.
-
Ensure this behavior is applied consistently wherever workflow templates are resolved (arrays/hashtables/nested structures).
-
Add unit tests for:
- bool
$false stays $false (typed)
- bool
$true stays $true
- int stays int
- datetime stays datetime
- mixed string interpolation still returns string
-
Add a regression test for the AD CreateIdentity scenario:
- CreateIdentity receives a boolean
$false for Enabled when templated.
Backwards compatibility
This is a bug fix. Typed preservation only happens for pure placeholders and should not break existing string interpolation patterns.
Additional Notes / References
- The AD provider currently interprets Enabled via
[bool]$Attributes['Enabled'], which is correct given a boolean input, but becomes wrong after string coercion.
- After this fix, provider code does not need to change; template resolution will supply correct types.
Environment
- PowerShell version: 7.x (PowerShell Core)
- OS: Windows (RSAT/AD tooling scenario)
- IdLE version / commit: current
main / latest release at time of report
Description
IdLE template resolution currently coerces resolved placeholder values to string unconditionally.
This breaks scenarios where a step expects a typed scalar (e.g.
bool) and the workflow uses a template placeholder like:Enabled = '{{Request.DesiredState.Enabled}}'Even if
Request.DesiredState.Enabledis a real boolean$false, the template resolver returns"False"(a non-empty string). In PowerShell, casting a non-empty string to[bool]yields$true, so the user ends up enabled unexpectedly.This is observed with
IdLE.Step.CreateIdentity(AD provider), whereEnabledis derived fromAttributes['Enabled']via[bool]conversion.This is a fail-fast correctness bug: it silently flips user intent for boolean parameters.
Step-0 Analysis (Safe)
What happens today
Request contains a real boolean:
DesiredState.Enabled = $falseWorkflow contains a template placeholder:
Enabled = '{{Request.DesiredState.Enabled}}'The template resolver resolves placeholders and coerces to string (effectively
[string]$resolvedValue).The AD provider adapter reads the resolved value:
$enabled = [bool]$Attributes['Enabled']In PowerShell:
[bool]"False"is$true(because it's a non-empty string).Why this is not a user error
Steps to Reproduce
Create a request:
Use a workflow step that templates
Enabled:Execute the plan (any host runner).
Expected Behavior
Enabledshould remain a booleanFalseafter template resolution.$enabled -eq $false).Actual Behavior
"False"(string).$true.Enabledbehaves inverted).Scope / Impact
Proposed Fix (Agent-Safe)
Fix rule
When a workflow value is exactly one template placeholder (no prefix/suffix text), the template engine must return the resolved value as-is (typed
object) instead of coercing to string.Examples:
Input:
'{{Request.DesiredState.Enabled}}'bool(same asRequest.DesiredState.Enabled)Input:
'Enabled={{Request.DesiredState.Enabled}}'(mixed string)string(string interpolation remains valid)Implementation outline
In the template resolution function (currently
Resolve-IdleTemplateStringor wherever the placeholder substitution is performed):^\s*\{\{.+?\}\}\s*$object) without converting to string.Ensure this behavior is applied consistently wherever workflow templates are resolved (arrays/hashtables/nested structures).
Add unit tests for:
$falsestays$false(typed)$truestays$trueAdd a regression test for the AD CreateIdentity scenario:
$falsefor Enabled when templated.Backwards compatibility
This is a bug fix. Typed preservation only happens for pure placeholders and should not break existing string interpolation patterns.
Additional Notes / References
[bool]$Attributes['Enabled'], which is correct given a boolean input, but becomes wrong after string coercion.Environment
main/ latest release at time of report