Problem Statement
Several production files remain very large and still mix responsibilities, which makes reviews and maintenance harder and increases merge conflict risk.
E.g.
src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1 (~692 LOC)
src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1 (~873 LOC)
src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1 (~782 LOC)
src/IdLE.Core/Public/New-IdlePlanObject.ps1 (~671 LOC)
The repo already contains Private/ folders in the affected modules, but they are not yet used for the majority of the logic in the files above. Other additional best-practice oriented folders may be introduced but require confirmation from owner (e.g. Classes)
Non-Goals
- No functional changes to workflows or step execution behavior.
- No public contract changes (cmdlet parameters, outputs, exported functions).
- No new cross-module “core contracts” unless strictly required (avoid scope creep).
- No new folder taxonomy (e.g. no
Internal/ convention introduced here).
Authoritative Decisions
A) Output improvements are allowed
Improving warning/error messages (clarity, actionability) is allowed and does not count as a behavior change in this issue.
Not allowed: structural or functional behavior changes (e.g., changing when an exception is thrown in a way that alters execution paths).
B) Helper granularity
Splitting is guided by cohesive / thematic responsibilities, not LOC thresholds.
C) Provider IO boundary
IO stays in the Adapter.
- Provider factories remain “facades”.
- Provider script methods call adapter methods for IO.
Private/ helpers are used for mapping, prereq checks, and small logic utilities that are not IO.
D) Prerequisites policy (required vs optional)
This issue introduces a consistent structure for prerequisites checks to avoid accidental timing/throw changes during refactor.
Rules:
- Module import MUST remain non-blocking.
- Provider creation (
New-Idle*IdentityProvider) MUST NOT throw due to missing prerequisites.
- It SHOULD call
Test-Idle<Provider>Prerequisites and emit warnings.
- Provider methods that require missing prerequisites MUST throw a clear, actionable error when invoked (fail-fast at call time).
- “Required vs optional” is defined per operation:
- Required: missing → operation cannot succeed → method must throw.
- Optional: missing → operation may still succeed, but may degrade features/performance; warning is allowed.
Minimal required list (default adapters):
- AD provider: ActiveDirectory (RSAT) module is required for any operation that uses AD cmdlets via the default adapter.
- EntraID provider: Graph client dependency used by the default adapter is required for any operation that performs Graph calls.
- Exact module set depends on current adapter implementation; the prereq helper must reflect the real default adapter requirements.
E) Prerequisites result shape
Test-Idle<Provider>Prerequisites MUST:
- NOT throw
- Return a structured PSCustomObject (provider-internal; no new core contract required)
Fields:
PSTypeName: IdLE.PrerequisitesResult
ProviderName (string)
IsHealthy (bool)
MissingRequired (string[])
MissingOptional (string[])
Notes (string[])
CheckedAt (datetime)
Proposed Refactor Patterns (normative)
Provider refactor pattern
Keep New-Idle*IdentityProvider responsible only for:
- Parameter validation (data-only, no IO)
- Creating/wiring the adapter (dependency injection stays supported)
- Constructing the provider object and attaching script methods
- Calling
Test-Idle<Provider>Prerequisites and emitting warnings (no throw)
Move implementation details into Private/ helpers:
- Mapping / conversion
ConvertTo-Idle<Provider>* helpers
- Prerequisites checks
Test-Idle<Provider>Prerequisites.ps1 as described above
- No IO in private helpers for this issue (IO stays in adapter)
Core refactor pattern
- Keep public function signatures and outputs identical.
- Replace nested helper functions inside public functions with calls to
src/IdLE.Core/Private/* helpers.
- Prefer extracting cohesive blocks into new
Private/ functions (e.g., result construction, provider bag normalization, step invocation wrapper).
Implementation Tasks
1) AD provider: facade + helpers
Target:
src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1
Create new Private/ helpers (names may be adjusted but must follow verb-noun and be consistent):
Test-IdleADPrerequisites.ps1
ConvertTo-IdleADEntitlement.ps1 (extract entitlement parsing/validation)
- Additional
ConvertTo-* helpers for identity/address parsing if needed
Refactor rules:
- Public factory remains the only public entrypoint; helpers are not exported.
- Keep dependency injection:
-Adapter continues to work.
- Ensure IO remains in the adapter.
- On provider creation, call
Test-IdleADPrerequisites and Write-Warning for missing prereqs (no throw).
2) EntraID provider: facade + helpers
Target:
src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1
Create new Private/ helpers:
Test-IdleEntraIDPrerequisites.ps1
ConvertTo-IdleEntraIDEntitlement.ps1 (and other mapping helpers as needed)
Refactor rules mirror the AD provider rules, including: IO remains in adapter.
3) Core: extract helpers from large public files
Targets:
src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1
src/IdLE.Core/Public/New-IdlePlanObject.ps1
Tasks:
- Identify nested helper functions and cohesive internal blocks.
- Extract them into
src/IdLE.Core/Private/ functions (new files as needed).
- Replace inline implementations with calls to the private helpers.
- Ensure outputs, events, and error behavior remain functionally identical (message improvements allowed).
4) Tests
- Keep existing tests green.
- Add targeted tests only if needed to protect against regressions from extraction.
- Prefer coverage gains without duplicate assertions.
Quality Gates / Definition of Done
Problem Statement
Several production files remain very large and still mix responsibilities, which makes reviews and maintenance harder and increases merge conflict risk.
E.g.
src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1(~692 LOC)src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1(~873 LOC)src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1(~782 LOC)src/IdLE.Core/Public/New-IdlePlanObject.ps1(~671 LOC)The repo already contains
Private/folders in the affected modules, but they are not yet used for the majority of the logic in the files above. Other additional best-practice oriented folders may be introduced but require confirmation from owner (e.g.Classes)Non-Goals
Internal/convention introduced here).Authoritative Decisions
A) Output improvements are allowed
Improving warning/error messages (clarity, actionability) is allowed and does not count as a behavior change in this issue.
Not allowed: structural or functional behavior changes (e.g., changing when an exception is thrown in a way that alters execution paths).
B) Helper granularity
Splitting is guided by cohesive / thematic responsibilities, not LOC thresholds.
C) Provider IO boundary
IO stays in the Adapter.
Private/helpers are used for mapping, prereq checks, and small logic utilities that are not IO.D) Prerequisites policy (required vs optional)
This issue introduces a consistent structure for prerequisites checks to avoid accidental timing/throw changes during refactor.
Rules:
New-Idle*IdentityProvider) MUST NOT throw due to missing prerequisites.Test-Idle<Provider>Prerequisitesand emit warnings.Minimal required list (default adapters):
E) Prerequisites result shape
Test-Idle<Provider>PrerequisitesMUST:Fields:
PSTypeName:IdLE.PrerequisitesResultProviderName(string)IsHealthy(bool)MissingRequired(string[])MissingOptional(string[])Notes(string[])CheckedAt(datetime)Proposed Refactor Patterns (normative)
Provider refactor pattern
Keep
New-Idle*IdentityProviderresponsible only for:Test-Idle<Provider>Prerequisitesand emitting warnings (no throw)Move implementation details into
Private/helpers:ConvertTo-Idle<Provider>*helpersTest-Idle<Provider>Prerequisites.ps1as described aboveCore refactor pattern
src/IdLE.Core/Private/*helpers.Private/functions (e.g., result construction, provider bag normalization, step invocation wrapper).Implementation Tasks
1) AD provider: facade + helpers
Target:
src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1Create new
Private/helpers (names may be adjusted but must follow verb-noun and be consistent):Test-IdleADPrerequisites.ps1ConvertTo-IdleADEntitlement.ps1(extract entitlement parsing/validation)ConvertTo-*helpers for identity/address parsing if neededRefactor rules:
-Adaptercontinues to work.Test-IdleADPrerequisitesandWrite-Warningfor missing prereqs (no throw).2) EntraID provider: facade + helpers
Target:
src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1Create new
Private/helpers:Test-IdleEntraIDPrerequisites.ps1ConvertTo-IdleEntraIDEntitlement.ps1(and other mapping helpers as needed)Refactor rules mirror the AD provider rules, including: IO remains in adapter.
3) Core: extract helpers from large public files
Targets:
src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1src/IdLE.Core/Public/New-IdlePlanObject.ps1Tasks:
src/IdLE.Core/Private/functions (new files as needed).4) Tests
Quality Gates / Definition of Done
Private/helpers (no IO in helpers)Private/helpers