Skip to content

Invoke-IdlePlan should default to Plan.Providers when -Providers is not supplied #150

@blindzero

Description

@blindzero

Problem Statement

Today, executing a plan requires passing the provider registry twice:

  1. During planning:
    • New-IdlePlan -Providers $providers
  2. 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

    • Before any provider validation or use (before Assert-IdleNoScriptBlock -InputObject $Providers, before Get-IdleStepRegistry -Providers $Providers), resolve:

      1. $effectiveProviders = $Providers
      2. If $effectiveProviders -eq $null:
        • If $Plan has property Providers and it is an IDictionary / hashtable: set $effectiveProviders = $Plan.Providers
      3. Use $effectiveProviders throughout the function instead of $Providers.
  • 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

  1. 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.).
  2. Remove -Providers from New-IdlePlan

    • Pros: No more duplication.
    • Cons: Planning would lose early validation / capability satisfaction checks and becomes less “fail-fast”.
  3. 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:

  1. 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.
  2. 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
  3. 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.

  1. 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.
  2. 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).
  3. 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

  • Invoke-IdlePlan -Plan $plan succeeds when $plan.Providers is a hashtable/dictionary and steps can execute with it.
  • Explicit Invoke-IdlePlan -Providers $providers overrides Plan.Providers.
  • If neither -Providers nor Plan.Providers exist/valid, execution fails early with a clear error message (no deep step failure).
  • Existing security validations (no ScriptBlocks; provider redaction in results) still apply to the effective providers.
  • Documentation (intro-use.md, providers.md, and at least one quickstart/workflow page) updated accordingly.
  • Examples updated accordingly (no redundant provider passing in the default path).
  • Pester tests updated and expanded:
    - default path, override path, missing providers path, plus regression coverage.

Metadata

Metadata

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions