From c75bb659ac52424fb8c13050cc3bcd9bcd35bc81 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 19:18:20 +0100 Subject: [PATCH 01/25] review about - intro --- docs/about/intro.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/about/intro.md b/docs/about/intro.md index 16ab231a..be0eb29a 100644 --- a/docs/about/intro.md +++ b/docs/about/intro.md @@ -16,17 +16,27 @@ The key idea is to **separate intent from implementation**: - **What** should happen is defined in a **workflow** (data-only configuration). - **How** it happens is implemented by **steps** and **providers** (pluggable modules). + - While **steps** define by StepTypes, which provider-agnostic **capabilities** are required to perform a workflow step + - **providers** register to the core and announce the provided **capabilities** and implement the vendor system specific interface --- ## Why IdLE exists +JML (joiner/mover/leavers) processes are + +- error prune, especially if performed manually +- time consuming and therefore +- quite annoying for operators + Identity lifecycle automation often turns into long scripts that are: - tightly coupled to one environment - hard to test - hard to change safely +Identity Management Systems (IdMS) on the other side are whether complex or expensive (or both of it) and then often do not care about supplementary systems that also need to be covered within the workflows. + IdLE aims to be: - **portable** run in different environments with PowerShell 7+ without a hard dependency on a specific host or UI From 65dc2ec3434e3dd87bf6ed0a79938037b8e92838 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:17:18 +0100 Subject: [PATCH 02/25] removing architecture --- .github/copilot-instructions.md | 5 +- AGENTS.md | 10 +- CONTRIBUTING.md | 9 +- README.md | 2 +- docs/about/architecture.md | 181 ------------------- docs/about/concepts.md | 160 +++++++++++++--- docs/about/intro.md | 9 - docs/extend/events.md | 9 - docs/extend/extensibility.md | 29 ++- docs/extend/providers.md | 10 - docs/extend/steps.md | 11 -- docs/index.md | 1 - docs/reference/capabilities.md | 2 +- docs/reference/providers/provider-ad.md | 12 -- docs/reference/providers/provider-entraID.md | 7 - docs/use/configuration.md | 10 - docs/use/quickstart.md | 10 - docs/use/steps.md | 6 - website/sidebars.js | 1 - 19 files changed, 175 insertions(+), 309 deletions(-) delete mode 100644 docs/about/architecture.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8f2a7a74..7eea0977 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -9,8 +9,9 @@ Use these instructions when suggesting or generating changes in this repo (chat, - Agent operating manual: `AGENTS.md` - Coding and documentation rules: `STYLEGUIDE.md` - Contributor workflow + Definition of Done: `CONTRIBUTING.md` -- Architecture: `docs/advanced/architecture.md` -- Security + trust boundaries: `docs/advanced/security.md` +- Concepts: `docs/about/concepts.md` +- Security + trust boundaries: `docs/about/security.md` +- Extensibiltiy: `docs/extend/extensibility.md` If anything in this file conflicts with those, the more specific document wins. diff --git a/AGENTS.md b/AGENTS.md index 972112e4..c60217e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,6 @@ Authoritative docs: - `README.md` (high-level) - `docs/index.md` (documentation entry point) -- `docs/advanced/architecture.md` (architecture decisions) - `docs/advanced/security.md` (trust boundaries) - `docs/advanced/provider-capabilities.md` (Capability rules) - `docs/reference/providers-and-contracts.md` (Provider contracts) @@ -161,8 +160,8 @@ Before proposing or finalizing a PR, ensure: - [ ] Public APIs have comment-based help - [ ] Docs updated where needed (`README.md`, `docs/`, `examples/`) - [ ] Generated docs regenerated if required (`docs/reference/*`) -- [ ] No architecture rules violated (`docs/advanced/architecture.md`) -- [ ] No security boundary regressions (`docs/advanced/security.md`) +- [ ] No concept or extensibility rules violated (`docs/about/concepts.md`, `docs/extend/extensibility.md`) +- [ ] No security boundary regressions (`docs/about/security.md`) --- @@ -171,8 +170,9 @@ Before proposing or finalizing a PR, ensure: - General, cross-cutting agent rules → `AGENTS.md` (repo root) - Code style details → `STYLEGUIDE.md` - Contributor workflow and DoD → `CONTRIBUTING.md` -- Architecture decisions → `docs/advanced/architecture.md` -- Security boundaries → `docs/advanced/security.md` +- Concept decisions → `docs/about/concepts.md` +- Extensibility decisions → `docs/extend/extensibility.md` +- Security boundaries → `docs/about/security.md` --- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60d4bc60..1344755c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -125,7 +125,8 @@ A contribution is complete when: - all tests pass (`pwsh -NoProfile -File ./tools/Invoke-IdlePesterTests.ps1`) - static analysis passes (`pwsh -NoProfile -File ./tools/Invoke-IdleScriptAnalyzer.ps1`) -- no architecture rules are violated (see `docs/advanced/architecture.md`) +- no concept rules are violated (see `docs/about/concepts.md`) +- no security or trust boundaries are violated (see `docs/about/security.md`) - public APIs are documented (comment-based help for exported functions) - documentation is updated where required: - README.md (only high-level overview + pointers) @@ -300,12 +301,14 @@ Repository maintainers should configure branch protection so that required statu Keep docs short and linkable: - README.md: landing page (what/why + 30s quickstart + links) -- docs/: architecture, usage, examples (small focused pages) +- docs/: concepts, usage, examples (small focused pages) - examples/: runnable scripts and workflow samples Key links: -- Architecture: `docs/about/architecture.md` +- Concepts: `docs/about/concepts.md` +- Security: `docs/about/security.md` +- Extensibility: `docs/extend/extensibility.md` - Examples: `examples/README.md` - Coding & in-code documentation rules: `STYLEGUIDE.md` diff --git a/README.md b/README.md index c4159473..f243b90d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ IdLE aims to be: - **configuration-driven** (workflows as data) - **extensible** (add custom steps and providers) -For a complete overview of concepts and architecture, see **[About > Concepts](docs/about/concepts.md)**. +For a complete overview of concepts, see **[About > Concepts](docs/about/concepts.md)**. --- diff --git a/docs/about/architecture.md b/docs/about/architecture.md deleted file mode 100644 index de2d4bd3..00000000 --- a/docs/about/architecture.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -title: Architecture -sidebar_labels: Architecture ---- - -# Architecture - -This page summarizes the core architecture decisions for IdLE. - -## Goals - -- Generic, configurable lifecycle orchestration (Joiner / Mover / Leaver) -- Portable, modular, testable -- Headless core (works in CLI, service, CI) -- Plan-first execution with structured events - -## Non-goals (V1) - -- No UI framework or service host inside IdLE.Core -- No dynamic code execution from configuration -- No automatic rollback orchestration -- No deep merge semantics for state outputs - -## Plan → Execute - -IdLE splits orchestration into two phases. - -### Plan - -IdLE builds a deterministic execution plan before any step is executed. -During this planning phase, the engine validates structural correctness, -conditions, and execution prerequisites. - -- evaluates declarative conditions -- validates inputs and references -- produces data-only actions -- captures a **data-only request intent snapshot** (e.g. IdentityKeys / DesiredState / Changes) for auditing and export - -#### Provider Capabilities (Planning-time Validation) - -IdLE uses a **capability-based provider model** to validate execution -prerequisites during plan build. - -Steps may declare required capabilities, while providers explicitly -advertise which capabilities they support. The engine matches both sides -and fails fast if required functionality is missing. - -For details on the capability-based provider model and the validation flow, -see [Provider Capabilities](../reference/capabilities.md). - -### Execute - -Execution runs the plan exactly as built: - -- no re-planning -- no re-evaluation of conditions - -This enables previews, approvals, and repeatable audits. - -## Plan export - -Hosts may persist or exchange a plan as a **machine-readable JSON artifact**. -The canonical contract format is defined here: - -- [Plan export specification (JSON)](../reference/specs/plan-export.md) - -The exported artifact is intended for **approvals, CI checks, and audits**. -To keep exports deterministic and review-friendly, the contract intentionally omits volatile information -such as engine build versions and timestamps. When required, hosts SHOULD attach build/time metadata -outside the exported plan artifact. - -Because IdLE separates planning from execution, the plan retains a **request intent snapshot** so that -exports can include `request.input` even after the original request object is no longer available. - -## Declarative conditions - -Conditions are data-only objects. -They are validated early and evaluated deterministically. - -## Eventing - -IdLE emits **structured events** during execution. - -- The engine always creates an `EventSink` and exposes it as `Context.EventSink`. -- Steps and the engine use a single contract: `Context.EventSink.WriteEvent(Type, Message, StepName, Data)`. -- All events are buffered in the execution result (`result.Events`). - -Hosts may optionally provide an external sink to stream events live: - -- `Invoke-IdlePlan -EventSink <object>` -- The sink must implement `WriteEvent(event)` -- ScriptBlock sinks are rejected (secure default) - -## State ownership - -Steps may only write to `State.*` and only to declared output paths. -No deep merge: replace-at-path semantics only. - -## Extensibility - -- Workflows describe intent -- Steps implement behaviors -- Providers integrate target systems - -See: [Extensibility](../extend/extensibility.md). - -## Trust boundaries - -IdLE treats workflow configuration and lifecycle requests as **untrusted data** and validates that they contain no ScriptBlocks. - -Host-provided extension points (step registry, providers, external event sinks) are **trusted inputs** and are validated for safe shapes (object contracts). For details, see `advanced/security.md`. - -## v1.0 Public API and Contracts - -### Supported Commands - -The **supported public API** for v1.0 consists of the following commands exported from the IdLE meta-module: - -- `Test-IdleWorkflow` -- `New-IdleLifecycleRequest` -- `New-IdlePlan` -- `Invoke-IdlePlan` -- `Export-IdlePlan` -- `New-IdleAuthSession` - -**Source of truth**: `src/IdLE/IdLE.psd1` → `FunctionsToExport` - -Only these commands are considered **stable contracts**. Internal modules (IdLE.Core, IdLE.Steps.*, IdLE.Provider.*) are unsupported when imported directly. - -### Command Contracts - -For supported commands, the following are **stable contracts** (breaking changes require a major version): - -- Command name -- Parameter names and parameter sets -- Observable semantics (mandatory/optional/default behavior) -- Output type identity at a coarse level (PSTypeName) - -The following are **not contracts** and may change in minor/patch versions: - -- Exact error message strings -- Undocumented internal object properties -- Internal module cmdlets - -### Data Contracts - -**Workflow authoring contract** (PSD1): -- Format: PSD1 workflow definitions validated by `Test-IdleWorkflow` -- Unknown keys: **FAIL** (strict validation) -- Required fields (Name, LifecycleEvent, Steps[].Name, Steps[].Type): **FAIL** if null/empty -- `With` payload values: allow `null` and empty strings (supports "clear attribute" scenarios) - -**Lifecycle request contract**: -- Required fields: `LifecycleEvent`, `CorrelationId` -- Optional fields: `Actor`, `IdentityKeys`, `DesiredState`, `Changes` - -**Plan export contract** (JSON): -- Format: JSON from `Export-IdlePlan` -- Schema and semantics are stable -- See [Plan export specification](../reference/specs/plan-export.md) - -### Capability ID Baseline (v1.0) - -The following capability IDs are frozen as the v1.0 baseline: - -- `IdLE.DirectorySync.Status` - Read directory sync status -- `IdLE.DirectorySync.Trigger` - Trigger directory sync -- `IdLE.Entitlement.Grant` - Grant group membership/entitlement -- `IdLE.Entitlement.List` - List user entitlements -- `IdLE.Entitlement.Revoke` - Revoke group membership/entitlement -- `IdLE.Identity.Attribute.Ensure` - Ensure identity attribute value -- `IdLE.Identity.Create` - Create identity -- `IdLE.Identity.Delete` - Delete identity -- `IdLE.Identity.Disable` - Disable identity -- `IdLE.Identity.Enable` - Enable identity -- `IdLE.Identity.Move` - Move identity (OU/container) -- `IdLE.Mailbox.Info.Read` - Read mailbox metadata/configuration -- `IdLE.Mailbox.OutOfOffice.Ensure` - Ensure Out of Office configuration -- `IdLE.Mailbox.Type.Ensure` - Ensure mailbox type (User/Shared/etc.) - -**Deprecated (pre-1.0)**: `IdLE.Mailbox.Read` → automatically mapped to `IdLE.Mailbox.Info.Read` with deprecation warning during planning. diff --git a/docs/about/concepts.md b/docs/about/concepts.md index 9151db3e..f4471177 100644 --- a/docs/about/concepts.md +++ b/docs/about/concepts.md @@ -5,32 +5,70 @@ sidebar_label: Concepts # IdLE Concepts -## Request +IdLE stays headless and avoids responsibilities that belong to a host application. -A **LifecycleRequest** represents the business intent (for example: Joiner, Mover, Leaver). -It is the input to planning. +## Goals -## Plan +- Generic, configurable lifecycle orchestration (Joiner / Mover / Leaver) +- Portable, modular, testable +- Headless core (works in CLI, service, CI) +- Plan-first execution with structured events -A **LifecyclePlan** is created deterministically from: +## Non-goals -- request -- workflow definition -- step catalog / step registry +- No UI framework or service host +- no interactive prompts +- no authentication flows inside steps +- No dynamic code execution from configuration +- No automatic rollback orchestration +- No deep merge semantics for state outputs -The plan is previewable and auditable. +--- -## Execute +IdLE consists of the following elements and components: -Execution runs **only the plan** (no re-planning). This supports: +## Request -- approvals -- repeatability -- deterministic audits +A **LifecycleRequest** represents the business intent (for example: Joiner, Mover, Leaver). It is the input to planning. + +```powershell +$Request = New-IdleLifecycleRequest -LifecycleEvent 'Joiner' -IdentityKeys @{ + key = 'first.last' +} +-DesiredState @{ + Firstname = 'First' + Lastname = 'Last' + Mail = 'First.Last@domain.tld' +} +``` --- -## Building Blocks +## Workflow + +Workflows are **data-only configuration files** (PSD1) describing which steps should run for a lifecycle event. +To enable larger flexibility, you can use placeholders instead of literals to be substituted with data from request. + +```powershell +@{ + Name = 'Joiner - Workflow Workflow' + LifecycleEvent = 'Joiner' + Steps = @( + @{ + Name = 'Step Name' + Type = 'IdLE.Step.StepType' + With = @{ + # Passed with values from Request Data + IdentityKey = '{{Request.IdentityKeys.key}}' + Attributes = @{ + GivenName = 'Firstname' + # Passed with values from Request Data + Surname = '{{Request.DesiredState.Lastname}}' + } + Provider = 'IdentityProvider' + } + } +``` ### Steps @@ -39,6 +77,10 @@ Execution runs **only the plan** (no re-planning). This supports: - Operate idempotently (converge towards desired state) - Are provider-agnostic (use contracts, not direct system calls) - Emit structured events for audit and progress +- Define the capabilities a provider has to supply to be eligible to perform a step + +Steps may only write to `State.*` and only to declared output paths. +No deep merge: replace-at-path semantics only. Learn more: [Steps](../use/steps.md) | [Step Catalog](../reference/steps.md) @@ -52,22 +94,88 @@ Learn more: [Steps](../use/steps.md) | [Step Catalog](../reference/steps.md) Learn more: [Providers](../use/providers.md) | [Providers and Contracts](../extend/providers.md) +### Declarative conditions + +Steps can contain conditions which are data-only objects. +They are validated early and evaluated deterministically. + +```powershell +Condition = @{ + Equals = @{ + Path = 'Plan.LifecycleEvent' + Value = 'Joiner' + } +} +``` + --- -## Non-goals (V1) +## Plan -IdLE.Core stays headless and avoids responsibilities that belong to a host application: +IdLE builds a **deterministic execution plan** before any step is executed. -- no UI framework -- no interactive prompts -- no authentication flows inside steps -- no dynamic code execution from configuration +The plan is created deterministically from: + +- request data +- workflow definition +- step catalog / step registry +- authentication session informations + +During this planning phase, the engine validates structural correctness, +conditions, and execution prerequisites. + +- evaluates declarative conditions +- validates inputs and references +- produces data-only actions +- captures a **data-only request intent snapshot** (e.g. IdentityKeys / DesiredState / Changes) for auditing and export + +### Provider Capabilities (Planning-time Validation) + +IdLE uses a **capability-based provider model** to validate execution +prerequisites during plan build. + +Steps may declare required capabilities, while providers explicitly +advertise which capabilities they support. The engine matches both sides +and fails fast if required functionality is missing. + +For details on the capability-based provider model and the validation flow, +see [Provider Capabilities](../reference/capabilities.md). + +### Plan export + +Hosts may persist or exchange a plan as a **machine-readable JSON artifact**. +The canonical contract format is defined here: + +- [Plan export specification (JSON)](../reference/specs/plan-export.md) + +The exported artifact is intended for **approvals, CI checks, and audits**. +To keep exports deterministic and review-friendly, the contract intentionally omits volatile information +such as engine build versions and timestamps. When required, hosts SHOULD attach build/time metadata +outside the exported plan artifact. + +Because IdLE separates planning from execution, the plan retains a **request intent snapshot** so that +exports can include `request.input` even after the original request object is no longer available. --- -## Next Steps +## Execute + +Execution runs **only the plan** (no re-planning). This supports: + +- approvals +- repeatability +- deterministic audits + +### Eventing + +IdLE emits **structured events** during execution. + +- The engine always creates an `EventSink` and exposes it as `Context.EventSink`. +- Steps and the engine use a single contract: `Context.EventSink.WriteEvent(Type, Message, StepName, Data)`. +- All events are buffered in the execution result (`result.Events`). + +Hosts may optionally provide an external sink to stream events live: -- [Installation](../use/installation.md) — Install and import guide -- [Quickstart](../use/quickstart.md) — Run the demo -- [Architecture](../about/architecture.md) — Design principles and decisions -- [Workflows](../use/workflows.md) — Define lifecycle workflows +- `Invoke-IdlePlan -EventSink ` +- The sink must implement `WriteEvent(event)` +- ScriptBlock sinks are rejected (secure default) diff --git a/docs/about/intro.md b/docs/about/intro.md index be0eb29a..eb9bfd24 100644 --- a/docs/about/intro.md +++ b/docs/about/intro.md @@ -54,12 +54,3 @@ IdLE aims to be: - **Provider/Adapter pattern** (directory, SaaS, REST, file/mock…) - **Structured events** for audit/progress (CorrelationId, Actor, step results) - **Idempotent execution** (steps can be written to converge state) - ---- - -## Where to go next - -- [Concepts](concepts.md): more details on the core concepts of IdLE. -- [Use](../use/intro.md): install IdLE, run workflows, export plans, troubleshoot. -- [Extend](../extend/intro.md): implement providers and steps, integrate with secrets and events. -- [Reference](../reference/intro.md): cmdlets, steps, capabilities, and specifications. diff --git a/docs/extend/events.md b/docs/extend/events.md index c7616fc9..320944f0 100644 --- a/docs/extend/events.md +++ b/docs/extend/events.md @@ -213,12 +213,3 @@ The engine must never: - assume interactive execution Violating this separation would break testability and portability. - ---- - -## Related documentation - -- [Workflows](../use/workflows.md) -- [Steps](../use/steps.md) -- [Providers](../use/providers.md) -- [Architecture](../about/architecture.md) diff --git a/docs/extend/extensibility.md b/docs/extend/extensibility.md index 83786d5c..7ee6ef1b 100644 --- a/docs/extend/extensibility.md +++ b/docs/extend/extensibility.md @@ -108,8 +108,29 @@ supports it. --- -## Related +## Command Contracts -- [Providers and Contracts](../extend/providers.md) — Provider extension guidance -- [Provider Capabilities](../reference/capabilities.md) — Capability system -- [Architecture](../about/architecture.md) — Design principles +For supported commands, the following are **stable contracts** (breaking changes require a major version): + +- Command name +- Parameter names and parameter sets +- Observable semantics (mandatory/optional/default behavior) +- Output type identity at a coarse level (PSTypeName) + +The following are **not contracts** and may change in minor/patch versions: + +- Exact error message strings +- Undocumented internal object properties +- Internal module cmdlets + +## Data Contracts + +**Workflow authoring contract** (PSD1): +- Format: PSD1 workflow definitions validated by `Test-IdleWorkflow` +- Unknown keys: **FAIL** (strict validation) +- Required fields (Name, LifecycleEvent, Steps[].Name, Steps[].Type): **FAIL** if null/empty +- `With` payload values: allow `null` and empty strings (supports "clear attribute" scenarios) + +**Lifecycle request contract**: +- Required fields: `LifecycleEvent`, `CorrelationId` +- Optional fields: `Actor`, `IdentityKeys`, `DesiredState`, `Changes` diff --git a/docs/extend/providers.md b/docs/extend/providers.md index 1f05c527..124944e5 100644 --- a/docs/extend/providers.md +++ b/docs/extend/providers.md @@ -337,13 +337,3 @@ Steps must never rely on provider-specific behavior beyond the documented contra If a step requires provider-specific functionality, the contract itself should be clarified or refined. - ---- - -## Related documentation - -- [Workflows](../use/workflows.md) -- [Steps](../use/steps.md) -- [Architecture](../about/architecture.md) -- [Extensibility](../extend/extensibility.md) -- [Capabilities](../reference/capabilities.md) diff --git a/docs/extend/steps.md b/docs/extend/steps.md index 461cf555..70fbcf9f 100644 --- a/docs/extend/steps.md +++ b/docs/extend/steps.md @@ -187,14 +187,3 @@ Undocumented behavior leads to: - broken expectations Metadata is not optional; it is part of the step contract. - ---- - -## Related documentation - -- [Architecture](../about/architecture.md) -- [Workflows](../use/workflows.md) -- [Configuration](../use/configuration.md) -- [Providers](providers.md) -- [Events and Observability](events.md) -- [Steps Catalog](../reference/steps.md) diff --git a/docs/index.md b/docs/index.md index 3a8e369d..fb7decc2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,7 +16,6 @@ for identity and account processes (Joiner / Mover / Leaver) built for **PowerSh Learn the basics [about IdLE](about/intro.md) - [Concepts](about/concepts.md) - What are the basic concepts of IdLE and how it works in general -- [Architecture](about/architecture.md) - Design principles and decisions - [Security](about/security.md) - Trust boundaries and threat model ## Use IdLE diff --git a/docs/reference/capabilities.md b/docs/reference/capabilities.md index 82d8ca75..0226573f 100644 --- a/docs/reference/capabilities.md +++ b/docs/reference/capabilities.md @@ -9,7 +9,7 @@ Capabilities are the contract between **Steps**, **Providers**, and the **Planni > Note: IdLE is still pre-1.0. Capability IDs may evolve. This document should be treated as the source of truth for IDs once stabilized. For the conceptual model (why capabilities exist, how discovery/merging works, and how validation behaves), see -- [About -> Architecture](../about/architecture.md) +- [About -> Concepts](../about/concepts.md) - [Extend -> Extensibility](../extend/extensibility.md) - [Extend -> Providers](../extend/providers.md) diff --git a/docs/reference/providers/provider-ad.md b/docs/reference/providers/provider-ad.md index c323d9aa..998654f7 100644 --- a/docs/reference/providers/provider-ad.md +++ b/docs/reference/providers/provider-ad.md @@ -410,15 +410,3 @@ The AD provider uses an internal adapter layer (`New-IdleADAdapter`) that isolat ### Capability-Driven Design The provider implements `GetCapabilities()` and announces all supported capabilities. The engine validates capabilities at plan-time before execution, enabling fail-fast behavior. - ---- - -## Related Documentation - -- [Providers and Contracts](../../extend/providers.md) - Extend providers -- [Steps and Metadata](../../extend/steps) - Extend steps -- [Capabilities Reference](../capabilities.md) - Capability naming and validation -- [Steps Catalog](../steps.md) - Steps Reference -- [Architecture](../../about/architecture.md) - IdLE architecture principles -- [Security Model](../../about/security.md) - Trust boundaries and security considerations - diff --git a/docs/reference/providers/provider-entraID.md b/docs/reference/providers/provider-entraID.md index 4e5f000a..a0b64ed8 100644 --- a/docs/reference/providers/provider-entraID.md +++ b/docs/reference/providers/provider-entraID.md @@ -337,10 +337,3 @@ Microsoft Graph enforces rate limits. The provider marks these as transient erro ### "Insufficient permissions" Verify the access token has the required Graph API permissions (see Required Permissions section). - -## See Also - -- [IdLE Architecture](../../about/architecture.md) -- [AuthSessionBroker Pattern](../../about/security.md) -- [Example Workflows](../../../examples/README.md) -- [Invoke-IdleDemo.ps1](../../../examples/Invoke-IdleDemo.ps1) diff --git a/docs/use/configuration.md b/docs/use/configuration.md index 6ed21044..875deb4e 100644 --- a/docs/use/configuration.md +++ b/docs/use/configuration.md @@ -204,13 +204,3 @@ Trying to solve every problem with configuration often results in overly complex workflows. Prefer small, focused steps with clear parameters. - ---- - -## Related documentation - -- [Workflows](../use/workflows.md) -- [Steps](../use/steps.md) -- [Providers and Contracts](../extend/providers.md) -- [Events and Observability](../extend/events.md) -- [Architecture](../about/architecture.md) diff --git a/docs/use/quickstart.md b/docs/use/quickstart.md index e6c49a41..cc08f306 100644 --- a/docs/use/quickstart.md +++ b/docs/use/quickstart.md @@ -140,13 +140,3 @@ $providers = @{ ``` During planning, IdLE validates prerequisites and fails early if required capabilities are missing. - ---- - -## Next steps - -- Learn the [Concepts](../about/concepts.md) -- Read about [Workflows](../use/workflows.md) -- Explore [Steps](../use/steps.md) -- Review available [Providers](../use/providers.md) -- See [Architecture](../about/architecture.md) diff --git a/docs/use/steps.md b/docs/use/steps.md index d7495f8d..d8607b87 100644 --- a/docs/use/steps.md +++ b/docs/use/steps.md @@ -113,12 +113,6 @@ IdLE ships with a small set of built-in steps to keep demos and tests frictionle - **IdLE.Step.EnsureAttribute**: converges an identity attribute to the desired value using `With.IdentityKey`, `With.Name`, and `With.Value`. Requires a provider with `EnsureAttribute` and usually the `IdLE.Identity.Attribute.Ensure` capability. - **IdLE.Step.EnsureEntitlement**: converges an entitlement assignment to `Present` or `Absent` using `With.IdentityKey`, `With.Entitlement` (Kind + Id + optional DisplayName), `With.State`, and optional `With.Provider` (default `Identity`). Requires provider methods `ListEntitlements` plus `GrantEntitlement` or `RevokeEntitlement` and typically the capabilities `IdLE.Entitlement.List` plus `IdLE.Entitlement.Grant|Revoke`. -## Related - -- [Workflows](workflows.md) -- [Providers](providers.md) -- [Architecture](../about/architecture.md) - ## Security notes - Steps emit events via `Context.EventSink.WriteEvent(...)`. diff --git a/website/sidebars.js b/website/sidebars.js index 8ac75eaa..070ed901 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -23,7 +23,6 @@ const sidebars = { items: [ 'about/intro', 'about/concepts', - 'about/architecture', 'about/security', ], }, From 44d0a687ee206fc9bf28782c1b7ef46588741345 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:21:49 +0100 Subject: [PATCH 03/25] revisit security to extensibility --- docs/about/security.md | 52 ++---------------------------------- docs/extend/extensibility.md | 4 +++ 2 files changed, 6 insertions(+), 50 deletions(-) diff --git a/docs/about/security.md b/docs/about/security.md index 2568193a..b04a5d39 100644 --- a/docs/about/security.md +++ b/docs/about/security.md @@ -75,6 +75,8 @@ Redaction happens for: ### Where redaction is applied +Redaction is intentionally centralized at output boundaries to keep the execution model unchanged and to avoid altering step/provider behavior while making outputs safe-by-default. + Redaction is applied **before** data is: - Buffered as run events (execution result `Events`) @@ -87,59 +89,9 @@ Redaction is applied **before** data is: - IdLE does **not** attempt to redact secrets embedded inside free-text message strings (e.g., `Event.Message`). - Steps and providers should avoid placing secrets into free-text messages. -### Rationale - -Redaction is intentionally centralized at output boundaries to keep the execution model unchanged and to avoid altering step/provider behavior while making outputs safe-by-default. - ## Guidance for hosts - Keep workflow files in a protected location and review them like code (even though they are data-only). - Load step and provider modules explicitly before execution. - Treat the step registry as privileged configuration and do not let workflow authors change it. - If you stream events, implement a small sink object with a `WriteEvent(event)` method and keep it side-effect free. - -## Guidance for step authors - -- Use providers for system operations; do not embed authentication logic inside steps. -- Emit events using `Context.EventSink.WriteEvent(Type, Message, StepName, Data)`. -- Avoid global state. Steps should be idempotent whenever possible. - -## Repository security hygiene (maintainers) - -This section documents the repository-level security posture and the settings maintainers should keep enabled. - -### Dependency hygiene (Dependabot) - -IdLE uses Dependabot to keep GitHub Actions dependencies current. - -- Configuration file: `.github/dependabot.yml` -- Expected behaviour: a **weekly** PR that groups GitHub Actions updates - -**Verify**: - -1. Repository → **Insights** → **Dependency graph** → **Dependabot** -2. Confirm update activity and that PRs are being opened - -### Recommended GitHub repository security settings - -These settings are managed in GitHub and cannot be enforced via source control. Maintain them as part of routine repo maintenance: - -- **Dependabot alerts**: enabled -- **Dependabot security updates**: enabled -- **Secret scanning**: enabled (and **push protection** if available for the repo) -- **Branch protection rules** on `main`: - - Require pull requests before merging - - Require status checks to pass before merging (CI) - - Restrict force pushes -- **Least privilege** for workflows: - - Use explicit `permissions:` blocks in workflows - - Prefer read-only defaults unless a job needs write access - -**Verify** (typical locations): - -- Repository → **Settings** → **Security** (feature availability depends on GitHub plan) -- Repository → **Settings** → **Branches** (branch protection) - -## See also - -- Root security policy: `SECURITY.md` diff --git a/docs/extend/extensibility.md b/docs/extend/extensibility.md index 7ee6ef1b..ffea2a0f 100644 --- a/docs/extend/extensibility.md +++ b/docs/extend/extensibility.md @@ -24,6 +24,10 @@ Steps can emit structured events using the execution context contract: Keep steps host-agnostic: do not call UI APIs directly. +- Use providers for system operations; do not embed authentication logic inside steps. +- Emit events using `Context.EventSink.WriteEvent(Type, Message, StepName, Data)`. +- Avoid global state. Steps should be idempotent whenever possible. + --- ## Add a new provider From e21af456bbc68d4368a1a2757a3ca3f07c40adc6 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:38:42 +0100 Subject: [PATCH 04/25] remove related --- docs/use/workflows.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/use/workflows.md b/docs/use/workflows.md index 0b0b42ac..40be201f 100644 --- a/docs/use/workflows.md +++ b/docs/use/workflows.md @@ -229,8 +229,3 @@ This makes configurations safe and statically validatable. (Content for advanced patterns will be added in future updates) This approach keeps workflows data-only while allowing rich message formatting in the host code. - -## Related - -- [Steps](steps.md) -- [Providers](providers.md) From e9b23deb444ae52fde31b9e364a33bf86e3f97e1 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:40:12 +0100 Subject: [PATCH 05/25] shorten intro use idle --- docs/use/intro.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/docs/use/intro.md b/docs/use/intro.md index a55a6969..a3719add 100644 --- a/docs/use/intro.md +++ b/docs/use/intro.md @@ -5,27 +5,17 @@ sidebar_label: Use IdLE This section explains how to **use IdLE** in day-to-day operations. -It is written for people who run workflows, validate plans, and operate IdLE in environments +It is written for people who **define and run workflows**, **validate plans**, and **operate IdLE** in environments with one or more providers. ## What you will find here - Installing and updating IdLE +- Define workflows - Planning versus execution - Running workflows - Exporting plans for review and audit +- Executing workflow plans - Troubleshooting common issues -## Where to continue? - -1. [Installation](installation.md) -2. [Quick Start](quickstart.md) -3. [Configuration](configuration.md) -4. [Workflows](workflows.md) -5. [Steps](steps.md) -6. [Providers](providers.md) -7. [Plan Export](plan-export.md) - -## Not the right section? - If you want to extend IdLE and implement providers or steps, go to [Extend](../extend/intro.md) instead. From 4090c31d4ec0f86d18aef0773f41fc5583ae70e1 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:40:40 +0100 Subject: [PATCH 06/25] adding testing requirements --- docs/develop/testing.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/develop/testing.md b/docs/develop/testing.md index 668ea0ba..54ce6c2f 100644 --- a/docs/develop/testing.md +++ b/docs/develop/testing.md @@ -2,6 +2,11 @@ IdLE is designed to be testable in isolation. Tests should be deterministic, fast, and runnable on any machine (local or CI) without requiring live systems. +## Requirements + +- Pester **5.7.1+** (for running tests, optional) +- PSScriptAnalyzer **1.24.0+** (for running static analysis, optional) + ## Test folder structure Tests are organized by domain under `tests/`: From d4bac8f1da7a4762bff7d154b54cc1e05ddb60f1 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 21:47:28 +0100 Subject: [PATCH 07/25] updating installation --- docs/use/installation.md | 71 ++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 46 deletions(-) diff --git a/docs/use/installation.md b/docs/use/installation.md index 7ba3c590..d02ca0d9 100644 --- a/docs/use/installation.md +++ b/docs/use/installation.md @@ -6,45 +6,34 @@ sidebar_label: Installation # Installation IdLE can be consumed either from the **PowerShell Gallery** (recommended for most users) or directly from the -repository source (useful for contributors and development scenarios). +**Github repository source** (useful for contributors and development scenarios). --- ## Requirements -- PowerShell **7.x** (`pwsh`) -- Pester **5.7.1+** (for running tests, optional) -- PSScriptAnalyzer **1.24.0+** (for running static analysis, optional) +- PowerShell **7.x** or later (`pwsh`) --- -## Install from PowerShell Gallery (recommended) +## Install IdLE + +### Install from PowerShell Gallery (recommended) From a PowerShell 7 prompt: ```powershell -Install-Module -Name IdLE -Scope CurrentUser -Import-Module IdLE +Install-Module -Name IdLE +Import-Module -Name IdLE ``` > Note: The `IdLE` module automatically imports the baseline modules (`IdLE.Core` and `IdLE.Steps.Common`). > Optional modules (providers, additional step modules) are shipped with the package but not auto-imported. -### Verify installation - -```powershell -Get-Module IdLE -ListAvailable | Select-Object Name, Version, Path -Get-Command -Module IdLE -``` - ---- - -## Install from repository source +### Install from repository source This path is primarily intended for contributors and development scenarios. -### Clone and import - From a PowerShell 7 prompt: ```powershell @@ -55,18 +44,18 @@ cd IdentityLifecycleEngine Import-Module ./src/IdLE/IdLE.psd1 -Force ``` -### Verify installation (source) +### Verify installation ```powershell +Get-Module IdLE -ListAvailable | Select-Object Name, Version, Path Get-Command -Module IdLE -Get-Command -Module IdLE.Core ``` --- ## What gets imported -### Default: `IdLE` meta-module (baseline) +### `IdLE` meta-module (baseline) `IdLE` is the **baseline** entrypoint. Importing it automatically loads: @@ -78,27 +67,25 @@ This keeps your PowerShell session clean while still allowing workflows to refer **Non-blocking guarantee:** `Import-Module IdLE` always succeeds on a clean PowerShell 7 environment without any external dependencies (RSAT, AD tools, third-party modules, etc.). -**When to use:** All users and production scenarios. - -### Advanced: Engine-only import - -Advanced hosts can import the engine without any step packs: +### Optional modules (shipped but not auto-imported) -```powershell -Import-Module ./src/IdLE.Core/IdLE.Core.psd1 -Force -``` +The `IdLE` package ships additional modules that are **not automatically imported**. +These modules may have system-specific or tool-specific dependencies and are imported explicitly when needed: -**When to use:** Custom host implementations that provide their own step registry and providers. +- **Provider** modules: see Provider Reference +- **Optional step modules:** `IdLE.Steps.DirectorySync`, `IdLE.Steps.Mailbox` +- **Development/testing modules:** `IdLE.Provider.Mock` ---- +Example (from module): -## Optional modules (shipped but not auto-imported) +```powershell +# Import baseline (auto-imports Core and Steps.Common) +Import-Module IdLE -Force -The `IdLE` package ships additional modules that are **not automatically imported**. These modules may have system-specific or tool-specific dependencies and are imported explicitly when needed: +# Import optional provider when needed +Import-Module IdLE.Provider.AD -Force +``` -- **Provider modules:** `IdLE.Provider.AD`, `IdLE.Provider.EntraID`, `IdLE.Provider.ExchangeOnline`, `IdLE.Provider.DirectorySync.EntraConnect` -- **Optional step modules:** `IdLE.Steps.DirectorySync`, `IdLE.Steps.Mailbox` -- **Development/testing modules:** `IdLE.Provider.Mock` Example (from source): @@ -110,12 +97,4 @@ Import-Module ./src/IdLE/IdLE.psd1 -Force Import-Module ./src/IdLE.Provider.AD/IdLE.Provider.AD.psd1 -Force ``` -For a complete list of available providers and usage details, see **[Providers](../use/providers.md)**. - ---- - -## Next steps - -- [Quickstart](quickstart.md) — Run the demo and learn the Plan → Execute flow -- [Providers](../use/providers.md) — Learn about provider aliases and usage -- [Workflows](../use/workflows.md) — Learn how to define workflows +For usage details, see [Use > Provider](../use/provider.md). From e825ce6b7c74944e1f69ca22be443dd081578b71 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:12:03 +0100 Subject: [PATCH 08/25] move examples from "live" to "templates2 --- examples/workflows/templates/.gitkeep | 1 - examples/workflows/{live => templates}/ad-joiner-complete.psd1 | 0 .../workflows/{live => templates}/ad-leaver-offboarding.psd1 | 0 .../{live => templates}/ad-mover-department-change.psd1 | 0 .../{live => templates}/complete-leaver-entraid-exo.psd1 | 0 .../workflows/{live => templates}/entraid-joiner-complete.psd1 | 0 .../{live => templates}/entraid-leaver-offboarding.psd1 | 0 .../{live => templates}/entraid-mover-department-change.psd1 | 0 .../{live => templates}/exo-leaver-mailbox-offboarding.psd1 | 0 .../workflows/{live => templates}/joiner-with-entraid-sync.psd1 | 0 10 files changed, 1 deletion(-) delete mode 100644 examples/workflows/templates/.gitkeep rename examples/workflows/{live => templates}/ad-joiner-complete.psd1 (100%) rename examples/workflows/{live => templates}/ad-leaver-offboarding.psd1 (100%) rename examples/workflows/{live => templates}/ad-mover-department-change.psd1 (100%) rename examples/workflows/{live => templates}/complete-leaver-entraid-exo.psd1 (100%) rename examples/workflows/{live => templates}/entraid-joiner-complete.psd1 (100%) rename examples/workflows/{live => templates}/entraid-leaver-offboarding.psd1 (100%) rename examples/workflows/{live => templates}/entraid-mover-department-change.psd1 (100%) rename examples/workflows/{live => templates}/exo-leaver-mailbox-offboarding.psd1 (100%) rename examples/workflows/{live => templates}/joiner-with-entraid-sync.psd1 (100%) diff --git a/examples/workflows/templates/.gitkeep b/examples/workflows/templates/.gitkeep deleted file mode 100644 index c4f88305..00000000 --- a/examples/workflows/templates/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# Templates directory diff --git a/examples/workflows/live/ad-joiner-complete.psd1 b/examples/workflows/templates/ad-joiner-complete.psd1 similarity index 100% rename from examples/workflows/live/ad-joiner-complete.psd1 rename to examples/workflows/templates/ad-joiner-complete.psd1 diff --git a/examples/workflows/live/ad-leaver-offboarding.psd1 b/examples/workflows/templates/ad-leaver-offboarding.psd1 similarity index 100% rename from examples/workflows/live/ad-leaver-offboarding.psd1 rename to examples/workflows/templates/ad-leaver-offboarding.psd1 diff --git a/examples/workflows/live/ad-mover-department-change.psd1 b/examples/workflows/templates/ad-mover-department-change.psd1 similarity index 100% rename from examples/workflows/live/ad-mover-department-change.psd1 rename to examples/workflows/templates/ad-mover-department-change.psd1 diff --git a/examples/workflows/live/complete-leaver-entraid-exo.psd1 b/examples/workflows/templates/complete-leaver-entraid-exo.psd1 similarity index 100% rename from examples/workflows/live/complete-leaver-entraid-exo.psd1 rename to examples/workflows/templates/complete-leaver-entraid-exo.psd1 diff --git a/examples/workflows/live/entraid-joiner-complete.psd1 b/examples/workflows/templates/entraid-joiner-complete.psd1 similarity index 100% rename from examples/workflows/live/entraid-joiner-complete.psd1 rename to examples/workflows/templates/entraid-joiner-complete.psd1 diff --git a/examples/workflows/live/entraid-leaver-offboarding.psd1 b/examples/workflows/templates/entraid-leaver-offboarding.psd1 similarity index 100% rename from examples/workflows/live/entraid-leaver-offboarding.psd1 rename to examples/workflows/templates/entraid-leaver-offboarding.psd1 diff --git a/examples/workflows/live/entraid-mover-department-change.psd1 b/examples/workflows/templates/entraid-mover-department-change.psd1 similarity index 100% rename from examples/workflows/live/entraid-mover-department-change.psd1 rename to examples/workflows/templates/entraid-mover-department-change.psd1 diff --git a/examples/workflows/live/exo-leaver-mailbox-offboarding.psd1 b/examples/workflows/templates/exo-leaver-mailbox-offboarding.psd1 similarity index 100% rename from examples/workflows/live/exo-leaver-mailbox-offboarding.psd1 rename to examples/workflows/templates/exo-leaver-mailbox-offboarding.psd1 diff --git a/examples/workflows/live/joiner-with-entraid-sync.psd1 b/examples/workflows/templates/joiner-with-entraid-sync.psd1 similarity index 100% rename from examples/workflows/live/joiner-with-entraid-sync.psd1 rename to examples/workflows/templates/joiner-with-entraid-sync.psd1 From 4c9d7339577f3ab09ff876a0a2b8cd2119607980 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:39:48 +0100 Subject: [PATCH 09/25] fixed providers link --- docs/use/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/use/installation.md b/docs/use/installation.md index d02ca0d9..c64f00c4 100644 --- a/docs/use/installation.md +++ b/docs/use/installation.md @@ -97,4 +97,4 @@ Import-Module ./src/IdLE/IdLE.psd1 -Force Import-Module ./src/IdLE.Provider.AD/IdLE.Provider.AD.psd1 -Force ``` -For usage details, see [Use > Provider](../use/provider.md). +For usage details, see [Use > Provider](../use/providers.md). From 3e1e94b271f5fc15a1ed0206d07fafee7dcf0f66 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:39:57 +0100 Subject: [PATCH 10/25] rewrote quickstart --- docs/use/quickstart.md | 152 +++++++++++++++++++++++------------------ 1 file changed, 87 insertions(+), 65 deletions(-) diff --git a/docs/use/quickstart.md b/docs/use/quickstart.md index cc08f306..71e664e8 100644 --- a/docs/use/quickstart.md +++ b/docs/use/quickstart.md @@ -1,49 +1,56 @@ --- title: Quick Start -sidebar_label: Quick Start +sidebar_label: QuickStart --- # Quick Start -This page gets you from **zero → first successful run** as fast as possible. +The repository contains a demo runner that showcases the full **Plan → Execute** flow using predefined example workflows.
+Each example workflow is a single workflow definition `psd1`-file in `/examples/workflows/...` directories. -IdLE is an orchestration engine. That means: +## Get Repository Demo -- **Planning** builds a deterministic plan (safe, no external changes) -- **Execution** runs that plan using **provider implementations** (system adapters) supplied by the host +Clone the repository (or download the source archive from a GitHub release). -If you just want a runnable end-to-end first run, start with **Repository demo**. -If you want to use IdLE as a library from the PowerShell Gallery package, follow **Install from PowerShell Gallery**. +```powershell +git clone https://github.com/blindzero/IdentityLifecycleEngine +cd IdentityLifecycleEngine +``` ---- +## Run Demo -## 1) Repository demo (recommended first run) +Our **Repository demo** provides sample workflows which are not provided by the module install from PowerShell Gallery package. -The repository contains a demo runner that showcases the full **Plan → Execute** flow using predefined example workflows. +### 1. Show Demo Workflows -1. Clone the repository (or download the source archive from a GitHub release). -2. Run the demo script: +By default the **IdLE Demo** script uses only examples workflow definition from the `examples/workflows/mock` folder category to avoid dependency to real-life systems. + +List available mock category examples: ```powershell -pwsh -File .\examples\Invoke-IdleDemo.ps1 +.\examples\Invoke-IdleDemo.ps1 -List ``` -List available examples: +### 2. Run Demo Workflow ```powershell -pwsh -File .\examples\Invoke-IdleDemo.ps1 -List +.\examples\Invoke-IdleDemo.ps1 ``` -Run a specific example: +Select one of the workflow examples available, that do _not_ use real provider interactions and only use the mock provider interface. + +Alternatively, select an example workflow with `-Example` parameter: ```powershell -pwsh -File .\examples\Invoke-IdleDemo.ps1 -Example +.\examples\Invoke-IdleDemo.ps1 -Example ``` -Run all examples: +Or run all mock workflows: + +* Run all examples: ```powershell -pwsh -File .\examples\Invoke-IdleDemo.ps1 -All +.\examples\Invoke-IdleDemo.ps1 -All ``` What you should see: @@ -53,23 +60,23 @@ What you should see: - the plan is executed with demo/mock providers - the result contains step results and buffered events ---- - -## 2) Install from PowerShell Gallery +### 3. Check other examples -Install and import the meta module: +We also provide additional "template" examples, which could be used with live systems. ```powershell -Install-Module -Name IdLE -Scope CurrentUser -Import-Module IdLE +.\examples\Invoke-IdleDemo.ps1 -List -Category All ``` -> IdLE does not ship a “live system host”. A host (your script, CI job, or service) must provide provider instances -> for execution. For a safe first run, IdLE ships mock providers that are sufficient to execute example workflows. +:::warning Use template examples with care as they connect and may cause harm to your live environments. +::: --- -## 3) First run from an installed package (mock providers + example workflow) +## Run your first workflow + +IdLE does not ship a “live system host”. A host (your script, CI job, or service) must provide provider instances +for execution. For a safe first run, IdLE ships mock providers that are sufficient to execute example workflows. This is the smallest runnable program that demonstrates the full flow: @@ -78,65 +85,80 @@ This is the smallest runnable program that demonstrates the full flow: 3. Execute with providers (mock) 4. Inspect result + events -### Use an example workflow from the repository +### 1. Import Mock Provider + +```powershell +Import-Module .\src\IdLE.Provider.Mock\IdLE.Provider.Mock.psd1 -Force +``` -Workflows are data files (`.psd1`). The quickest path is to reuse one of the repository examples: +### 2. Select Workflow -- clone the repository, then reference a workflow file from `examples/workflows` -- or copy a single example workflow file into your working directory +Workflows are data files (`.psd1`). The quickest path is to reuse one of the repository examples, -Example (workflow from repo checkout): +- reference a workflow file from `examples/workflows` +- or copy and adapt a single example workflow file into your working directory + +:::note + +The mock provider below can be used with workflows that use following Step Types: + +- IdLE.Step.EmitEvent +- IdLE.Step.ReadIdentity +- IdLE.Step.EnsureAttribute +- IdLE.Step.DisableIdentity +- IdLE.Step.EnableIdentity +- IdLE.Step.EnsureEntitlement + +::: ```powershell -# 0) Point to an example workflow file -$workflowPath = Join-Path 'C:\path\to\IdentityLifecycleEngine' 'examples\workflows\.psd1' +$workflow = Join-Path 'C:\path\to\IdentityLifecycleEngine' 'examples\workflows\.psd1' +``` + +### 3. Create Request Object -# 1) Create the request (your input intent) +With the following command we create a simple 'Joiner' request. + +```powershell $request = New-IdleLifecycleRequest -LifecycleEvent 'Joiner' +``` + +### 4. Build the plan (deterministic, data-only) + +The plan evaluates validity of the request in combination with the workflow definition. -# 2) Build the plan (deterministic, data-only) -$plan = New-IdlePlan -WorkflowPath $workflowPath -Request $request +```powershell +$plan = New-IdlePlan -WorkflowPath $workflow -Request $request +``` + +### 5. Select providers -# 3) Provide providers (mock providers are included for first runs) +For first run, we just use our internal mock provider. + +```powershell $providers = @{ Identity = New-IdleMockIdentityProvider } +``` -# 4) Execute the plan +### 6. Execute the plan + +```powershell $result = Invoke-IdlePlan -Plan $plan -Providers $providers +``` -# 5) Inspect result + events +### 7. Inspect result + events + +```powershell $result.Status $result.Steps $result.Events | Select-Object Type, StepName, Message ``` -Notes: +:::tip - If your workflow contains steps that require additional provider roles (e.g. `Messaging`, `Entitlement`), you must add them to `$providers`. - Many steps default to the provider alias `'Identity'` unless a step explicitly sets `With.Provider`. ---- - -## 4) Using real providers (host integration) - -To execute against real systems, you supply provider implementations that: - -- implement the required provider contract methods for the steps you use -- advertise capabilities via `GetCapabilities()` (used for planning-time validation) - -Example structure: - -```powershell -$providers = @{ - Identity = $myIdentityProvider - # Entitlement = $myEntitlementProvider - # Messaging = $myMessagingProvider - - # Optional (recommended): host-provided auth session broker - # AuthSessionBroker = $myAuthSessionBroker -} -``` - -During planning, IdLE validates prerequisites and fails early if required capabilities are missing. +::: From 106cdc65b891c62da4fbf83ef694b5b601325839 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 22:42:08 +0100 Subject: [PATCH 11/25] removed configuration page --- docs/index.md | 1 - docs/use/configuration.md | 206 -------------------------------------- docs/use/quickstart.md | 2 +- website/sidebars.js | 1 - 4 files changed, 1 insertion(+), 209 deletions(-) delete mode 100644 docs/use/configuration.md diff --git a/docs/index.md b/docs/index.md index fb7decc2..565c2148 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,7 +24,6 @@ Learn how to [use IdLE](use/intro.md) as an operator or admin, e.g. for workflow - [Quickstart](use/quickstart.md) - Run the demo and understand Plan → Execute flow - [Installation](use/installation.md) - Install and import guide (requirements, import options) -- [Configuration](use/configuration.md) - Configuration schema reference - [Workflows](use/workflows.md) - Define lifecycle workflows - [Steps](use/steps.md) - Use and configure steps - [Providers](use/providers.md) - Provider aliases and injection diff --git a/docs/use/configuration.md b/docs/use/configuration.md deleted file mode 100644 index 875deb4e..00000000 --- a/docs/use/configuration.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -title: Configuration -sidebar_label: Configuration ---- - -# Configuration - -## Purpose - -Configuration in IdLE defines *what* should happen during a lifecycle execution, -not *how* it is implemented. - -This document explains the conceptual role of configuration in IdLE, the different -levels at which configuration exists, and how responsibilities are intentionally -separated to keep workflows portable, testable, and reusable. - ---- - -## Scope - -This document covers: - -- The role of configuration in IdLE -- Different configuration layers and their responsibilities -- How configuration influences planning and execution -- Recommended configuration patterns - -Out of scope: - -- Complete workflow schema documentation -- Provider-specific configuration options -- Implementation details of steps or providers -- Validation rules for individual configuration properties - ---- - -## Concept - -### Configuration as declarative intent - -Configuration in IdLE is **declarative**. - -It describes: - -- desired state -- required steps -- conditions under which steps apply - -It does not describe: - -- execution order mechanics -- infrastructure-specific behavior -- side effects - -This allows IdLE to build and execute plans deterministically based on intent. - ---- - -### Configuration layers - -IdLE uses multiple configuration layers with clear boundaries: - -#### Workflow configuration - -- Defines lifecycle intent -- Declares steps, conditions, and parameters -- Declares optional OnFailureSteps for cleanup/rollback -- Is environment-agnostic -- Stored as version-controlled files (e.g. PSD1) - -**OnFailureSteps** are an optional workflow section that defines cleanup or rollback steps -executed when primary steps fail. They run in best-effort mode: each OnFailure step is attempted -regardless of previous OnFailure step failures. - -#### Execution request - -- Describes *why* a workflow is executed -- Includes lifecycle event, actor, and context -- Supplied at runtime by the host - -#### Provider configuration - -- Defines how external systems are accessed -- Is environment-specific -- Managed entirely by the host - -The engine combines these layers when building a plan. - ---- - -### Workflow configuration is portable - -Workflow configuration must remain: - -- deterministic -- side-effect free -- independent of environment - -A workflow should not: - -- reference credentials -- embed infrastructure details -- assume a specific provider implementation - -This ensures the same workflow can run unchanged in: - -- local development -- CI pipelines -- production environments - ---- - -### Conditions and intent - -Conditions are part of configuration, not logic. - -They express: - -- when a step should apply -- based on input state or request context - -Conditions must not: - -- perform mutations -- depend on external systems -- replace step logic - -They are evaluated during planning, not execution. - ---- - -## Usage - -### When to use configuration - -Configuration should be used to express: - -- *what* needs to be ensured -- *under which circumstances* -- *with which parameters* - -Examples include: - -- ensuring attributes -- assigning entitlements -- skipping steps based on request data - ---- - -### When not to use configuration - -Configuration should not be used to: - -- encode complex logic -- perform calculations -- replace step implementations - -If behavior cannot be expressed declaratively, it likely belongs in a step. - ---- - -### Host responsibility - -The host is responsible for: - -- loading workflow configuration -- supplying execution requests -- providing provider implementations -- validating configuration before execution (if required) - -The engine assumes configuration is valid and focuses on orchestration. - ---- - -## Common pitfalls - -### Mixing configuration and logic - -Embedding logic into configuration leads to: - -- unreadable workflows -- fragile behavior -- difficult testing - -Logic belongs in steps, not configuration. - ---- - -### Environment-specific configuration in workflows - -Workflows must not contain: - -- credentials -- hostnames -- environment-specific identifiers - -Such data belongs in host configuration and provider setup. - ---- - -### Overloading configuration - -Trying to solve every problem with configuration often results in -overly complex workflows. - -Prefer small, focused steps with clear parameters. diff --git a/docs/use/quickstart.md b/docs/use/quickstart.md index 71e664e8..27501d3d 100644 --- a/docs/use/quickstart.md +++ b/docs/use/quickstart.md @@ -1,6 +1,6 @@ --- title: Quick Start -sidebar_label: QuickStart +sidebar_label: Quick Start --- # Quick Start diff --git a/website/sidebars.js b/website/sidebars.js index 070ed901..a4cbd66f 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -34,7 +34,6 @@ const sidebars = { 'use/intro', 'use/installation', 'use/quickstart', - 'use/configuration', 'use/workflows', 'use/steps', 'use/providers', From 040eed8b88d32063ecda05cb7ced3cf6bc9de4fe Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Fri, 30 Jan 2026 23:40:59 +0100 Subject: [PATCH 12/25] reworking steps into use workflows and extend to remove use step page --- docs/extend/steps.md | 129 +++++++++++++++++++------- docs/use/steps.md | 120 ------------------------- docs/use/workflows.md | 205 +++++++++++++++++++++++++++++------------- website/sidebars.js | 1 - 4 files changed, 236 insertions(+), 219 deletions(-) delete mode 100644 docs/use/steps.md diff --git a/docs/extend/steps.md b/docs/extend/steps.md index 70fbcf9f..3576f63b 100644 --- a/docs/extend/steps.md +++ b/docs/extend/steps.md @@ -12,25 +12,6 @@ reference for step authors and reviewers. --- -## Scope - -This document covers: - -- What a step represents conceptually -- The lifecycle of a step during plan execution -- The role and meaning of step metadata -- Expectations regarding idempotency and side effects -- Responsibility boundaries between steps and other components - -Out of scope: - -- Inventory or listing of existing steps -- Step-specific parameter reference -- Provider-specific implementation details -- Code examples of individual steps - ---- - ## Concept ### What is a step? @@ -47,7 +28,14 @@ A step: Steps do not orchestrate other steps and do not control execution flow beyond their own outcome. ---- +### Design goals + +Steps should be: + +- idempotent (converge towards the desired state) +- deterministic (same inputs produce the same plan) +- provider-agnostic (use provider contracts, not direct system calls) +- safe for preview (planning must not change external state) ### Step lifecycle @@ -60,8 +48,6 @@ During execution, each step follows a consistent lifecycle: The engine is responsible for invoking steps and sequencing their execution. ---- - ### Step metadata Metadata describes **what a step is**, not **how it is implemented**. @@ -87,8 +73,6 @@ Metadata exists to make steps: - reviewable - documentable ---- - ### Idempotency expectations Steps should be designed to be **idempotent whenever possible**. @@ -101,8 +85,6 @@ Idempotent steps: If a step is not idempotent, this must be clearly documented in its metadata. ---- - ### Side effects and responsibility Steps are the only components allowed to produce side effects. @@ -129,8 +111,6 @@ When writing a new step, authors should: - respect idempotency expectations - avoid embedding configuration or environment assumptions ---- - ### Steps and configuration Configuration supplies **parameters** to steps. @@ -142,8 +122,6 @@ Steps should: - validate required inputs - remain functional across environments ---- - ### Steps and providers Steps interact with external systems exclusively through providers. @@ -154,6 +132,93 @@ Steps must not: - assume provider implementation details - embed credentials or environment-specific values +## Inputs + +Steps receive inputs from the workflow under `Inputs` and may reference: + +- `Request.*` +- `State.*` +- `Policy.*` (optional root, host-defined) + +Avoid executing code from configuration. Keep inputs data-only. + +--- + +## Outputs + +Steps may write to `State.*` only, and only to declared output paths. +This prevents hidden coupling between steps. + +### Eventing + +Steps may emit **structured events** for progress and audit. + +The engine provides a stable, object-based contract on the execution context: + +- `Context.EventSink.WriteEvent(Type, Message, StepName, Data)` + +Notes: + +- `Type` is a short string (for example: `Custom`, `Debug`). +- `Message` is a human-readable message. +- `StepName` should be the current step name (if available). +- `Data` is an optional hashtable for structured details. + +Example: + +```powershell +$Context.EventSink.WriteEvent( + 'Custom', + 'Ensured Department attribute.', + $Step.Name, + @{ Provider = 'Identity'; Attribute = 'Department' } +) +``` + +- Steps must never execute code from configuration. +- Steps must not assume a specific host UI. +- Hosts can optionally stream events via `Invoke-IdlePlan -EventSink `, + but **ScriptBlock sinks are not supported**. + +--- + +## Error behavior + +### Primary steps (fail-fast) + +IdLE uses a **fail-fast execution model** for primary workflow steps: + +- A failing step stops plan execution immediately +- Subsequent primary steps are not executed +- Results and events capture what happened up to the failure + +### OnFailureSteps (best-effort) + +When primary steps fail, workflows can define **OnFailureSteps** for cleanup or rollback. + +OnFailureSteps are executed in **best-effort mode**: + +- Each OnFailure step is attempted regardless of previous OnFailure step failures +- OnFailure step failures do not stop execution of remaining OnFailure steps +- The overall execution status remains 'Failed' even if all OnFailure steps succeed + +**Execution result structure:** + +```powershell +$result.Status # 'Failed' when primary steps fail +$result.Steps # Array of primary step results (only executed steps) +$result.OnFailure.Status # 'NotRun', 'Completed', or 'PartiallyFailed' +$result.OnFailure.Steps # Array of OnFailure step results +``` + +**OnFailure status values:** + +- `NotRun`: No primary steps failed, OnFailure steps were not executed +- `Completed`: All OnFailure steps succeeded +- `PartiallyFailed`: At least one OnFailure step failed, but execution continued + +For details on declaring OnFailureSteps, see [Workflows](../use/workflows.md). + --- ## Common pitfalls @@ -168,16 +233,12 @@ Steps that attempt to do too much: Prefer composing multiple focused steps instead. ---- - ### Encoding logic in configuration Complex logic does not belong in configuration. If a workflow becomes hard to read, the logic likely belongs in a step. ---- - ### Ignoring metadata Undocumented behavior leads to: diff --git a/docs/use/steps.md b/docs/use/steps.md deleted file mode 100644 index d8607b87..00000000 --- a/docs/use/steps.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Steps -sidebar_label: Steps ---- - -# Steps - -Steps are reusable plugins that define how IdLE should converge state. - -A step typically has two parts: - -- **Test**: plan data-only actions -- **Invoke**: execute planned actions - -## Design goals - -Steps should be: - -- idempotent (converge towards the desired state) -- deterministic (same inputs produce the same plan) -- provider-agnostic (use provider contracts, not direct system calls) -- safe for preview (planning must not change external state) - -## Inputs - -Steps receive inputs from the workflow under `Inputs` and may reference: - -- `Request.*` -- `State.*` -- `Policy.*` (optional root, host-defined) - -Avoid executing code from configuration. Keep inputs data-only. - -## Outputs - -Steps may write to `State.*` only, and only to declared output paths. -This prevents hidden coupling between steps. - -## Eventing - -Steps may emit **structured events** for progress and audit. - -The engine provides a stable, object-based contract on the execution context: - -- `Context.EventSink.WriteEvent(Type, Message, StepName, Data)` - -Notes: - -- `Type` is a short string (for example: `Custom`, `Debug`). -- `Message` is a human-readable message. -- `StepName` should be the current step name (if available). -- `Data` is an optional hashtable for structured details. - -Example: - -```powershell -$Context.EventSink.WriteEvent( - 'Custom', - 'Ensured Department attribute.', - $Step.Name, - @{ Provider = 'Identity'; Attribute = 'Department' } -) -``` - -Security and portability: - -- Steps must never execute code from configuration. -- Steps must not assume a specific host UI. -- Hosts can optionally stream events via `Invoke-IdlePlan -EventSink <object>`, - but **ScriptBlock sinks are not supported**. - -## Error behavior - -### Primary steps (fail-fast) - -IdLE uses a **fail-fast execution model** for primary workflow steps: - -- A failing step stops plan execution immediately -- Subsequent primary steps are not executed -- Results and events capture what happened up to the failure - -### OnFailureSteps (best-effort) - -When primary steps fail, workflows can define **OnFailureSteps** for cleanup or rollback. - -OnFailureSteps are executed in **best-effort mode**: - -- Each OnFailure step is attempted regardless of previous OnFailure step failures -- OnFailure step failures do not stop execution of remaining OnFailure steps -- The overall execution status remains 'Failed' even if all OnFailure steps succeed - -**Execution result structure:** - -```powershell -$result.Status # 'Failed' when primary steps fail -$result.Steps # Array of primary step results (only executed steps) -$result.OnFailure.Status # 'NotRun', 'Completed', or 'PartiallyFailed' -$result.OnFailure.Steps # Array of OnFailure step results -``` - -**OnFailure status values:** - -- `NotRun`: No primary steps failed, OnFailure steps were not executed -- `Completed`: All OnFailure steps succeeded -- `PartiallyFailed`: At least one OnFailure step failed, but execution continued - -For details on declaring OnFailureSteps, see [Workflows](workflows.md). - -## Built-in steps (starter pack) - -IdLE ships with a small set of built-in steps to keep demos and tests frictionless: - -- **IdLE.Step.EnsureAttribute**: converges an identity attribute to the desired value using `With.IdentityKey`, `With.Name`, and `With.Value`. Requires a provider with `EnsureAttribute` and usually the `IdLE.Identity.Attribute.Ensure` capability. -- **IdLE.Step.EnsureEntitlement**: converges an entitlement assignment to `Present` or `Absent` using `With.IdentityKey`, `With.Entitlement` (Kind + Id + optional DisplayName), `With.State`, and optional `With.Provider` (default `Identity`). Requires provider methods `ListEntitlements` plus `GrantEntitlement` or `RevokeEntitlement` and typically the capabilities `IdLE.Entitlement.List` plus `IdLE.Entitlement.Grant|Revoke`. - -## Security notes - -- Steps emit events via `Context.EventSink.WriteEvent(...)`. -- Step handlers are referenced by function name (string) in the step registry. -- ScriptBlock handlers are not supported as a secure default. diff --git a/docs/use/workflows.md b/docs/use/workflows.md index 40be201f..39021b68 100644 --- a/docs/use/workflows.md +++ b/docs/use/workflows.md @@ -1,13 +1,22 @@ --- -title: Workflows -sidebar_label: Workflows +title: Workflows & Steps +sidebar_label: Workflows / Steps --- -# Workflows +# Workflows & Steps Workflows are **data-only** configuration files (PSD1) describing which steps should run for a lifecycle event. -## Format +A step is a self-contained unit of work executed as part of a plan.
+A step: + +- performs a single, well-defined responsibility +- operates on the execution context provided by the engine +- may interact with external systems through providers +- reports its outcome through status and events +- does _not_ orchestrate other steps and do _not_ control execution flow beyond their own outcome. + +## Workflow File Format A workflow is a PowerShell hashtable stored as `.psd1`. @@ -30,6 +39,82 @@ Example: } ``` +#### Name + +The identifying name of your workflow + +```yaml +Type: String +Required: True +``` + +#### LifecycleEvent + +The type of the lifecycle event described by your workflow. + +```yaml +Type: String +Required: True +Options: Joiner, Mover, Leaver +``` + +#### Steps + +An Array of step objects, each a Hashtable. + +```yaml +Type: Array +Required: True +``` + +## Steps + +Each step represents a distinct action that is performed, based on data defined in the workflow parameters or that are passed by the host's request object and merges with the workflow definition on the plan. + +Steps are represented by PowerShell Hashtable objects. + +### Step Types + +Step types are treated as **contracts**. Prefer fully-qualified ids (module + step name), for example: `IdLE.Step.EmitEvent`. +Each step type's implementation is made available via a step registry. +Additionally, each step type's implementation defines required capabilities for this step. +Later, provider implementations are providing these capabilities for the steps. +If a provider selected for a step has not the capabilities available required by the step type, the plan of the workflow with fail. + +For a list of available Step Types please see the [Step Type Catalog](../reference/steps.md). + +Additionally, you can provide your own custom [extend with custom steps](../extend/steps.md). + +### Conditional steps + +Steps can be skipped using declarative `Condition` key. + +Example: + +```powershell +@{ + Name = 'Joiner - Condition Demo' + LifecycleEvent = 'Joiner' + Steps = @( + @{ + Name = 'EmitOnlyForJoiner' + Type = 'IdLE.Step.EmitEvent' + Condition = @{ + Equals = @{ + Path = 'Plan.LifecycleEvent' + Value = 'Joiner' + } + } + With = @{ + Message = 'This step runs only if Plan.LifecycleEvent == Joiner.' + } + } + ) +} +``` + +If the condition is not met, the step is marked as `Skipped` and a skip event is emitted. + ### OnFailureSteps (optional) Workflows can define cleanup or rollback steps that run when primary steps fail. @@ -44,19 +129,33 @@ Example: ```powershell @{ - Name = 'Joiner - With Cleanup' - LifecycleEvent = 'Joiner' - - Steps = @( - @{ Name = 'CreateAccount'; Type = 'IdLE.Step.CreateAccount' } - @{ Name = 'AssignLicense'; Type = 'IdLE.Step.AssignLicense' } - ) - - OnFailureSteps = @( - @{ Name = 'NotifyAdmin'; Type = 'IdLE.Step.SendEmail'; With = @{ Recipient = 'admin@example.com' } } - @{ Name = 'RollbackAccount'; Type = 'IdLE.Step.DeleteAccount' } - @{ Name = 'LogFailure'; Type = 'IdLE.Step.LogToDatabase' } - ) + Name = 'Joiner - With Cleanup' + LifecycleEvent = 'Joiner' + + Steps = @( + @{ + Name = 'CreateAccount'; + Type = 'IdLE.Step.CreateAccount' + } + @{ + Name = 'EnsureEntitlement'; + Type = 'IdLE.Step.EnsureEntitlement' + } + ) + + OnFailureSteps = @( + @{ + Name = 'RollbackAccount'; + Type = 'IdLE.Step.DeleteAccount' + } + @{ + Name = 'LogFailure'; + Type = 'IdLE.Step.EmitEvent'; + With = @{ + Message = 'Joiner Failed - Rollback performed' + } + } + ) } ``` @@ -73,47 +172,14 @@ The execution result includes a separate `OnFailure` section: ```powershell $result.OnFailure.Status # 'NotRun', 'Completed', or 'PartiallyFailed' -$result.OnFailure.Steps # Array of OnFailure step results +$result.OnFailure.Steps # Array of OnFailure step results ``` -## Planning and validation - -Workflows are validated during planning. - -Typical validation rules: - -- unknown keys are errors -- required keys must exist -- condition schemas must be valid -- `*From` paths must reference allowed roots - -## Step identifiers - -Step types are treated as **contracts**. Prefer fully-qualified ids (module + step name), -for example: `IdLE.Step.EmitEvent`. - -The host maps step types to step implementations via a step registry. - -## Conditional steps - -Steps can be skipped using declarative `Condition` key. - -Example: - -```powershell -Condition = @{ - Path = 'Plan.LifecycleEvent' - Equals = 'Joiner' -} -``` - -If the condition is not met, the step is marked as `Skipped` and a skip event is emitted. - ## References and inputs ### Template substitution (double curly braces) -IdLE supports **template substitution** for embedding request values into workflow step configurations using ``\{\{...\}\}`` placeholders. Templates are resolved during planning (plan build), producing a plan with resolved values. +IdLE supports **template substitution** for embedding request values into workflow step configurations using ``{{...}}`` placeholders. Templates are resolved during planning (plan build), producing a plan with resolved values. **How it works:** @@ -141,8 +207,8 @@ The values in `DesiredState` are accessible via `Request.Input.*` (or `Request.D Type = 'IdLE.Step.CreateIdentity' With = @{ Attributes = @{ - UserPrincipalName = '`\{\{Request.Input.UserPrincipalName\}\}`' - DisplayName = '`\{\{Request.Input.DisplayName\}\}`' + UserPrincipalName = '{{Request.Input.UserPrincipalName}}' + DisplayName = '{{Request.Input.DisplayName}}' } } } @@ -150,24 +216,24 @@ The values in `DesiredState` are accessible via `Request.Input.*` (or `Request.D Name = 'EmitEvent' Type = 'IdLE.Step.EmitEvent' With = @{ - Message = 'Creating user `\{\{Request.Input.DisplayName\}\}` (`\{\{Request.Input.UserPrincipalName\}\}`)' + Message = 'Creating user {{Request.Input.DisplayName}} ({{Request.Input.UserPrincipalName}})' } } ``` When the plan is built, templates are resolved to the actual values from the request: -- ``\{\{Request.Input.UserPrincipalName\}\}`` → `'jdoe@example.com'` -- ``\{\{Request.Input.DisplayName\}\}`` → `'John Doe'` +- `{{Request.Input.UserPrincipalName}}` → `'jdoe@example.com'` +- `{{Request.Input.DisplayName}}` → `'John Doe'` **Key features:** -- **Concise syntax**: Use ``\{\{Path\}\}`` instead of verbose `@{ ValueFrom = 'Path' }` objects +- **Concise syntax**: Use ``{{Path}}`` instead of verbose `@{ ValueFrom = 'Path' }` objects - **Multiple placeholders**: Place multiple templates in one string - **Nested structures**: Templates work in nested hashtables and arrays - **Planning-time resolution**: Templates are resolved during plan build, not execution - **Security boundary**: Only allowlisted request roots are accessible -**Allowed roots:** +**Allowed references:** For security, template resolution only allows accessing these request properties: @@ -203,7 +269,7 @@ Use `\{{` to include literal `{{` in a string: ```powershell With = @{ - Message = 'Literal \`{{ braces here and template `{{Request.Input.Name}}`' + Message = 'Literal \{{ braces here and template {{Request.Input.Name}}' } # Resolves to: 'Literal `{{ braces here and template ' ``` @@ -222,10 +288,21 @@ Prefer explicit reference fields over implicit parsing: This makes configurations safe and statically validatable. -**Note:** Template substitution (``\{\{...\}\}``) is preferred for string fields. Use `ValueFrom` objects when you need non-string references or conditional defaults. +:::note + +Template substitution (`{{...}}`) is preferred for string fields. Use `ValueFrom` objects when you need non-string references or conditional defaults. + +::: -## Advanced Workflow Patterns +--- + +## Workflow validation -(Content for advanced patterns will be added in future updates) +Workflows are validated during planning. + +Typical validation rules: -This approach keeps workflows data-only while allowing rich message formatting in the host code. +- unknown keys are errors +- required keys must exist +- condition schemas must be valid +- `*From` paths must reference allowed roots diff --git a/website/sidebars.js b/website/sidebars.js index a4cbd66f..53bfe6e2 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -35,7 +35,6 @@ const sidebars = { 'use/installation', 'use/quickstart', 'use/workflows', - 'use/steps', 'use/providers', 'use/plan-export', ], From 8a4a8e9489f890cdb02d46b0a51aec1f09e241cd Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:07:47 +0100 Subject: [PATCH 13/25] adding responsibility separation to about --- docs/about/concepts.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/about/concepts.md b/docs/about/concepts.md index f4471177..b5738f41 100644 --- a/docs/about/concepts.md +++ b/docs/about/concepts.md @@ -25,6 +25,45 @@ IdLE stays headless and avoids responsibilities that belong to a host applicatio --- +## Responsibilities + +### Separation of Responsibility + +Clear separation of responsibility is the essential foundation for maintainability: + +- **Engine** + - Orchestrates workflow execution + - Invokes steps + - Passes providers to steps + - Never depends on provider internals + +- **Steps** + - Implement domain logic + - Use providers through contracts + - Must not assume a specific provider implementation + +- **Providers** + - Implement infrastructure-specific behavior + - Fulfill contracts expected by steps + - Encapsulate external system details + - Authenticate and manage sessions + - Translate generic operations to system APIs + - Are mockable for tests + - Avoid global state + +- **Host** + - Selects and configures providers + - Injects providers into plan execution + - Decides which provider implementations are used + +This separation keeps the core engine free of environmental assumptions. + +**Important:** Steps should not handle authentication. Authentication is a provider responsibility via AuthSessionBroker. + +--- + +--- + IdLE consists of the following elements and components: ## Request From a423afc9ba3f9ba5332597ab5cc7b2a65950126a Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:08:09 +0100 Subject: [PATCH 14/25] revisit use providers --- docs/extend/providers.md | 103 --------------------------- docs/use/providers.md | 146 ++++++++++++++++++++++++++++++++------- 2 files changed, 122 insertions(+), 127 deletions(-) diff --git a/docs/extend/providers.md b/docs/extend/providers.md index 124944e5..8f473626 100644 --- a/docs/extend/providers.md +++ b/docs/extend/providers.md @@ -82,109 +82,6 @@ The contract is expressed through: --- -## Responsibilities - -### Separation of responsibilities - -Clear separation is essential for maintainability: - -- **Engine** - - Orchestrates workflow execution - - Invokes steps - - Passes providers to steps - - Never depends on provider internals - -- **Steps** - - Implement domain logic - - Use providers through contracts - - Must not assume a specific provider implementation - -- **Providers** - - Implement infrastructure-specific behavior - - Fulfill contracts expected by steps - - Encapsulate external system details - - Authenticate and manage sessions - - Translate generic operations to system APIs - - Are mockable for tests - - Avoid global state - -- **Host** - - Selects and configures providers - - Injects providers into plan execution - - Decides which provider implementations are used - -This separation keeps the core engine free of environmental assumptions. - -**Important:** Steps should not handle authentication. Authentication is a provider responsibility via AuthSessionBroker. - ---- - -## Usage - -### Provider Aliases - -When you supply providers to IdLE, you use a **hashtable** that maps **alias names** to **provider instances**: - -```powershell -$providers = @{ - Identity = $adProvider -} -``` - -#### Alias Naming - -The alias name (hashtable key) is **completely flexible** and chosen by you (the host): - -- It can be any valid PowerShell hashtable key -- Common patterns: - - **Role-based**: `Identity`, `Entitlement`, `Messaging` (when you have one provider per role) - - **Instance-based**: `SourceAD`, `TargetEntra`, `ProdForest`, `DevSystem` (when you have multiple providers) -- The built-in steps default to `'Identity'` if no `Provider` is specified in the step's `With` block - -#### How Workflows Reference Providers - -Workflow steps can specify which provider to use via the `Provider` key in the `With` block: - -```powershell -@{ - Name = 'Create user in source' - Type = 'IdLE.Step.CreateIdentity' - With = @{ - IdentityKey = 'newuser' - Attributes = @\{ ... \} - Provider = 'SourceAD' # References the alias from the provider hashtable - } -} -``` - -If `Provider` is not specified, it defaults to `'Identity'`: - -```powershell -# These are equivalent when Provider is not specified: -With = @{ IdentityKey = 'user1'; Name = 'Department'; Value = 'IT' } -With = @{ IdentityKey = 'user1'; Name = 'Department'; Value = 'IT'; Provider = 'Identity' } -``` - -#### Multiple Provider Example - -```powershell -# Create provider instances -$sourceAD = New-IdleADIdentityProvider -Credential $sourceCred -$targetEntra = New-IdleEntraIDIdentityProvider -Credential $targetCred - -# Map to custom aliases -$providers = @{ - SourceAD = $sourceAD - TargetEntra = $targetEntra -} - -# Workflow steps reference the aliases -# Step 1: With = @{ Provider = 'SourceAD'; ... } -# Step 2: With = @{ Provider = 'TargetEntra'; ... } -``` - ---- - ### Provider Categories While IdLE does not enforce provider categories, common conceptual groupings exist: diff --git a/docs/use/providers.md b/docs/use/providers.md index ecf2b784..f7b3ffa3 100644 --- a/docs/use/providers.md +++ b/docs/use/providers.md @@ -1,6 +1,6 @@ --- title: Providers -sidebar_label: Provider +sidebar_label: Providers --- # Providers @@ -8,30 +8,35 @@ sidebar_label: Provider Providers are the system-specific adapters (for example: Active Directory, Entra ID, Exchange Online) that connect IdLE workflows to external systems. -For complete provider documentation, including concepts, contracts, authentication, usage patterns, and examples, see: - -**→ [Providers and Contracts](../extend/providers.md)** (single source of truth) +For advanced provider documentation, including concepts, contracts, authentication, usage patterns, and examples, see: [Extend > Providers](../extend/providers.md) --- -## Quick Reference - -### What are providers? +## What are providers? Providers: -- Adapt IdLE workflows to external systems -- Handle authentication via AuthSessionBroker -- Translate generic operations to system APIs -- Are mockable for tests +- adapt IdLE workflows to external systems +- handle authentication via AuthSessionBroker +- translate generic operations to system APIs +- are mockable for tests +- provide capabilities, required by step types + +See: [Provider responsibilities](../about/concepts.md#responsibilities) -See: [Provider responsibilities](../extend/providers.md#responsibilities) +## How to use providers? -### How to use providers +Providers are supplied to plan execution as a hashtable with alias names. -Providers are supplied to plan execution as a hashtable with alias names: +:::info + +As providers may require additional tools and configuration, they are not imported automatically and must be imported manually by the host. + +::: ```powershell +Import-Module -Name IdLE.Provider.Mock + $providers = @{ Identity = New-IdleMockIdentityProvider } @@ -39,18 +44,111 @@ $providers = @{ $result = Invoke-IdlePlan -Plan $plan -Providers $providers ``` -See: [Provider aliases and usage](../extend/providers.md#usage) +### Provider Aliases -### Available providers +When you supply providers to IdLE, you use a **hashtable** that maps **alias names** to **provider instances**, e.g. `MockIdentity` -- [Active Directory Provider](../reference/providers/provider-ad.md) -- [Entra ID Provider](../reference/providers/provider-entraID.md) +```powershell +$providers = @{ + MockIdentity = New-IdleMockIdentityProvider +} +``` ---- +#### Alias Naming + +The alias name (hashtable key) is **completely flexible** and chosen by you (the host): + +- It can be any valid PowerShell hashtable key +- Common patterns: + - **Role-based**: `Identity`, `Entitlement`, `Messaging` (when you have one provider per role) + - **Instance-based**: `SourceAD`, `TargetEntra`, `ProdForest`, `DevSystem` (when you have multiple providers) +- The built-in steps default to `'Identity'` if no `Provider` is specified in the step's `With` block + +#### How Workflows Reference Providers + +Workflow steps can specify which provider to use via the `Provider` key in the `With` block: + +```powershell +@{ + Name = 'Create user' + Type = 'IdLE.Step.CreateIdentity' + With = @{ + IdentityKey = 'newuser' + Attributes = @{ ... } + Provider = 'MockIdentity' # References the alias from the provider hashtable + } +} +``` + +If `Provider` is not specified, it defaults to `'Identity'`: + +```powershell +# These are equivalent when Provider is not specified: +With = @{ IdentityKey = 'user1'; Name = 'Department'; Value = 'IT' } +With = @{ IdentityKey = 'user1'; Name = 'Department'; Value = 'IT'; Provider = 'Identity' } +``` + +### Multiple Provider Example + +```powershell +# Create provider instances +$sourceAD = New-IdleADIdentityProvider -Credential $sourceCred +$targetEntra = New-IdleEntraIDIdentityProvider -Credential $targetCred + +# Map to custom aliases +$providers = @{ + SourceAD = $sourceAD + TargetEntra = $targetEntra +} + +# Workflow steps reference the aliases +# Step 1: With = @{ Provider = 'SourceAD'; ... } +# Step 2: With = @{ Provider = 'TargetEntra'; ... } +``` + +## Authentication for Providers (AuthSessionBroker) + +Many providers require authenticated connections (tokens, API clients, remote sessions). +IdLE keeps authentication out of the engine and out of individual providers by using a +host-supplied broker. Using the **AuthSessionBroker** is in particular helpful for scenarios that use different providers or different authentications for one provider in one workflow. + +```powershell +# Assuming you have credentials available (e.g., from a secure vault or credential manager) +$tier0Credential = Get-Credential -Message "Enter Tier0 admin credentials" +$adminCredential = Get-Credential -Message "Enter regular admin credentials" + +# Create provider +$provider = New-IdleADIdentityProvider + +# Create broker with role-based credential mapping +$broker = New-IdleAuthSession -SessionMap @{ + @{ Role = 'Tier0' } = $tier0Credential + @{ Role = 'Admin' } = $adminCredential +} -DefaultCredential $adminCredential + +# Use provider with broker +$plan = New-IdlePlan -WorkflowPath './workflow.psd1' -Request $request -Providers @{ + Identity = $provider + AuthSessionBroker = $broker +} +``` + +The different authentication sessions are used by the workflow definition by the steps via `AuthSessionOptions`. +```powershell +With = @{ + ... + AuthSessionName = 'ActiveDirectory' + AuthSessionOptions = @{ Role = 'Tier0' } +} +``` + +:::info + +Please see the detailed Provider Reference documentation for authentication help. + +::: -## Related +## Provider Reference -- [Providers and Contracts](../extend/providers.md) — Complete provider reference -- [Workflows](workflows.md) — How workflows reference providers -- [Steps](steps.md) — How steps use providers -- [Extensibility](../extend/extensibility.md) — How to create custom providers +### [Active Directory Provider](../reference/providers/provider-ad.md) +### [Entra ID Provider](../reference/providers/provider-entraID.md) From 7c2e8171cc9d72788693986969a2bf32d2e34448 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:10:32 +0100 Subject: [PATCH 15/25] revisit plan export use docu --- docs/use/plan-export.md | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/docs/use/plan-export.md b/docs/use/plan-export.md index dfc0ea10..aa7199e5 100644 --- a/docs/use/plan-export.md +++ b/docs/use/plan-export.md @@ -3,9 +3,10 @@ title: Plan Export sidebar_label: Plan Export --- -# Plan Export (User Guide) +# Plan Export ## Why export a plan? + Plan export provides a reproducible, machine-readable representation of the planned actions IdLE intends to run. This is useful to: @@ -15,6 +16,7 @@ This is useful to: - Integrate IdLE planning into CI pipelines (validate workflows without executing them). ## What a plan export contains + A plan export typically includes: - Metadata about the exported plan (name, workflow, timestamps). @@ -22,10 +24,9 @@ A plan export typically includes: - Per-step resolved providers and parameters (as applicable). - Capability information (if exported) to help with validation and portability. -For the exact format and normative rules, see: -- `Plan Export Specification` in `Reference -> Specs`. +For the exact format and normative rules, see [Plan Export Specification](../reference/specs/plan-export.md). -## Typical workflow +## Typical use of plan export 1. Plan - Load workflow definition(s) @@ -51,30 +52,15 @@ Export-IdlePlan -Plan $plan -Path './artifacts/joiner.plan.json' ``` ### Review tips + - Verify provider selection matches your intent (especially in multi-provider environments). - Ensure sensitive values are not embedded in exported parameters. - Confirm step ordering and prerequisite behavior. -## Handling secrets -Do not store secrets in exported plan files. - -Recommended patterns: -- Use secret providers (environment variables, vault providers, injected runtime secrets). -- Store only secret references in workflow definitions and resolve them at execution time. -- Ensure logs and event sinks redact sensitive values. - ## CI usage + Plan export can be used as a build artifact: - Generate a plan export from a known input set. - Validate the export with schema checks (if available). - Compare against a known-good baseline (golden file) to detect unexpected drift. - -## Troubleshooting - -### MDX build errors when documenting exports -If you document placeholders like `\{\{...\}\}`, `{Name}` or examples like `@{ Key = 'Value' }`, -wrap them in inline code or fenced code blocks to avoid MDX parsing issues. - -## See also -- Plan Export Specification (Reference -> Specs) From c39b9c46691083105e0313c633b696b2b0cad2f3 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:17:25 +0100 Subject: [PATCH 16/25] fixed link in concepts to workflows (was steps) --- docs/about/concepts.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/about/concepts.md b/docs/about/concepts.md index b5738f41..619fd15c 100644 --- a/docs/about/concepts.md +++ b/docs/about/concepts.md @@ -62,8 +62,6 @@ This separation keeps the core engine free of environmental assumptions. --- ---- - IdLE consists of the following elements and components: ## Request @@ -109,9 +107,9 @@ To enable larger flexibility, you can use placeholders instead of literals to be } ``` -### Steps +### Workflows and Steps -**Steps** are reusable plugins that define convergence logic. They: +**Steps** are reusable plugins used by **workflows** that define convergence logic. They: - Operate idempotently (converge towards desired state) - Are provider-agnostic (use contracts, not direct system calls) @@ -121,7 +119,7 @@ To enable larger flexibility, you can use placeholders instead of literals to be Steps may only write to `State.*` and only to declared output paths. No deep merge: replace-at-path semantics only. -Learn more: [Steps](../use/steps.md) | [Step Catalog](../reference/steps.md) +Learn more: [Workflows](../use/workflows.md) | [Step Catalog](../reference/steps.md) ### Providers From d034aa2ffda57a0bedf14d8d387b62c5a8d353ce Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:18:49 +0100 Subject: [PATCH 17/25] wrong double escapes --- docs/use/workflows.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/use/workflows.md b/docs/use/workflows.md index 39021b68..517f3ab7 100644 --- a/docs/use/workflows.md +++ b/docs/use/workflows.md @@ -179,7 +179,7 @@ $result.OnFailure.Steps # Array of OnFailure step results ### Template substitution (double curly braces) -IdLE supports **template substitution** for embedding request values into workflow step configurations using ``{{...}}`` placeholders. Templates are resolved during planning (plan build), producing a plan with resolved values. +IdLE supports **template substitution** for embedding request values into workflow step configurations using `{{...}}` placeholders. Templates are resolved during planning (plan build), producing a plan with resolved values. **How it works:** @@ -227,7 +227,7 @@ When the plan is built, templates are resolved to the actual values from the req **Key features:** -- **Concise syntax**: Use ``{{Path}}`` instead of verbose `@{ ValueFrom = 'Path' }` objects +- **Concise syntax**: Use `{{Path}}` instead of verbose `@{ ValueFrom = 'Path' }` objects - **Multiple placeholders**: Place multiple templates in one string - **Nested structures**: Templates work in nested hashtables and arrays - **Planning-time resolution**: Templates are resolved during plan build, not execution From 32363c4fc77a2990d04e463572a04f933664e93d Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:22:01 +0100 Subject: [PATCH 18/25] removed "live" examples --- tests/Examples/WorkflowSamples.Tests.ps1 | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/tests/Examples/WorkflowSamples.Tests.ps1 b/tests/Examples/WorkflowSamples.Tests.ps1 index d3ad0bbc..3f4c35f9 100644 --- a/tests/Examples/WorkflowSamples.Tests.ps1 +++ b/tests/Examples/WorkflowSamples.Tests.ps1 @@ -59,27 +59,6 @@ Describe 'Mock example workflows' { } } -Describe 'Live example workflows' { - BeforeAll { - $liveWorkflowsPath = Join-Path -Path $workflowsPath -ChildPath 'live' - $liveWorkflows = Get-ChildItem -Path $liveWorkflowsPath -Filter '*.psd1' -File -ErrorAction SilentlyContinue - } - - It 'Live workflow directory exists' { - $liveWorkflowsPath | Should -Exist - } - - It 'Live workflows exist' { - $liveWorkflows | Should -Not -BeNullOrEmpty - } - - It 'All live workflows validate with Test-IdleWorkflow' { - foreach ($file in $liveWorkflows) { - { Test-IdleWorkflow -WorkflowPath $file.FullName } | Should -Not -Throw - } - } -} - Describe 'Template example workflows' { BeforeAll { $templatesWorkflowsPath = Join-Path -Path $workflowsPath -ChildPath 'templates' From e861410dcdf3a6839ece3cf147ef56338c76da9c Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:28:49 +0100 Subject: [PATCH 19/25] remove "Live" workflows fully --- examples/Invoke-IdleDemo.ps1 | 8 +++----- tests/Core/CapabilityDeprecation.Tests.ps1 | 4 ++-- tests/Core/ImportIdLE.Tests.ps1 | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/Invoke-IdleDemo.ps1 b/examples/Invoke-IdleDemo.ps1 index b3ebe188..796cf4d3 100644 --- a/examples/Invoke-IdleDemo.ps1 +++ b/examples/Invoke-IdleDemo.ps1 @@ -13,7 +13,7 @@ param( [Parameter(ParameterSetName = 'List')] [Parameter(ParameterSetName = 'Run')] - [ValidateSet('Mock', 'Live', 'Templates', 'All')] + [ValidateSet('Mock', 'Templates', 'All')] [string]$Category = 'Mock', [Parameter(ParameterSetName = 'Run')] @@ -132,7 +132,7 @@ function Get-IdleLifecycleEventFromWorkflowName { function Get-DemoWorkflows { param( [Parameter()] - [ValidateSet('Mock', 'Live', 'Templates', 'All')] + [ValidateSet('Mock', 'Templates', 'All')] [string]$Category = 'Mock' ) @@ -143,7 +143,7 @@ function Get-DemoWorkflows { } $categories = if ($Category -eq 'All') { - @('mock', 'live', 'templates') + @('mock', 'templates') } else { @($Category.ToLowerInvariant()) } @@ -244,8 +244,6 @@ if ($List) { $selected = @(Select-DemoWorkflows -AvailableWorkflows $available -ExampleNames $Example -AllWorkflows:$All) # Note: Mock workflows use New-IdleMockIdentityProvider which works out-of-the-box. -# Live workflows require real providers and will fail if those providers are not available. -# To run Live workflows, users should modify this script to provide the necessary providers. $providers = @{ Identity = New-IdleMockIdentityProvider } diff --git a/tests/Core/CapabilityDeprecation.Tests.ps1 b/tests/Core/CapabilityDeprecation.Tests.ps1 index 43fbf0b9..6b470256 100644 --- a/tests/Core/CapabilityDeprecation.Tests.ps1 +++ b/tests/Core/CapabilityDeprecation.Tests.ps1 @@ -27,7 +27,7 @@ Describe 'Capability Deprecation and Migration' { } # Use a real workflow file that uses mailbox steps - $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'live' 'exo-leaver-mailbox-offboarding.psd1' + $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'template' 'exo-leaver-mailbox-offboarding.psd1' # Verify the workflow file exists $wfPath | Should -Exist @@ -65,7 +65,7 @@ Describe 'Capability Deprecation and Migration' { } # Use a real workflow file - $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'live' 'exo-leaver-mailbox-offboarding.psd1' + $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'template' 'exo-leaver-mailbox-offboarding.psd1' $req = New-IdleLifecycleRequest -LifecycleEvent 'Leaver' $providers = @{ MockProvider = $mockProvider } diff --git a/tests/Core/ImportIdLE.Tests.ps1 b/tests/Core/ImportIdLE.Tests.ps1 index ff356908..4eb9fbaa 100644 --- a/tests/Core/ImportIdLE.Tests.ps1 +++ b/tests/Core/ImportIdLE.Tests.ps1 @@ -12,17 +12,17 @@ Describe 'Import-IdLE helper script' { } It 'import-idle.ps1 finds workflows in subdirectories' { - # The script should find workflows in examples/workflows/mock, live, and templates subdirectories + # The script should find workflows in examples/workflows/mock and templates subdirectories # This test validates that the script can discover workflows after the directory restructuring $workflowDir = Join-Path -Path $repoRoot -ChildPath 'examples/workflows' # Verify workflows exist in subdirectories $mockWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'mock') -Filter '*.psd1' -File -ErrorAction SilentlyContinue - $liveWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'live') -Filter '*.psd1' -File -ErrorAction SilentlyContinue + $templateWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'template') -Filter '*.psd1' -File -ErrorAction SilentlyContinue $mockWorkflows | Should -Not -BeNullOrEmpty - $liveWorkflows | Should -Not -BeNullOrEmpty + $templateWorkflows | Should -Not -BeNullOrEmpty # Verify the script logic for finding workflows recursively $allWorkflows = Get-ChildItem -Path $workflowDir -Filter '*.psd1' -File -Recurse From a01764932eef92d21b015e4770861fe168ee646f Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:34:33 +0100 Subject: [PATCH 20/25] fixed typo templateS --- tests/Core/CapabilityDeprecation.Tests.ps1 | 6 +++--- tests/Core/ImportIdLE.Tests.ps1 | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Core/CapabilityDeprecation.Tests.ps1 b/tests/Core/CapabilityDeprecation.Tests.ps1 index 6b470256..5dd4cfcb 100644 --- a/tests/Core/CapabilityDeprecation.Tests.ps1 +++ b/tests/Core/CapabilityDeprecation.Tests.ps1 @@ -65,14 +65,14 @@ Describe 'Capability Deprecation and Migration' { } # Use a real workflow file - $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'template' 'exo-leaver-mailbox-offboarding.psd1' - + $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'templates' 'exo-leaver-mailbox-offboarding.psd1' + $req = New-IdleLifecycleRequest -LifecycleEvent 'Leaver' $providers = @{ MockProvider = $mockProvider } # Planning should succeed without deprecation warnings $output = New-IdlePlan -WorkflowPath $wfPath -Request $req -Providers $providers 3>&1 - + # Separate plan from warnings $plan = $output | Where-Object { $_ -isnot [System.Management.Automation.WarningRecord] } $warnings = $output | Where-Object { $_ -is [System.Management.Automation.WarningRecord] } diff --git a/tests/Core/ImportIdLE.Tests.ps1 b/tests/Core/ImportIdLE.Tests.ps1 index 4eb9fbaa..f64f4e89 100644 --- a/tests/Core/ImportIdLE.Tests.ps1 +++ b/tests/Core/ImportIdLE.Tests.ps1 @@ -14,16 +14,16 @@ Describe 'Import-IdLE helper script' { It 'import-idle.ps1 finds workflows in subdirectories' { # The script should find workflows in examples/workflows/mock and templates subdirectories # This test validates that the script can discover workflows after the directory restructuring - + $workflowDir = Join-Path -Path $repoRoot -ChildPath 'examples/workflows' - + # Verify workflows exist in subdirectories $mockWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'mock') -Filter '*.psd1' -File -ErrorAction SilentlyContinue - $templateWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'template') -Filter '*.psd1' -File -ErrorAction SilentlyContinue - + $templateWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'templates') -Filter '*.psd1' -File -ErrorAction SilentlyContinue + $mockWorkflows | Should -Not -BeNullOrEmpty $templateWorkflows | Should -Not -BeNullOrEmpty - + # Verify the script logic for finding workflows recursively $allWorkflows = Get-ChildItem -Path $workflowDir -Filter '*.psd1' -File -Recurse $allWorkflows | Should -Not -BeNullOrEmpty From f3b57abee4d5098452325777aa1c3334a4e8def4 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:34:33 +0100 Subject: [PATCH 21/25] fixed typo templateS --- tests/Core/CapabilityDeprecation.Tests.ps1 | 8 ++++---- tests/Core/ImportIdLE.Tests.ps1 | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/Core/CapabilityDeprecation.Tests.ps1 b/tests/Core/CapabilityDeprecation.Tests.ps1 index 6b470256..d3dadd88 100644 --- a/tests/Core/CapabilityDeprecation.Tests.ps1 +++ b/tests/Core/CapabilityDeprecation.Tests.ps1 @@ -27,7 +27,7 @@ Describe 'Capability Deprecation and Migration' { } # Use a real workflow file that uses mailbox steps - $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'template' 'exo-leaver-mailbox-offboarding.psd1' + $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'templates' 'exo-leaver-mailbox-offboarding.psd1' # Verify the workflow file exists $wfPath | Should -Exist @@ -65,14 +65,14 @@ Describe 'Capability Deprecation and Migration' { } # Use a real workflow file - $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'template' 'exo-leaver-mailbox-offboarding.psd1' - + $wfPath = Join-Path $PSScriptRoot '..' '..' 'examples' 'workflows' 'templates' 'exo-leaver-mailbox-offboarding.psd1' + $req = New-IdleLifecycleRequest -LifecycleEvent 'Leaver' $providers = @{ MockProvider = $mockProvider } # Planning should succeed without deprecation warnings $output = New-IdlePlan -WorkflowPath $wfPath -Request $req -Providers $providers 3>&1 - + # Separate plan from warnings $plan = $output | Where-Object { $_ -isnot [System.Management.Automation.WarningRecord] } $warnings = $output | Where-Object { $_ -is [System.Management.Automation.WarningRecord] } diff --git a/tests/Core/ImportIdLE.Tests.ps1 b/tests/Core/ImportIdLE.Tests.ps1 index 4eb9fbaa..f64f4e89 100644 --- a/tests/Core/ImportIdLE.Tests.ps1 +++ b/tests/Core/ImportIdLE.Tests.ps1 @@ -14,16 +14,16 @@ Describe 'Import-IdLE helper script' { It 'import-idle.ps1 finds workflows in subdirectories' { # The script should find workflows in examples/workflows/mock and templates subdirectories # This test validates that the script can discover workflows after the directory restructuring - + $workflowDir = Join-Path -Path $repoRoot -ChildPath 'examples/workflows' - + # Verify workflows exist in subdirectories $mockWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'mock') -Filter '*.psd1' -File -ErrorAction SilentlyContinue - $templateWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'template') -Filter '*.psd1' -File -ErrorAction SilentlyContinue - + $templateWorkflows = Get-ChildItem -Path (Join-Path $workflowDir 'templates') -Filter '*.psd1' -File -ErrorAction SilentlyContinue + $mockWorkflows | Should -Not -BeNullOrEmpty $templateWorkflows | Should -Not -BeNullOrEmpty - + # Verify the script logic for finding workflows recursively $allWorkflows = Get-ChildItem -Path $workflowDir -Filter '*.psd1' -File -Recurse $allWorkflows | Should -Not -BeNullOrEmpty From 63b5806dddf90cb42273a9225f5b54540a5e2fb0 Mon Sep 17 00:00:00 2001 From: Matthias <13959569+blindzero@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:38:23 +0100 Subject: [PATCH 22/25] fixed typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/use/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/use/quickstart.md b/docs/use/quickstart.md index 27501d3d..562509e5 100644 --- a/docs/use/quickstart.md +++ b/docs/use/quickstart.md @@ -37,7 +37,7 @@ List available mock category examples: .\examples\Invoke-IdleDemo.ps1 ``` -Select one of the workflow examples available, that do _not_ use real provider interactions and only use the mock provider interface. +Select one of the workflow examples available, that does _not_ use real provider interactions and only use the mock provider interface. Alternatively, select an example workflow with `-Example` parameter: From 6ab6e8c0fa209488b013015676c9348cb50b3c32 Mon Sep 17 00:00:00 2001 From: Matthias <13959569+blindzero@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:38:56 +0100 Subject: [PATCH 23/25] fixed typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7eea0977..4c7d0004 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -11,7 +11,7 @@ Use these instructions when suggesting or generating changes in this repo (chat, - Contributor workflow + Definition of Done: `CONTRIBUTING.md` - Concepts: `docs/about/concepts.md` - Security + trust boundaries: `docs/about/security.md` -- Extensibiltiy: `docs/extend/extensibility.md` +- Extensibility: `docs/extend/extensibility.md` If anything in this file conflicts with those, the more specific document wins. From e7245ea7962ba1d4d6b458a3a78283a44a1e8481 Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:50:19 +0100 Subject: [PATCH 24/25] grammar and typos --- docs/about/concepts.md | 7 ++++--- docs/about/intro.md | 4 ++-- docs/index.md | 1 - docs/use/installation.md | 1 - docs/use/quickstart.md | 9 +++++---- docs/use/workflows.md | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/about/concepts.md b/docs/about/concepts.md index 619fd15c..b888f134 100644 --- a/docs/about/concepts.md +++ b/docs/about/concepts.md @@ -71,8 +71,7 @@ A **LifecycleRequest** represents the business intent (for example: Joiner, Move ```powershell $Request = New-IdleLifecycleRequest -LifecycleEvent 'Joiner' -IdentityKeys @{ key = 'first.last' -} --DesiredState @{ +} -DesiredState @{ Firstname = 'First' Lastname = 'Last' Mail = 'First.Last@domain.tld' @@ -88,7 +87,7 @@ To enable larger flexibility, you can use placeholders instead of literals to be ```powershell @{ - Name = 'Joiner - Workflow Workflow' + Name = 'Joiner - Example Workflow' LifecycleEvent = 'Joiner' Steps = @( @{ @@ -105,6 +104,8 @@ To enable larger flexibility, you can use placeholders instead of literals to be Provider = 'IdentityProvider' } } + ) +} ``` ### Workflows and Steps diff --git a/docs/about/intro.md b/docs/about/intro.md index eb9bfd24..ca3c2695 100644 --- a/docs/about/intro.md +++ b/docs/about/intro.md @@ -25,7 +25,7 @@ The key idea is to **separate intent from implementation**: JML (joiner/mover/leavers) processes are -- error prune, especially if performed manually +- error prone, especially if performed manually - time consuming and therefore - quite annoying for operators @@ -35,7 +35,7 @@ Identity lifecycle automation often turns into long scripts that are: - hard to test - hard to change safely -Identity Management Systems (IdMS) on the other side are whether complex or expensive (or both of it) and then often do not care about supplementary systems that also need to be covered within the workflows. +Identity Management Systems (IdMS) on the other side are either complex or expensive (or both of it) and then often do not care about supplementary systems that also need to be covered within the workflows. IdLE aims to be: diff --git a/docs/index.md b/docs/index.md index 565c2148..7dfbb5b9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,7 +25,6 @@ Learn how to [use IdLE](use/intro.md) as an operator or admin, e.g. for workflow - [Quickstart](use/quickstart.md) - Run the demo and understand Plan → Execute flow - [Installation](use/installation.md) - Install and import guide (requirements, import options) - [Workflows](use/workflows.md) - Define lifecycle workflows -- [Steps](use/steps.md) - Use and configure steps - [Providers](use/providers.md) - Provider aliases and injection - [Plan Export](use/plan-export.md) - How to use the Plan Exporter (JSON) diff --git a/docs/use/installation.md b/docs/use/installation.md index c64f00c4..184e4c8d 100644 --- a/docs/use/installation.md +++ b/docs/use/installation.md @@ -86,7 +86,6 @@ Import-Module IdLE -Force Import-Module IdLE.Provider.AD -Force ``` - Example (from source): ```powershell diff --git a/docs/use/quickstart.md b/docs/use/quickstart.md index 27501d3d..96767f33 100644 --- a/docs/use/quickstart.md +++ b/docs/use/quickstart.md @@ -37,7 +37,7 @@ List available mock category examples: .\examples\Invoke-IdleDemo.ps1 ``` -Select one of the workflow examples available, that do _not_ use real provider interactions and only use the mock provider interface. +Select one of the workflow examples available, that does _not_ use real provider interactions and only use the mock provider interface. Alternatively, select an example workflow with `-Example` parameter: @@ -47,8 +47,6 @@ Alternatively, select an example workflow with `-Example` parameter: Or run all mock workflows: -* Run all examples: - ```powershell .\examples\Invoke-IdleDemo.ps1 -All ``` @@ -68,7 +66,10 @@ We also provide additional "template" examples, which could be used with live sy .\examples\Invoke-IdleDemo.ps1 -List -Category All ``` -:::warning Use template examples with care as they connect and may cause harm to your live environments. +:::warning + +Use template examples with care as they connect and may cause harm to your live environments. + ::: --- diff --git a/docs/use/workflows.md b/docs/use/workflows.md index 517f3ab7..9a0d2148 100644 --- a/docs/use/workflows.md +++ b/docs/use/workflows.md @@ -14,7 +14,7 @@ A step: - operates on the execution context provided by the engine - may interact with external systems through providers - reports its outcome through status and events -- does _not_ orchestrate other steps and do _not_ control execution flow beyond their own outcome. +- does _not_ orchestrate other steps and does _not_ control execution flow beyond their own outcome. ## Workflow File Format @@ -69,7 +69,7 @@ Required: True ## Steps -Each step represents a distinct action that is performed, based on data defined in the workflow parameters or that are passed by the host's request object and merges with the workflow definition on the plan. +Each step represents a distinct action that is performed based on data defined in the workflow parameters or passed in the host's request object; the engine merges with the workflow definition on the plan. Steps are represented by PowerShell Hashtable objects. @@ -79,11 +79,11 @@ Step types are treated as **contracts**. Prefer fully-qualified ids (module + st Each step type's implementation is made available via a step registry. Additionally, each step type's implementation defines required capabilities for this step. Later, provider implementations are providing these capabilities for the steps. -If a provider selected for a step has not the capabilities available required by the step type, the plan of the workflow with fail. +If a provider selected for a step does not have the capabilities required by the step type, the workflow plan with fail. For a list of available Step Types please see the [Step Type Catalog](../reference/steps.md). -Additionally, you can provide your own custom [extend with custom steps](../extend/steps.md). +Additionally, you can extend IdLE with your own custom steps; see [Extending steps](../extend/steps.md). ### Conditional steps From b10b494b43776f06ad61ef467648e6bedadfecdb Mon Sep 17 00:00:00 2001 From: Matthias <13959569+blindzero@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:53:21 +0100 Subject: [PATCH 25/25] improve wording Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/about/intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/about/intro.md b/docs/about/intro.md index ca3c2695..0011ae8c 100644 --- a/docs/about/intro.md +++ b/docs/about/intro.md @@ -16,7 +16,7 @@ The key idea is to **separate intent from implementation**: - **What** should happen is defined in a **workflow** (data-only configuration). - **How** it happens is implemented by **steps** and **providers** (pluggable modules). - - While **steps** define by StepTypes, which provider-agnostic **capabilities** are required to perform a workflow step + - **steps** define, via StepTypes, which provider-agnostic **capabilities** are required to perform a workflow step - **providers** register to the core and announce the provided **capabilities** and implement the vendor system specific interface ---