Problem Statement
Today, executing a plan requires passing the provider registry twice:
- During planning:
New-IdlePlan -Providers $providers
- During execution:
Invoke-IdlePlan -Plan $plan -Providers $providers
If -Providers is omitted during Invoke-IdlePlan, steps can fail with errors like:
Context.Providers must be a hashtable.
This is confusing and adds unnecessary boilerplate for the most common workflow:
$providers = @{
Identity = $providerAD
AuthSessionBroker = $authSession
}
$plan = New-IdlePlan -WorkflowPath .\joiner.psd1 -Request $req -Providers $providers
$result = Invoke-IdlePlan -Plan $plan # <-- fails today unless -Providers is passed again
The expectation for a typical user is that a plan built with providers can be executed without re-supplying the same providers, unless the caller explicitly wants to override them.
Proposed Solution
Implement a deterministic fallback in the execution path:
- If
Invoke-IdlePlan / Invoke-IdlePlanObject is called without -Providers (i.e., $null)
- And the plan object contains a
Providers property that is a dictionary/hashtable
- Then execution must use
Plan.Providers as the effective provider registry.
Override rules:
- If
-Providers is supplied to Invoke-IdlePlan / Invoke-IdlePlanObject, it must take precedence over Plan.Providers.
Validation / error message rules:
- If neither
-Providers nor Plan.Providers are present/valid, execution must fail early with a clear message, e.g.:
Providers are required. Provide -Providers to Invoke-IdlePlan or build the plan with Providers.
Security rules:
- All existing security validations must continue to apply to the effective provider registry:
- ScriptBlocks must still be rejected (e.g.,
Assert-IdleNoScriptBlock).
- Providers must still be redacted at the output boundary (no provider objects leaked in result).
- The fallback must not weaken the “host supplies auth broker” design: the effective provider registry still needs
AuthSessionBroker when steps require it.
Implementation detail (explicit, agent-safe):
- Implement the fallback in IdLE.Core (not only in the thin public wrapper), so all hosts benefit.
Suggested code location:
-
src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1
-
Ensure the public entrypoint src/IdLE/Public/Invoke-IdlePlan.ps1 continues to pass through -Providers as-is; no behavior changes needed there if core is fixed, but update comment-based help accordingly.
Alternatives Considered
-
Keep current behavior (status quo)
- Pros: Explicit host injection every time.
- Cons: Duplicated configuration, confusing UX, common user error (
Context.Providers must be a hashtable.).
-
Remove -Providers from New-IdlePlan
- Pros: No more duplication.
- Cons: Planning would lose early validation / capability satisfaction checks and becomes less “fail-fast”.
-
Always ignore Plan.Providers and require explicit Invoke -Providers
- Pros: Clear separation, exported plans remain pure.
- Cons: Worse UX for the primary “build plan -> run plan” scenario.
The proposed fallback keeps the best parts of (status quo) and preserves override + export scenarios.
Impact
- User experience: Plan execution becomes simpler for the default scenario.
- Backward compatibility:
- Existing scripts that pass
-Providers to Invoke-IdlePlan are unaffected (explicit override remains).
- Existing scripts that do not pass
-Providers will now work if the plan contains Providers. This is a positive compatibility improvement.
- Plan export/import:
- Exported plans that do not include provider objects still require explicit
Invoke -Providers (unchanged).
- Security: No change intended; security checks must apply to the effective providers.
Additional Context
Documentation updates (required)
Update documentation to reflect the new default behavior and override behavior:
-
Usage docs (intro-use.md)
- Add a “Providers resolution” section:
- Default:
Invoke-IdlePlan uses Plan.Providers when -Providers is omitted.
- Override: passing
-Providers to Invoke-IdlePlan replaces Plan.Providers.
- Export scenario: exported plan usually needs
Invoke -Providers.
-
Providers docs (providers.md)
- Update examples to show the simplest flow:
- Provide
$providers once to New-IdlePlan, then Invoke-IdlePlan -Plan $plan without -Providers.
- Add a short explicit example showing override:
Invoke-IdlePlan -Plan $plan -Providers $otherProviders
-
Quickstart / workflow docs
- Search and update any snippet that shows
Invoke-IdlePlan -Providers $providers when it’s redundant.
- Keep at least one example that demonstrates passing providers at invoke time for exported plans / other hosts.
Example updates (required)
Update any shipped examples / sample snippets (wherever they are stored in the repo) to align with the new behavior:
- “Build plan with providers once; execute without repeating providers”.
- Ensure any AD/Entra/provider-specific example is consistent and still includes
AuthSessionBroker where required.
Tests (required)
Add new tests and adapt existing tests to cover both old and new paths.
-
New tests (Core)
Invoke-IdlePlanObject uses Plan.Providers when -Providers is $null.
Invoke-IdlePlanObject uses explicit -Providers over Plan.Providers.
- When neither is present: throws a clear, specific error.
-
Update existing tests
- Find tests that currently always pass
-Providers to Invoke-IdlePlanObject / Invoke-IdlePlan and:
- Keep them (to verify override path), but also
- Add/convert at least one “default path” execution test where
Invoke is called without -Providers.
- Ensure tests that validate provider redaction still pass (provider objects must not leak into results).
-
Regression test for the original failure
- Add a test reproducing the old failure mode (“Context.Providers must be a hashtable”) and verify it does not occur anymore in the default path.
Acceptance Criteria
Problem Statement
Today, executing a plan requires passing the provider registry twice:
New-IdlePlan -Providers $providersInvoke-IdlePlan -Plan $plan -Providers $providersIf
-Providersis omitted duringInvoke-IdlePlan, steps can fail with errors like:Context.Providers must be a hashtable.This is confusing and adds unnecessary boilerplate for the most common workflow:
The expectation for a typical user is that a plan built with providers can be executed without re-supplying the same providers, unless the caller explicitly wants to override them.
Proposed Solution
Implement a deterministic fallback in the execution path:
Invoke-IdlePlan/Invoke-IdlePlanObjectis called without-Providers(i.e.,$null)Providersproperty that is a dictionary/hashtablePlan.Providersas the effective provider registry.Override rules:
-Providersis supplied toInvoke-IdlePlan/Invoke-IdlePlanObject, it must take precedence overPlan.Providers.Validation / error message rules:
-ProvidersnorPlan.Providersare present/valid, execution must fail early with a clear message, e.g.:Providers are required. Provide -Providers to Invoke-IdlePlan or build the plan with Providers.Security rules:
Assert-IdleNoScriptBlock).AuthSessionBrokerwhen steps require it.Implementation detail (explicit, agent-safe):
Suggested code location:
src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1Before any provider validation or use (before
Assert-IdleNoScriptBlock -InputObject $Providers, beforeGet-IdleStepRegistry -Providers $Providers), resolve:$effectiveProviders = $Providers$effectiveProviders -eq $null:$Planhas propertyProvidersand it is anIDictionary/hashtable: set$effectiveProviders = $Plan.Providers$effectiveProvidersthroughout the function instead of$Providers.Ensure the public entrypoint
src/IdLE/Public/Invoke-IdlePlan.ps1continues to pass through-Providersas-is; no behavior changes needed there if core is fixed, but update comment-based help accordingly.Alternatives Considered
Keep current behavior (status quo)
Context.Providers must be a hashtable.).Remove
-ProvidersfromNew-IdlePlanAlways ignore
Plan.Providersand require explicitInvoke -ProvidersThe proposed fallback keeps the best parts of (status quo) and preserves override + export scenarios.
Impact
-ProviderstoInvoke-IdlePlanare unaffected (explicit override remains).-Providerswill now work if the plan containsProviders. This is a positive compatibility improvement.Invoke -Providers(unchanged).Additional Context
Documentation updates (required)
Update documentation to reflect the new default behavior and override behavior:
Usage docs (
intro-use.md)Invoke-IdlePlanusesPlan.Providerswhen-Providersis omitted.-ProviderstoInvoke-IdlePlanreplacesPlan.Providers.Invoke -Providers.Providers docs (
providers.md)$providersonce toNew-IdlePlan, thenInvoke-IdlePlan -Plan $planwithout-Providers.Invoke-IdlePlan -Plan $plan -Providers $otherProvidersQuickstart / workflow docs
Invoke-IdlePlan -Providers $providerswhen it’s redundant.Example updates (required)
Update any shipped examples / sample snippets (wherever they are stored in the repo) to align with the new behavior:
AuthSessionBrokerwhere required.Tests (required)
Add new tests and adapt existing tests to cover both old and new paths.
New tests (Core)
Invoke-IdlePlanObjectusesPlan.Providerswhen-Providersis$null.Invoke-IdlePlanObjectuses explicit-ProvidersoverPlan.Providers.Update existing tests
-ProviderstoInvoke-IdlePlanObject/Invoke-IdlePlanand:Invokeis called without-Providers.Regression test for the original failure
Acceptance Criteria
Invoke-IdlePlan -Plan $plansucceeds when$plan.Providersis a hashtable/dictionary and steps can execute with it.Invoke-IdlePlan -Providers $providersoverridesPlan.Providers.-ProvidersnorPlan.Providersexist/valid, execution fails early with a clear error message (no deep step failure).intro-use.md,providers.md, and at least one quickstart/workflow page) updated accordingly.- default path, override path, missing providers path, plus regression coverage.