diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md deleted file mode 100644 index af279bc5..00000000 --- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +++ /dev/null @@ -1,49 +0,0 @@ -# Summary - -Provide a short summary of the changes. - -## Motivation - -Why is this change needed? What problem does it solve? - -## Type of Change - -Please select the relevant option: - -- [ ] Bug fix -- [ ] New feature -- [ ] Breaking change -- [ ] Documentation update -- [ ] Refactoring / internal improvement - -## Changes - -- -- -- - -## Testing - -Describe how this change was tested. - -- [ ] Unit tests -- [ ] Contract tests -- [ ] Manual testing - -### How to test & review - -If helpful, provide additional information how to test. - -## Checklist - -- [ ] Code follows STYLEGUIDE.md -- [ ] Tests added or updated -- [ ] Documentation updated -- [ ] No UI/auth logic added to IdLE.Core -- [ ] No breaking changes without discussion - -## Related Issues - -Link related issues here (if any). - -Closes #... diff --git a/README.md b/README.md index f243b90d..ad6680e1 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,8 @@ Start here: - [docs/index.md](docs/index.md) – Documentation map - [docs/about/intro.md](docs/about/intro.md) – About IdLE -- [docs/use/intro.md](docs/use/intro.md) – How to use IdLE -- [docs/reference/intro.md](docs/reference/intro.md) - The authoritative IdLE reference +- [docs/use/intro-use.md](docs/use/intro-use.md) – How to use IdLE +- [docs/reference/intro-reference.md](docs/reference/intro-reference.md) - The authoritative IdLE reference --- diff --git a/docs/extend/intro.md b/docs/extend/intro-extend.md similarity index 82% rename from docs/extend/intro.md rename to docs/extend/intro-extend.md index b137164e..3f162cae 100644 --- a/docs/extend/intro.md +++ b/docs/extend/intro-extend.md @@ -22,13 +22,6 @@ It is written for people who want to add or customize functionality, for example - Authentication and session handling - Testing approaches (unit tests, contract tests) -## Where to continue? - -1. [Extensibility](extensibility.md) -2. [Providers](providers.md) -3. [Steps](steps.md) -4. [Events](events.md) - ## Not the right section? -If you want to run workflows as an operator/admin, go to [Use Idle](../use/intro.md) instead. +If you want to run workflows as an operator/admin, go to [Use Idle](../use/intro-use.md) instead. diff --git a/docs/index.md b/docs/index.md index 7dfbb5b9..4180ec71 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ Learn the basics [about IdLE](about/intro.md) ## Use IdLE -Learn how to [use IdLE](use/intro.md) as an operator or admin, e.g. for workflow authoring. +Learn how to [use IdLE](use/intro-use.md) as an operator or admin, e.g. for workflow authoring. - [Quickstart](use/quickstart.md) - Run the demo and understand Plan → Execute flow - [Installation](use/installation.md) - Install and import guide (requirements, import options) @@ -30,7 +30,7 @@ Learn how to [use IdLE](use/intro.md) as an operator or admin, e.g. for workflow ## Extend IdLE -Learn how to [extend IdLE](extend/intro.md) as a developer. +Learn how to [extend IdLE](extend/intro-extend.md) as a developer. - [Extensibility](extend/extensibility.md) - General extensibility concept of IdLE - [Events](extend/events.md) - Eventing at IdLE to be used in your extensions @@ -38,20 +38,12 @@ Learn how to [extend IdLE](extend/intro.md) as a developer. ## Reference -The [authoritative reference](reference/intro.md) for IdLE components. +The [authoritative reference](reference/intro-reference.md) for IdLE components. -- [Cmdlets](reference/cmdlets.md) - Public cmdlets and usage -- [Step Catalog](reference/steps.md) - Built-in step reference (generated) -- [Capabilities](reference/capabilities.md) - The capabilities catalog - -## Workflow Examples - -- [Workflow Examples](../examples/README.md) - Runnable examples and demo workflows - -### Provider Reference - -- [Active Directory Provider](reference/providers/provider-ad.md) -- [Entra ID Provider](reference/providers/provider-entraID.md) +- [Cmdlet Reference](reference/cmdlets.md) +- [Provider Reference](reference/providers.md) +- [Step Reference](reference/steps.md) +- [Capabilities](reference/capabilities.md) ### Specifications @@ -60,6 +52,10 @@ used between IdLE and its hosts. - [Plan export (JSON)](reference/specs/plan-export.md) - The JSON specification of the Plan export file +### Workflow Examples + +- [Workflow Examples](../examples/README.md) - Runnable examples and demo workflows + --- ## Developer Documentation diff --git a/docs/reference/cmdlets.md b/docs/reference/cmdlets.md index 96558f22..54c2435c 100644 --- a/docs/reference/cmdlets.md +++ b/docs/reference/cmdlets.md @@ -1,4 +1,4 @@ -# Cmdlet Reference +# Cmdlets > Generated file. Do not edit by hand. > Source: tools/Generate-IdleCmdletReference.ps1 diff --git a/docs/reference/intro.md b/docs/reference/intro-reference.md similarity index 64% rename from docs/reference/intro.md rename to docs/reference/intro-reference.md index 4f19a58e..8f1ecd23 100644 --- a/docs/reference/intro.md +++ b/docs/reference/intro-reference.md @@ -6,15 +6,8 @@ sidebar_label: Reference This section is the **authoritative reference** for IdLE. It contains precise, stable and normative information intended for lookup and verification. -Content here is not tutorial-style and usually assumes prior knowledge from the **Use** or -**Extend** sections. - -## What you will find here - -- Cmdlet reference -- Step reference -- Capability reference -- Specifications (schemas, formats, contracts) +Content here is not tutorial-style and usually assumes prior knowledge from the **[Use](../use/intro-use.md)** or +**[Extend](../extend/intro-extend.md)** sections. ## How to use this section @@ -22,6 +15,11 @@ Content here is not tutorial-style and usually assumes prior knowledge from the - Verify assumptions during development or review - Link from guides and concepts to authoritative definitions -## Not the right section? +## What you will find here -If you are looking for guidance or explanations, start with [Use](../use/intro.md) or [Extend](../extend/intro.md) instead. +- [Cmdlet reference](./cmdlets.md) +- [Step reference](./steps.md) +- [Providers](./providers.md) +- [Capability reference](capabilities.md) +- Specifications (schemas, formats, contracts) + - [Plan Export JSON Schema](./specs/plan-export.md) diff --git a/docs/reference/providers.md b/docs/reference/providers.md new file mode 100644 index 00000000..894d95ba --- /dev/null +++ b/docs/reference/providers.md @@ -0,0 +1,41 @@ +--- +title: Providers Reference +sidebar_label: Providers +--- + +> Entry page for provider reference. This section is **reference-only**: what a provider implements, how to configure it, and which contracts/capabilities it exposes. + +--- + +## Built-in / first-party providers + +- **[Active Directory (AD)](providers/provider-ad.md)** — Identity operations against on-prem AD via the AD provider module +- **[Entra ID](providers/provider-entraID.md)** — Identity operations against Microsoft Entra ID via Microsoft Graph +- **[Exchange Online](providers/provider-exchangeonline.md)** — Messaging / mailbox related operations against Exchange Online +- **[DirectorySync.EntraConnect](providers/provider-directorysync-entraconnect.md)** — Directory synchronization provider for Entra Connect / sync-cycle related operations +- **[Mock Provider](providers/provider-mock.md)** — In-memory / file-backed provider for tests and local development without live systems + +--- + +## Choosing a provider + +- Match the **capabilities required by your steps** to the provider’s `GetCapabilities()` output. +- Steps acquire auth sessions via `Context.AcquireAuthSession(...)` and pass them to provider methods that accept an optional `AuthSession` parameter (host-controlled). +- In workflows, steps select a provider by **alias** (defaults to `Identity` if omitted). + +Related: + +- [Capabilities Reference](capabilities.md) +- [Provider fundamentals (concept)](../about/concepts.md#providers) +- [Use Providers](../use/providers.md) + +--- + +## Authoring a provider (for developers) + +Minimal checklist: + +- Implement provider contracts (only what you need) +- Advertise deterministic capabilities (`GetCapabilities()`) +- Accept optional `AuthSession` parameter in methods that require authentication (sessions acquired by steps via host context; no prompts inside providers) +- Add unit tests + contract tests (no live calls in CI) diff --git a/docs/reference/providers/_provider-name_template.md b/docs/reference/providers/_provider-name_template.md new file mode 100644 index 00000000..9b119a30 --- /dev/null +++ b/docs/reference/providers/_provider-name_template.md @@ -0,0 +1,191 @@ +# Provider Reference Template + +> **Purpose:** This page is a **reference** for a specific provider implementation. +> Keep it factual and contract-oriented. Put conceptual explanations elsewhere and link to them. + +--- + +## Summary + +- **Provider name:** `` +- **Module:** `` (e.g. `IdLE.Provider.*`) +- **Provider kind:** `` +- **Targets:** `` +- **Status:** `` +- **Since:** `` (optional) +- **Compatibility:** PowerShell 7+ (IdLE requirement) + +--- + +## What this provider does + +- **Primary responsibilities:** + - `` + - `` +- **Out of scope / non-goals:** + - `` + - `` + +--- + +## Contracts and capabilities + +### Contracts implemented + +List the IdLE provider contracts this provider implements and what they mean at a glance. + +| Contract | Used by steps for | Notes | +| --- | --- | --- | +| `` | `` | `` | +| `` | `` | `` | + +> Keep the contract list stable and link to the canonical contract reference. + +### Capability advertisement (`GetCapabilities()`) + +- **Implements `GetCapabilities()`**: `` +- **Capabilities returned (stable identifiers):** + - `` + - `` + - `` + +--- + +## Authentication and session acquisition + +> Providers must not prompt for auth. Use the host-provided broker contract. + +- **Auth session name(s) requested via `Context.AcquireAuthSession(...)`:** + - `` +- **Session options (data-only):** + - ``: `` — `` (optional default: `<...>`) + +:::warning + +**Security notes** + +- Do not pass secrets in provider options. +- Ensure token/credential objects are not emitted in events. + +::: + +--- + +## Configuration + +### Provider constructor / factory + +How to create an instance. + +- **Public constructor cmdlet(s):** + - `` — `` + +**Parameters (high signal only)** + +- `-Name ` — `<...>` +- `-Options ` — `<...>` + +> Do not copy full comment-based help here. Link to the cmdlet reference. + +### Provider bag / alias usage + +How to pass the provider instance to IdLE as part of the host's provider map. + +```powershell +$providers = @{ + = +} +``` + +- **Recommended alias pattern:** `` +- **Default alias expected by built-in steps (if any):** `` (if applicable) + +--- + +## Provider-specific options reference + +> Document only **data-only** keys. Keep this list short and unambiguous. + +| Option key | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `` | `` | `` | `<...>` | `<...>` | +| `` | `` | `` | `<...>` | `<...>` | + +--- + +## Operational behavior + +### Idempotency and consistency + +- **Idempotent operations:** `` +- **Consistency model:** `` +- **Concurrency notes:** `` + +### Error mapping and retry behavior + +- **Common error categories:** `` +- **Retry strategy:** `` + +--- + +## Observability + +- **Events emitted by provider (if any):** + - `` — `` — `` +- **Sensitive data redaction:** `` + +--- + +## Examples + +### Minimal host usage + +```powershell +# 1) Create provider instance +$provider = + +# 2) Build provider map +$providers = @{ = $provider } + +# 3) Plan + execute +$plan = New-IdlePlan -WorkflowPath -Request -Providers $providers +$result = Invoke-IdlePlan -Plan $plan -Providers $providers +``` + +### Example workflow snippet + +```powershell +@{ + Steps = @( + @{ + Name = '' + Type = '' + With = @{ + Provider = '' + # ... + } + } + ) +} +``` + +--- + +## Limitations and known issues + +- `` +- `` + +--- + +## Testing + +- **Unit tests:** `` +- **Contract tests:** `` +- **Known CI constraints:** `` + +--- + +## Changelog (optional) + +- `` — `` diff --git a/docs/reference/providers/provider-ad.md b/docs/reference/providers/provider-ad.md index 998654f7..7b23467a 100644 --- a/docs/reference/providers/provider-ad.md +++ b/docs/reference/providers/provider-ad.md @@ -1,4 +1,7 @@ -# IdLE.Provider.AD - Active Directory Provider +--- +title: Provider Reference - IdLE.Provider.AD (Active Directory) +sidebar_label: Active Directory +--- ## Overview @@ -83,6 +86,71 @@ This makes `New-IdleADIdentityProvider` available in your session. --- +## Authentication and session acquisition + +> Providers must not prompt for auth. Use the host-provided broker contract. + +- **Auth session name(s) used by built-in steps:** `ActiveDirectory` +- **Auth session formats supported:** + - `null` (integrated authentication / run-as) + - `PSCredential` (used for AD cmdlets `-Credential`) +- **Session options (data-only):** Any hashtable; commonly `@{ Role = 'Tier0' }` / `@{ Role = 'Admin' }` + +:::warning + +**Security notes** + +- Do not pass secrets in workflow files or provider options. +- Make sure your host does not emit credential objects (or their secure strings) in logs/events. + +::: + +### Auth examples + +**A) Integrated authentication (no broker)** + +```powershell +# Run the host under an account that already has the required AD permissions. +$providers = @{ + Identity = New-IdleADIdentityProvider +} +``` + +**B) Role-based routing with `New-IdleAuthSession` (typical Tier0/Admin)** + +```powershell +$tier0Credential = Get-Credential -Message 'Enter Tier0 AD admin credentials' +$adminCredential = Get-Credential -Message 'Enter AD admin credentials' + +$broker = New-IdleAuthSession -SessionMap @{ + @{ Role = 'Tier0' } = $tier0Credential + @{ Role = 'Admin' } = $adminCredential +} -DefaultCredential $adminCredential + +$providers = @{ + Identity = New-IdleADIdentityProvider + AuthSessionBroker = $broker +} + +# In the workflow step: +# With.AuthSessionName = 'ActiveDirectory' +# With.AuthSessionOptions = @{ Role = 'Tier0' } +``` + +**C) Multi-forest / multi-domain routing** + +```powershell +$sourceCred = Get-Credential -Message 'Enter credentials for source forest' +$targetCred = Get-Credential -Message 'Enter credentials for target forest' + +$broker = New-IdleAuthSession -SessionMap @{ + @{ Domain = 'SourceForest' } = $sourceCred + @{ Domain = 'TargetForest' } = $targetCred +} + +# Steps use With.AuthSessionOptions = @{ Domain = 'SourceForest' } etc. +``` + ## Usage ### Basic Usage (Integrated Auth) diff --git a/docs/reference/providers/provider-directorysync-entraconnect.md b/docs/reference/providers/provider-directorysync-entraconnect.md new file mode 100644 index 00000000..0d42b067 --- /dev/null +++ b/docs/reference/providers/provider-directorysync-entraconnect.md @@ -0,0 +1,228 @@ +--- +title: Provider Reference - IdLE.Provider.DirectorySync.EntraConnect +sidebar_label: DirectorySync.EntraConnect +--- + +> **Purpose:** This page is a **reference** for a specific provider implementation. +> Keep it factual and contract-oriented. Put conceptual explanations elsewhere and link to them. + +--- + +## Summary + +- **Provider name:** EntraConnect DirectorySync +- **Module:** `IdLE.Provider.DirectorySync.EntraConnect` +- **Provider kind:** Other +- **Targets:** Entra ID Connect (ADSync) sync scheduler on a Windows server (remote execution) +- **Status:** First-party (bundled) +- **Since:** 0.9.0 +- **Compatibility:** PowerShell 7+ (IdLE requirement) + +--- + +## What this provider does + +- **Primary responsibilities:** + - Trigger an Entra Connect sync cycle (`Delta` or `Initial`). + - Query sync cycle state (whether a cycle is in progress). +- **Out of scope / non-goals:** + - Establishing remote connectivity, authentication, or elevation (handled by the host/broker). + - Installing or configuring Entra Connect / ADSync. + +--- + +## Contracts and capabilities + +### Contracts implemented + +| Contract | Used by steps for | Notes | +| --- | --- | --- | +| Directory sync provider (implicit) | Trigger and monitor directory sync cycles | Exposes `StartSyncCycle(PolicyType, AuthSession)` and `GetSyncCycleState(AuthSession)` as script methods. | + +### Capability advertisement (`GetCapabilities()`) + +- **Implements `GetCapabilities()`**: Yes +- **Capabilities returned (stable identifiers):** + - `IdLE.DirectorySync.Trigger` + - `IdLE.DirectorySync.Status` + +--- + +## Authentication and session acquisition + +> Providers must not prompt for auth. Use the host-provided broker contract. + +This provider expects a host-provided **AuthSession** object that implements: + +- `InvokeCommand(CommandName, Parameters)` + +The provider does not call `Context.AcquireAuthSession(...)` directly; IdLE steps acquire an auth session +and pass it to provider methods. + +- **Auth session name(s) used by built-in steps:** + - `DirectorySync` (see `IdLE.Step.TriggerDirectorySync`) +- **Session options (data-only):** + - Forwarded to the host broker for session selection (provider does not interpret option keys). + +:::warning + +**Security notes** + +- Do not pass secrets in provider options. +- Ensure token/credential objects are not emitted in events. + +::: + +### Auth examples + +**A) Simple WinRM/PowerShell Remoting wrapper (typical)** + +The provider expects an auth session object with an `InvokeCommand(CommandName, Parameters)` method. +Your host can wrap `Invoke-Command` like this: + +```powershell +$syncCred = Get-Credential -Message 'Enter credentials for Entra Connect server' + +$authSession = [pscustomobject]@{ + ComputerName = 'entra-connect-01.contoso.local' + Credential = $syncCred +} +$authSession | Add-Member -MemberType ScriptMethod -Name InvokeCommand -Value { + param([string] $CommandName, [hashtable] $Parameters) + + Invoke-Command -ComputerName $this.ComputerName -Credential $this.Credential -ScriptBlock { + param($cmd, $params) + & $cmd @params + } -ArgumentList $CommandName, $Parameters +} + +$broker = [pscustomobject]@{} +$broker | Add-Member -MemberType ScriptMethod -Name AcquireAuthSession -Value { + param($Name, $Options) + return $authSession +} + +$providers = @{ + DirectorySync = New-IdleEntraConnectDirectorySyncProvider + AuthSessionBroker = $broker +} + +# Steps use With.AuthSessionName = 'DirectorySync' +``` + +**B) Role-based routing (Tier0 vs. Admin)** + +```powershell +$tier0 = New-EntraConnectAuthSession -ComputerName 'entra-connect-01' -Credential (Get-Credential) +$admin = New-EntraConnectAuthSession -ComputerName 'entra-connect-01' -Credential (Get-Credential) + +$broker = [pscustomobject]@{} +$broker | Add-Member -MemberType ScriptMethod -Name AcquireAuthSession -Value { + param($Name, $Options) + if ($Options.Role -eq 'Tier0') { return $tier0 } + return $admin +} +``` + +> Note: if `Start-ADSyncSyncCycle` requires elevation on your server, handle that in the host +> (scheduled task, JEA endpoint, endpoint configuration), not inside the provider. + +--- + +## Configuration + +### Provider constructor / factory + +- **Public constructor cmdlet(s):** + - `New-IdleEntraConnectDirectorySyncProvider` — Creates a provider instance. + +> Do not copy full comment-based help here. Link to the cmdlet reference. + +### Provider bag / alias usage + +```powershell +$providers = @{ + DirectorySync = New-IdleEntraConnectDirectorySyncProvider +} +``` + +- **Recommended alias pattern:** `DirectorySync` +- **Default alias expected by built-in steps (if any):** `DirectorySync` (used by `IdLE.Step.TriggerDirectorySync`) + +--- + +## Provider-specific options reference + +This provider has **no provider-specific option bag**. Runtime behavior depends on the host-provided AuthSession. + +--- + +## Operational behavior + +### Idempotency and consistency + +- **Idempotent operations:** Partial (triggering a sync cycle is an action; the step may optionally wait for completion) +- **Consistency model:** Depends on the directory synchronization runtime +- **Concurrency notes:** + - Triggering a new cycle may fail if a cycle is already in progress. + +### Error mapping and retry behavior + +- **Common error categories:** `PermissionDenied/ElevationRequired`, `Throttled/Busy`, `RemoteExecutionFailed` +- **Retry strategy:** None in the provider; any retries/backoff should be handled by the host or by the calling step. + +--- + +## Observability + +- **Events emitted by provider (if any):** None +- **Sensitive data redaction:** Enforced by IdLE output-boundary redaction; hosts should ensure the AuthSession does not leak secrets. + +--- + +## Examples + +### Minimal host usage + +```powershell +# 1) Create provider instance +$provider = New-IdleEntraConnectDirectorySyncProvider + +# 2) Build provider map +$providers = @{ + DirectorySync = $provider + AuthSessionBroker = $broker # host-provided +} + +# 3) Plan + execute +$plan = New-IdlePlan -WorkflowPath .\workflow.psd1 -Request $request -Providers $providers +$result = Invoke-IdlePlan -Plan $plan -Providers $providers +``` + +### Example workflow snippet + +```powershell +@{ + Steps = @( + @{ + Name = 'Trigger directory sync' + Type = 'IdLE.Step.TriggerDirectorySync' + With = @{ + Provider = 'DirectorySync' + AuthSessionName = 'DirectorySync' + PolicyType = 'Delta' + Wait = $true + TimeoutSeconds = 600 + PollIntervalSeconds = 10 + } + } + ) +} +``` + +--- + +## Limitations and known issues + +- Requires an elevated remote execution context on the Entra Connect server. +- The remote target must have the ADSync cmdlets available (`Start-ADSyncSyncCycle`, `Get-ADSyncScheduler`). diff --git a/docs/reference/providers/provider-entraID.md b/docs/reference/providers/provider-entraID.md index a0b64ed8..cc064b27 100644 --- a/docs/reference/providers/provider-entraID.md +++ b/docs/reference/providers/provider-entraID.md @@ -1,4 +1,7 @@ -# IdLE.Provider.EntraID Reference +--- +title: Provider Reference - IdLE.Provider.EntraID +sidebar_label: Entra ID +--- Microsoft Entra ID (formerly Azure Active Directory) identity provider for IdLE. @@ -92,6 +95,94 @@ $broker = New-IdleAuthSession -SessionMap @{ # Workflow steps specify: With.AuthSessionOptions = @{ Role = 'Tier0' } ``` + + +> Providers must not prompt for auth. Use the host-provided broker contract. + +- **Auth session name(s) used by built-in steps:** `MicrosoftGraph` +- **Auth session formats supported:** + - `string` Bearer access token + - object with `AccessToken` property + - object with `GetAccessToken()` method + - `PSCredential` (token stored in password field; username is ignored) +- **Session options (data-only):** Any hashtable; common keys: `Role`, `Tenant`, `Environment` + +:::warning + +**Security notes** + +- Do not pass secrets in workflow files or provider options. +- If you use access tokens, ensure your host does not log them (events, transcripts, verbose output). + +::: + +### Auth examples + +**A) Delegated auth (interactive) – host obtains token, provider consumes token** + +```powershell +# Host responsibility: +# Example with Microsoft Graph PowerShell (interactive sign-in) +Connect-MgGraph -Scopes 'User.ReadWrite.All','Group.ReadWrite.All' | Out-Null +$ctx = Get-MgContext + +# Provide a token supplier object so tokens can refresh +$tokenSupplier = [pscustomobject]@{ Context = $ctx } +$tokenSupplier | Add-Member -MemberType ScriptMethod -Name GetAccessToken -Value { + # NOTE: Replace this with your real token retrieval logic. + # In many hosts you would acquire tokens via MSAL / managed identity. + throw 'Implement token acquisition in the host.' +} + +$broker = [pscustomobject]@{} +$broker | Add-Member -MemberType ScriptMethod -Name AcquireAuthSession -Value { + param($Name, $Options) + return $tokenSupplier +} + +$providers = @{ + Identity = New-IdleEntraIDIdentityProvider + AuthSessionBroker = $broker +} + +# Steps use: +# With.AuthSessionName = 'MicrosoftGraph' +``` + +**B) App-only auth – host supplies a fixed token string (simple demo / lab)** + +```powershell +$accessToken = Get-MyGraphAppOnlyToken # host-managed (MSAL / managed identity / etc.) + +$broker = [pscustomobject]@{} +$broker | Add-Member -MemberType ScriptMethod -Name AcquireAuthSession -Value { + param($Name, $Options) + return $accessToken +} + +$providers = @{ + Identity = New-IdleEntraIDIdentityProvider + AuthSessionBroker = $broker +} +``` + +**C) Multi-tenant routing** + +```powershell +$tokenProd = Get-GraphToken -Tenant 'contoso.onmicrosoft.com' +$tokenLab = Get-GraphToken -Tenant 'contoso-lab.onmicrosoft.com' + +$broker = [pscustomobject]@{} +$broker | Add-Member -MemberType ScriptMethod -Name AcquireAuthSession -Value { + param($Name, $Options) + if ($Options.Tenant -eq 'Prod') { return $tokenProd } + if ($Options.Tenant -eq 'Lab') { return $tokenLab } + throw "Unknown tenant option: $($Options.Tenant)" +} + +# Steps use With.AuthSessionOptions = @{ Tenant = 'Prod' } etc. +``` + ## Required Microsoft Graph Permissions ### Delegated Permissions (User Context) diff --git a/docs/reference/providers/provider-exchangeonline.md b/docs/reference/providers/provider-exchangeonline.md new file mode 100644 index 00000000..82f6eb48 --- /dev/null +++ b/docs/reference/providers/provider-exchangeonline.md @@ -0,0 +1,205 @@ +--- +title: Provider Reference - IdLE.Provider.ExchangeOnline +sidebar_label: ExchangeOnline +--- + +> **Purpose:** This page is a **reference** for a specific provider implementation. +> Keep it factual and contract-oriented. Put conceptual explanations elsewhere and link to them. + +--- + +## Summary + +- **Provider name:** ExchangeOnline +- **Module:** `IdLE.Provider.ExchangeOnline` +- **Provider kind:** Messaging +- **Targets:** Exchange Online (ExchangeOnlineManagement cmdlets) +- **Status:** First-party (bundled) +- **Since:** 0.9.0 +- **Compatibility:** PowerShell 7+ (IdLE requirement) + +--- + +## What this provider does + +- **Primary responsibilities:** + - Read mailbox information (type, primary SMTP, UPN, GUID). + - Converge mailbox type (User/Shared/Room/Equipment). + - Converge Out of Office configuration. +- **Out of scope / non-goals:** + - Establishing an Exchange Online session (handled by the host/broker). + - Managing identity objects (use an identity provider such as AD or EntraID). + +--- + +## Contracts and capabilities + +### Contracts implemented + +| Contract | Used by steps for | Notes | +| --- | --- | --- | +| Mailbox provider (implicit) | Read mailbox info, ensure mailbox type, ensure Out of Office | Methods are exposed as script methods on the provider object. | + +### Capability advertisement (`GetCapabilities()`) + +- **Implements `GetCapabilities()`**: Yes +- **Capabilities returned (stable identifiers):** + - `IdLE.Mailbox.Info.Read` + - `IdLE.Mailbox.Type.Ensure` + - `IdLE.Mailbox.OutOfOffice.Ensure` + +--- + +## Authentication and session acquisition + +> Providers must not prompt for auth. Use the host-provided broker contract. + +- **Auth session name(s) requested via `Context.AcquireAuthSession(...)`:** + - Typically the step passes `With.AuthSessionName` (if present). For built-in mailbox steps, if `With.AuthSessionName` is absent, it defaults to the provider alias (commonly `ExchangeOnline`). +- **Session options (data-only):** + - The provider does not interpret options; they are used by the host/broker to select credentials/route to a tenant/session. + +:::warning + +**Security notes** + +- Do not pass secrets in workflow/provider options. +- Ensure token/credential objects are not emitted in events. + +::: + +### Auth examples + +**A) Delegated auth (interactive) – connect once in the host** + +```powershell +# Host responsibility: +Connect-ExchangeOnline -UserPrincipalName 'admin@contoso.com' + +$providers = @{ + ExchangeOnline = New-IdleExchangeOnlineProvider +} +``` + +**B) App-only (certificate) – connect once in the host** + +```powershell +# Host responsibility: +Connect-ExchangeOnline ` + -AppId '00000000-0000-0000-0000-000000000000' ` + -Organization 'contoso.onmicrosoft.com' ` + -CertificateThumbprint 'THUMBPRINT' + +$providers = @{ + ExchangeOnline = New-IdleExchangeOnlineProvider +} +``` + +**C) Multi-connection routing (advanced)** + +If you need **multiple** Exchange Online sessions (e.g., multiple tenants), implement a custom +`AuthSessionBroker` that returns an **AuthSession** object understood by your host (for example, +an object that selects the right connection context before invoking cmdlets). The provider itself +does not create or own sessions. + +--- + +## Configuration + +### Provider constructor / factory + +- **Public constructor cmdlet(s):** + - `New-IdleExchangeOnlineProvider` — creates an Exchange Online mailbox provider. + +**Parameters (high signal only)** + +- `-Adapter ` — dependency injection hook for tests (optional). + +> Do not copy full comment-based help here. Link to the cmdlet reference. + +### Provider bag / alias usage + +```powershell +$providers = @{ + ExchangeOnline = (New-IdleExchangeOnlineProvider) +} +``` + +- **Recommended alias pattern:** `ExchangeOnline` (or role-based, e.g. `Messaging`) +- **Default alias expected by built-in steps (if any):** `ExchangeOnline` (Mailbox steps default to this when `With.Provider` is not provided) + +--- + +## Provider-specific options reference + +This provider has no dedicated data-only `-Options` surface. Session selection is done via: + +- `With.AuthSessionName` +- `With.AuthSessionOptions` (data-only hashtable, validated by the engine/steps) + +--- + +## Operational behavior + +### Idempotency and consistency + +- **Idempotent operations:** Yes (for `Ensure*` methods; no-op when already in desired state) +- **Consistency model:** Depends on Exchange Online / service latency +- **Concurrency notes:** Exchange Online can throttle; retries are delegated to the host/workflow design. + +### Error mapping and retry behavior + +- **Common error categories:** NotFound, PermissionDenied, Throttled +- **Retry strategy:** None in the provider (delegate retries/backoff to the host if needed) + +--- + +## Observability + +- **Events emitted by provider (if any):** None (steps emit events via the execution context). +- **Sensitive data redaction:** IdLE redacts secrets at output boundaries; providers should avoid returning secret material. + +--- + +## Examples + +### Minimal host usage + +```powershell +# 1) Create provider instance +$provider = New-IdleExchangeOnlineProvider + +# 2) Build provider map +$providers = @{ ExchangeOnline = $provider } + +# 3) Plan + execute +$plan = New-IdlePlan -WorkflowPath -Request -Providers $providers +$result = Invoke-IdlePlan -Plan $plan -Providers $providers +``` + +### Example workflow snippet + +```powershell +@{ + Steps = @( + @{ + Name = 'Ensure mailbox type' + Type = 'IdLE.Step.MailboxType.Ensure' + With = @{ + Provider = 'ExchangeOnline' + IdentityKey = 'user@contoso.com' + Type = 'Shared' + # AuthSessionName is optional; defaults to the provider alias if omitted + # AuthSessionOptions = @{ ... } + } + } + ) +} +``` + +--- + +## Limitations and known issues + +- Requires the `ExchangeOnlineManagement` PowerShell module at runtime. +- The host must establish or broker a usable Exchange Online session; the provider does not connect interactively. diff --git a/docs/reference/providers/provider-mock.md b/docs/reference/providers/provider-mock.md new file mode 100644 index 00000000..2bfe82a5 --- /dev/null +++ b/docs/reference/providers/provider-mock.md @@ -0,0 +1,180 @@ +--- +title: Provider Reference - IdLE.Provider.Mock +sidebar_label: Mock +--- + +> **Purpose:** This page is a **reference** for a specific provider implementation. +> Keep it factual and contract-oriented. Put conceptual explanations elsewhere and link to them. + +--- + +## Summary + +- **Provider name:** MockIdentity +- **Module:** `IdLE.Provider.Mock` +- **Provider kind:** Identity + Entitlement +- **Targets:** In-memory store (tests/demos) +- **Status:** First-party (bundled) +- **Since:** 0.9.0 +- **Compatibility:** PowerShell 7+ (IdLE requirement) + +--- + +## What this provider does + +- **Primary responsibilities:** + - Provide deterministic, in-memory identity operations for tests and examples. + - Converge identity attributes. + - List, grant and revoke entitlements (in-memory). + - Avoid any external dependencies and avoid global state. +- **Out of scope / non-goals:** + - Any live system integration. + - Authentication and session handling. + +--- + +## Contracts and capabilities + +### Contracts implemented + +| Contract | Used by steps for | Notes | +| --- | --- | --- | +| Identity provider (implicit) | Read identities and ensure attributes | Creates missing identities on demand to keep demos frictionless. | +| Entitlement provider (implicit) | List/grant/revoke entitlements | Entitlements are normalized to `{ Kind; Id; DisplayName? }` and compared case-insensitively by `Id`. | + +### Capability advertisement (`GetCapabilities()`) + +- **Implements `GetCapabilities()`**: Yes +- **Capabilities returned (stable identifiers):** + - `IdLE.Identity.Read` + - `IdLE.Identity.Attribute.Ensure` + - `IdLE.Identity.Disable` + - `IdLE.Entitlement.List` + - `IdLE.Entitlement.Grant` + - `IdLE.Entitlement.Revoke` + +--- + +## Authentication and session acquisition + +This provider does not require authentication. + +:::warning + +**Security notes** + +- Even in tests, do not embed real secrets into workflow files or fixtures. + +::: + +### Auth examples + +This provider does not require authentication. + +```powershell +$providers = @{ + Identity = New-IdleMockIdentityProvider +} +``` + +--- + +## Configuration + +### Provider constructor / factory + +- **Public constructor cmdlet(s):** + - `New-IdleMockIdentityProvider` — creates an isolated in-memory provider instance. + +**Parameters (high signal only)** + +- `-InitialStore ` — optional initial content, shallow-copied into the provider store. + +### Provider bag / alias usage + +```powershell +$provider = New-IdleMockIdentityProvider + +$providers = @{ + Identity = $provider +} +``` + +- **Recommended alias pattern:** `Identity` +- **Default alias expected by built-in steps (if any):** `Identity` + +--- + +## Provider-specific options reference + +This provider has no additional data-only option keys beyond its constructor parameters. + +--- + +## Operational behavior + +### Idempotency and consistency + +- **Idempotent operations:** Partial + - `EnsureAttribute` is idempotent (returns `Changed = $false` when already converged). + - `DisableIdentity` is idempotent. + - Entitlement grant/revoke are idempotent by Kind+Id. + - `GetIdentity` creates missing identities on demand (test convenience). +- **Consistency model:** Strong (in-memory) +- **Concurrency notes:** Not designed for concurrent mutation across threads/runspaces. + +### Error mapping and retry behavior + +- **Common error categories:** input validation errors (e.g., missing entitlement id) +- **Retry strategy:** none (deterministic, in-memory) + +--- + +## Observability + +- **Events emitted by provider (if any):** none +- **Sensitive data redaction:** not applicable (no auth material handled) + +--- + +## Examples + +### Minimal host usage + +```powershell +# 1) Create provider instance +$provider = New-IdleMockIdentityProvider + +# 2) Build provider map +$providers = @{ Identity = $provider } + +# 3) Plan + execute +$plan = New-IdlePlan -WorkflowPath -Request -Providers $providers +$result = Invoke-IdlePlan -Plan $plan -Providers $providers +``` + +### Example workflow snippet + +```powershell +@{ + Steps = @( + @{ + Name = 'Ensure department' + Type = 'IdLE.Step.EnsureAttribute' + With = @{ + Provider = 'Identity' + IdentityKey = 'user1' + Name = 'Department' + Value = 'IT' + } + } + ) +} +``` + +--- + +## Limitations and known issues + +- Designed for tests and examples only. +- `GetIdentity` auto-creates missing identities, which may hide "NotFound" scenarios unless tests seed the store explicitly. diff --git a/docs/reference/steps.md b/docs/reference/steps.md index ca5fc9da..9b6ddee5 100644 --- a/docs/reference/steps.md +++ b/docs/reference/steps.md @@ -1,332 +1,16 @@ -# Step Catalog +# Steps > Generated file. Do not edit by hand. > Source: tools/Generate-IdleStepReference.ps1 -This page documents built-in IdLE steps discovered from `Invoke-IdleStep*` functions in `IdLE.Steps.*` modules. - ---- - -## CreateIdentity - -- **Step Name**: `CreateIdentity` -- **Implementation**: `Invoke-IdleStepCreateIdentity` -- **Idempotent**: `Yes` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Creates a new identity in the target system. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>] that implements CreateIdentity(identityKey, attributes) -and returns an object with properties 'IdentityKey' and 'Changed'. - -The step is idempotent by design: if the identity already exists, the provider -should return Changed = $false without creating a duplicate. - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider method - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -| Key | Required | -| --- | --- | -| IdentityKey | Yes | -| Attributes | Yes | - ---- - -## DeleteIdentity - -- **Step Name**: `DeleteIdentity` -- **Implementation**: `Invoke-IdleStepDeleteIdentity` -- **Idempotent**: `Yes` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Deletes an identity from the target system. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>] that implements DeleteIdentity(identityKey) -and returns an object with properties 'IdentityKey' and 'Changed'. - -The step is idempotent by design: if the identity is already deleted, the provider -should return Changed = $false. - -IMPORTANT: This step requires the provider to advertise the IdLE.Identity.Delete -capability, which is typically opt-in for safety. The provider must be configured -to allow deletion (e.g., AllowDelete = $true for AD provider). - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider method - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ - ---- - -## DisableIdentity - -- **Step Name**: `DisableIdentity` -- **Implementation**: `Invoke-IdleStepDisableIdentity` -- **Idempotent**: `Yes` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Disables an identity in the target system. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>] that implements DisableIdentity(identityKey) -and returns an object with properties 'IdentityKey' and 'Changed'. - -The step is idempotent by design: if the identity is already disabled, the provider -should return Changed = $false. - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider method - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ - ---- - -## EmitEvent - -- **Step Name**: `EmitEvent` -- **Implementation**: `Invoke-IdleStepEmitEvent` -- **Idempotent**: `Unknown` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Emits a custom event (demo step). - -**Description** - -This step does not change external state. It emits a custom event message. -The engine provides an EventSink on the execution context that the step can use -to write structured events. - -**Inputs (With.\*)** - -_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ - ---- - -## EnableIdentity - -- **Step Name**: `EnableIdentity` -- **Implementation**: `Invoke-IdleStepEnableIdentity` -- **Idempotent**: `Yes` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Enables an identity in the target system. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>] that implements EnableIdentity(identityKey) -and returns an object with properties 'IdentityKey' and 'Changed'. - -The step is idempotent by design: if the identity is already enabled, the provider -should return Changed = $false. - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider method - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ - ---- - -## EnsureAttribute - -- **Step Name**: `EnsureAttribute` -- **Implementation**: `Invoke-IdleStepEnsureAttribute` -- **Idempotent**: `Yes` -- **Contracts**: `Provider must implement method: EnsureAttribute` -- **Events**: Unknown - -**Synopsis** - -Ensures that an identity attribute matches the desired value. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>]. The provider must implement an EnsureAttribute -method with the signature (IdentityKey, Name, Value) and return an object that -contains a boolean property 'Changed'. - -The step is idempotent by design: it converges state to the desired value. - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider method - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -| Key | Required | -| --- | --- | -| IdentityKey | Yes | -| Name | Yes | -| Value | Yes | - ---- - -## EnsureEntitlement - -- **Step Name**: `EnsureEntitlement` -- **Implementation**: `Invoke-IdleStepEnsureEntitlement` -- **Idempotent**: `Yes` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Ensures that an entitlement assignment is present or absent for an identity. - -**Description** - -This provider-agnostic step uses entitlement provider contracts to converge -an assignment to the desired state. The host must supply a provider instance -via `Context.Providers[<ProviderAlias>]` that implements: - -- ListEntitlements(identityKey) -- GrantEntitlement(identityKey, entitlement) -- RevokeEntitlement(identityKey, entitlement) - -The step is idempotent and only calls Grant/Revoke when the assignment needs -to change. - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider methods - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -| Key | Required | -| --- | --- | -| IdentityKey | Yes | -| Entitlement | Yes | -| State | Yes | - ---- - -## MoveIdentity - -- **Step Name**: `MoveIdentity` -- **Implementation**: `Invoke-IdleStepMoveIdentity` -- **Idempotent**: `Yes` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Moves an identity to a different container/OU in the target system. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>] that implements MoveIdentity(identityKey, targetContainer) -and returns an object with properties 'IdentityKey' and 'Changed'. - -The step is idempotent by design: if the identity is already in the target container, -the provider should return Changed = $false. - -Authentication: -- If With.AuthSessionName is present, the step acquires an auth session via - Context.AcquireAuthSession(Name, Options) and passes it to the provider method - if the provider supports an AuthSession parameter. -- With.AuthSessionOptions (optional, hashtable) is passed to the broker for - session selection (e.g., @\{ Role = 'Tier0' \}). -- ScriptBlocks in AuthSessionOptions are rejected (security boundary). - -**Inputs (With.\*)** - -| Key | Required | -| --- | --- | -| IdentityKey | Yes | -| TargetContainer | Yes | - ---- - -## TriggerDirectorySync - -- **Step Name**: `TriggerDirectorySync` -- **Implementation**: `Invoke-IdleStepTriggerDirectorySync` -- **Idempotent**: `Unknown` -- **Contracts**: `Unknown` -- **Events**: Unknown - -**Synopsis** - -Triggers a directory sync cycle and optionally waits for completion. - -**Description** - -This is a provider-agnostic step. The host must supply a provider instance via -Context.Providers[<ProviderAlias>] that implements: -- StartSyncCycle(PolicyType, AuthSession) -- GetSyncCycleState(AuthSession) - -The step is designed for remote execution and requires an elevated auth session -provided by the host's AuthSessionBroker. - -Authentication: -- With.AuthSessionName (required): routing key for AuthSessionBroker -- With.AuthSessionOptions (optional, hashtable): forwarded to broker for session selection -- ScriptBlocks in AuthSessionOptions are rejected (security boundary) - -**Inputs (With.\*)** - -_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ - ---- +| Step Type | Module | Synopsis | +| --- | --- | --- | +| [CreateIdentity](steps/step-create-identity.md) | ``IdLE.Steps.Common`` | Creates a new identity in the target system. | +| [DeleteIdentity](steps/step-delete-identity.md) | ``IdLE.Steps.Common`` | Deletes an identity from the target system. | +| [DisableIdentity](steps/step-disable-identity.md) | ``IdLE.Steps.Common`` | Disables an identity in the target system. | +| [EmitEvent](steps/step-emit-event.md) | ``IdLE.Steps.Common`` | Emits a custom event (demo step). | +| [EnableIdentity](steps/step-enable-identity.md) | ``IdLE.Steps.Common`` | Enables an identity in the target system. | +| [EnsureAttribute](steps/step-ensure-attribute.md) | ``IdLE.Steps.Common`` | Ensures that an identity attribute matches the desired value. | +| [EnsureEntitlement](steps/step-ensure-entitlement.md) | ``IdLE.Steps.Common`` | Ensures that an entitlement assignment is present or absent for an identity. | +| [MoveIdentity](steps/step-move-identity.md) | ``IdLE.Steps.Common`` | Moves an identity to a different container/OU in the target system. | +| [TriggerDirectorySync](steps/step-trigger-directory-sync.md) | ``IdLE.Steps.DirectorySync`` | Triggers a directory sync cycle and optionally waits for completion. | diff --git a/docs/reference/steps/step-create-identity.md b/docs/reference/steps/step-create-identity.md new file mode 100644 index 00000000..83cc4ab9 --- /dev/null +++ b/docs/reference/steps/step-create-identity.md @@ -0,0 +1,44 @@ +# CreateIdentity + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `CreateIdentity` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepCreateIdentity` +- **Idempotent**: `Yes` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Creates a new identity in the target system. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>] that implements CreateIdentity(identityKey, attributes) +and returns an object with properties 'IdentityKey' and 'Changed'. + +The step is idempotent by design: if the identity already exists, the provider +should return Changed = $false without creating a duplicate. + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider method + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +| Key | Required | +| --- | --- | +| IdentityKey | Yes | +| Attributes | Yes | diff --git a/docs/reference/steps/step-delete-identity.md b/docs/reference/steps/step-delete-identity.md new file mode 100644 index 00000000..f090ffbc --- /dev/null +++ b/docs/reference/steps/step-delete-identity.md @@ -0,0 +1,45 @@ +# DeleteIdentity + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `DeleteIdentity` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepDeleteIdentity` +- **Idempotent**: `Yes` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Deletes an identity from the target system. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>] that implements DeleteIdentity(identityKey) +and returns an object with properties 'IdentityKey' and 'Changed'. + +The step is idempotent by design: if the identity is already deleted, the provider +should return Changed = $false. + +IMPORTANT: This step requires the provider to advertise the IdLE.Identity.Delete +capability, which is typically opt-in for safety. The provider must be configured +to allow deletion (e.g., AllowDelete = $true for AD provider). + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider method + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ diff --git a/docs/reference/steps/step-disable-identity.md b/docs/reference/steps/step-disable-identity.md new file mode 100644 index 00000000..cf5ee16b --- /dev/null +++ b/docs/reference/steps/step-disable-identity.md @@ -0,0 +1,41 @@ +# DisableIdentity + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `DisableIdentity` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepDisableIdentity` +- **Idempotent**: `Yes` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Disables an identity in the target system. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>] that implements DisableIdentity(identityKey) +and returns an object with properties 'IdentityKey' and 'Changed'. + +The step is idempotent by design: if the identity is already disabled, the provider +should return Changed = $false. + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider method + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ diff --git a/docs/reference/steps/step-emit-event.md b/docs/reference/steps/step-emit-event.md new file mode 100644 index 00000000..dcde656f --- /dev/null +++ b/docs/reference/steps/step-emit-event.md @@ -0,0 +1,27 @@ +# EmitEvent + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `EmitEvent` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepEmitEvent` +- **Idempotent**: `Unknown` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Emits a custom event (demo step). + +## Description + +This step does not change external state. It emits a custom event message. +The engine provides an EventSink on the execution context that the step can use +to write structured events. + +## Inputs (With.*) + +_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ diff --git a/docs/reference/steps/step-enable-identity.md b/docs/reference/steps/step-enable-identity.md new file mode 100644 index 00000000..771ddcba --- /dev/null +++ b/docs/reference/steps/step-enable-identity.md @@ -0,0 +1,41 @@ +# EnableIdentity + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `EnableIdentity` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepEnableIdentity` +- **Idempotent**: `Yes` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Enables an identity in the target system. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>] that implements EnableIdentity(identityKey) +and returns an object with properties 'IdentityKey' and 'Changed'. + +The step is idempotent by design: if the identity is already enabled, the provider +should return Changed = $false. + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider method + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ diff --git a/docs/reference/steps/step-ensure-attribute.md b/docs/reference/steps/step-ensure-attribute.md new file mode 100644 index 00000000..a569a1e1 --- /dev/null +++ b/docs/reference/steps/step-ensure-attribute.md @@ -0,0 +1,45 @@ +# EnsureAttribute + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `EnsureAttribute` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepEnsureAttribute` +- **Idempotent**: `Yes` +- **Contracts**: `Provider must implement method: EnsureAttribute` +- **Events**: Unknown + +## Synopsis + +Ensures that an identity attribute matches the desired value. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>]. The provider must implement an EnsureAttribute +method with the signature (IdentityKey, Name, Value) and return an object that +contains a boolean property 'Changed'. + +The step is idempotent by design: it converges state to the desired value. + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider method + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +| Key | Required | +| --- | --- | +| IdentityKey | Yes | +| Name | Yes | +| Value | Yes | diff --git a/docs/reference/steps/step-ensure-entitlement.md b/docs/reference/steps/step-ensure-entitlement.md new file mode 100644 index 00000000..63dbbe96 --- /dev/null +++ b/docs/reference/steps/step-ensure-entitlement.md @@ -0,0 +1,51 @@ +# EnsureEntitlement + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `EnsureEntitlement` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepEnsureEntitlement` +- **Idempotent**: `Yes` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Ensures that an entitlement assignment is present or absent for an identity. + +## Description + +This provider-agnostic step uses entitlement provider contracts to converge +an assignment to the desired state. The host must supply a provider instance +via `Context.Providers[<ProviderAlias>]` that implements: + +- ListEntitlements(identityKey) + +- GrantEntitlement(identityKey, entitlement) + +- RevokeEntitlement(identityKey, entitlement) + +The step is idempotent and only calls Grant/Revoke when the assignment needs +to change. + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider methods + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +| Key | Required | +| --- | --- | +| IdentityKey | Yes | +| Entitlement | Yes | +| State | Yes | diff --git a/docs/reference/steps/step-move-identity.md b/docs/reference/steps/step-move-identity.md new file mode 100644 index 00000000..48134535 --- /dev/null +++ b/docs/reference/steps/step-move-identity.md @@ -0,0 +1,44 @@ +# MoveIdentity + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `MoveIdentity` +- **Module**: `IdLE.Steps.Common` +- **Implementation**: `Invoke-IdleStepMoveIdentity` +- **Idempotent**: `Yes` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Moves an identity to a different container/OU in the target system. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>] that implements MoveIdentity(identityKey, targetContainer) +and returns an object with properties 'IdentityKey' and 'Changed'. + +The step is idempotent by design: if the identity is already in the target container, +the provider should return Changed = $false. + +Authentication: + +- If With.AuthSessionName is present, the step acquires an auth session via + Context.AcquireAuthSession(Name, Options) and passes it to the provider method + if the provider supports an AuthSession parameter. + +- With.AuthSessionOptions (optional, hashtable) is passed to the broker for + session selection (e.g., @\{ Role = 'Tier0' \}). + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary). + +## Inputs (With.*) + +| Key | Required | +| --- | --- | +| IdentityKey | Yes | +| TargetContainer | Yes | diff --git a/docs/reference/steps/step-trigger-directory-sync.md b/docs/reference/steps/step-trigger-directory-sync.md new file mode 100644 index 00000000..8edaa1b7 --- /dev/null +++ b/docs/reference/steps/step-trigger-directory-sync.md @@ -0,0 +1,41 @@ +# TriggerDirectorySync + +> Generated file. Do not edit by hand. +> Source: tools/Generate-IdleStepReference.ps1 + +## Summary + +- **Step Type**: `TriggerDirectorySync` +- **Module**: `IdLE.Steps.DirectorySync` +- **Implementation**: `Invoke-IdleStepTriggerDirectorySync` +- **Idempotent**: `Unknown` +- **Contracts**: `Unknown` +- **Events**: Unknown + +## Synopsis + +Triggers a directory sync cycle and optionally waits for completion. + +## Description + +This is a provider-agnostic step. The host must supply a provider instance via +Context.Providers[<ProviderAlias>] that implements: + +- StartSyncCycle(PolicyType, AuthSession) + +- GetSyncCycleState(AuthSession) + +The step is designed for remote execution and requires an elevated auth session +provided by the host's AuthSessionBroker. + +Authentication: + +- With.AuthSessionName (required): routing key for AuthSessionBroker + +- With.AuthSessionOptions (optional, hashtable): forwarded to broker for session selection + +- ScriptBlocks in AuthSessionOptions are rejected (security boundary) + +## Inputs (With.*) + +_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._ diff --git a/docs/use/intro.md b/docs/use/intro-use.md similarity index 92% rename from docs/use/intro.md rename to docs/use/intro-use.md index a3719add..4b25fb08 100644 --- a/docs/use/intro.md +++ b/docs/use/intro-use.md @@ -18,4 +18,4 @@ with one or more providers. - Executing workflow plans - Troubleshooting common issues -If you want to extend IdLE and implement providers or steps, go to [Extend](../extend/intro.md) instead. +If you want to extend IdLE and implement providers or steps, go to [Extend](../extend/intro-extend.md) instead. diff --git a/docs/use/providers.md b/docs/use/providers.md index f7b3ffa3..f368ea56 100644 --- a/docs/use/providers.md +++ b/docs/use/providers.md @@ -144,11 +144,6 @@ With = @{ :::info -Please see the detailed Provider Reference documentation for authentication help. +Please see the detailed [Provider Reference](../reference/providers.md) documentation for authentication help. ::: - -## Provider Reference - -### [Active Directory Provider](../reference/providers/provider-ad.md) -### [Entra ID Provider](../reference/providers/provider-entraID.md) diff --git a/src/IdLE.Provider.AD/README.md b/src/IdLE.Provider.AD/README.md index a18f0f7a..1b19c6d5 100644 --- a/src/IdLE.Provider.AD/README.md +++ b/src/IdLE.Provider.AD/README.md @@ -24,6 +24,7 @@ $plan = New-IdlePlan -WorkflowPath '.\joiner.psd1' -Request $request -Providers ## Documentation See **[Complete Provider Documentation](../../docs/reference/providers/provider-ad.md)** for: + - Full usage guide and examples - Capabilities and built-in steps - Identity resolution and idempotency diff --git a/src/IdLE.Provider.EntraID/README.md b/src/IdLE.Provider.EntraID/README.md index 58f5173c..96eb61c2 100644 --- a/src/IdLE.Provider.EntraID/README.md +++ b/src/IdLE.Provider.EntraID/README.md @@ -35,6 +35,7 @@ $plan = New-IdlePlan -WorkflowPath '.\joiner.psd1' -Request $request -Providers ## Documentation See **[Complete Provider Documentation](../../docs/reference/providers/provider-entraID.md)** for: + - Full usage guide and examples - Capabilities and built-in steps - Authentication patterns (delegated + app-only) diff --git a/tools/Generate-IdleCmdletReference.ps1 b/tools/Generate-IdleCmdletReference.ps1 index af3b81c6..992b7cf4 100644 --- a/tools/Generate-IdleCmdletReference.ps1 +++ b/tools/Generate-IdleCmdletReference.ps1 @@ -217,7 +217,7 @@ function New-IdleCmdletIndexMarkdown { } $lines = New-Object System.Collections.Generic.List[string] - $lines.Add('# Cmdlet Reference') + $lines.Add('# Cmdlets') $lines.Add('') $lines.Add('> Generated file. Do not edit by hand.') $lines.Add('> Source: tools/Generate-IdleCmdletReference.ps1') diff --git a/tools/Generate-IdleStepReference.ps1 b/tools/Generate-IdleStepReference.ps1 index 0ba5a304..6a999277 100644 --- a/tools/Generate-IdleStepReference.ps1 +++ b/tools/Generate-IdleStepReference.ps1 @@ -5,11 +5,18 @@ param( [ValidateNotNullOrEmpty()] [string] $ModuleManifestPath, - # Markdown output path (will be created/overwritten). + # Markdown output path for the generated index page (will be created/overwritten). + # Example: ./docs/reference/steps.md [Parameter()] [ValidateNotNullOrEmpty()] [string] $OutputPath, + # Output directory for generated per-step-type pages. + # If omitted, it is derived from OutputPath: "/steps". + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] $DetailOutputDirectory, + # Restrict which step modules are scanned. [Parameter()] [ValidateNotNullOrEmpty()] @@ -17,34 +24,53 @@ param( # Optional: Step function names to exclude (exact command names). [Parameter()] - [string[]] $ExcludeCommands = @() + [string[]] $ExcludeCommands = @(), + + # If specified, remove previously generated per-step-type pages that no longer exist. + # Safety: only deletes files that contain the generator marker string. + [Parameter()] + [switch] $CleanObsoleteDetailPages ) <# .SYNOPSIS -Generates Markdown reference documentation for IdLE steps. +Generates Markdown reference documentation for IdLE step types. .DESCRIPTION -This script imports the IdLE module from the current repository clone and generates a Markdown -"Step Catalog" based on functions following the naming convention: Invoke-IdleStep. +This script imports the IdLE modules from the current repository clone and generates: + +- An index page at -OutputPath (default: ./docs/reference/steps.md) +- One page per step type in -DetailOutputDirectory (default: ./docs/reference/steps/) + +Step types are discovered from functions following the naming convention: +Invoke-IdleStep. The generator uses comment-based help (Get-Help) as the primary source and adds a small amount of heuristic extraction from the step source file (e.g., required With.* keys when they are defined in a simple static pattern). Important: -- Do not edit the generated file by hand. Update step help/source and regenerate. +- Do not edit generated files by hand. Update step help/source and regenerate. +- Per-step-type filenames are slugified from the StepType (kebab-case) and do not include an "idle" prefix. + Example: "EnsureAttribute" -> "step-ensure-attribute.md" MDX compatibility: - Step help text may contain angle tokens like which MDX can interpret as JSX. - Step help text may contain braces like @{ ... } or {Name} which MDX can interpret as expressions. - This generator sanitizes help-derived text to be MDX-safe. +Markdown linting: +- Many linters require a blank line before lists. The generator ensures there is an empty line before + markdown list items like "- ", "* ", "+ ", or "1. ". + .PARAMETER ModuleManifestPath Path to the IdLE module manifest (IdLE.psd1). Defaults to ./src/IdLE/IdLE.psd1 relative to this script. .PARAMETER OutputPath -Path to the generated Markdown file. Defaults to ./docs/reference/steps.md relative to this script. +Path to the generated Markdown index page. Defaults to ./docs/reference/steps.md relative to this script. + +.PARAMETER DetailOutputDirectory +Directory for generated per-step-type pages. Defaults to "/steps". .PARAMETER StepModules Modules that contain step functions (IdLE.Steps.*). @@ -52,6 +78,9 @@ Modules that contain step functions (IdLE.Steps.*). .PARAMETER ExcludeCommands Specific step function names to exclude (exact command names). +.PARAMETER CleanObsoleteDetailPages +If specified, delete previously generated detail pages that are no longer produced. + .EXAMPLE pwsh ./tools/Generate-IdleStepReference.ps1 @@ -62,6 +91,8 @@ System.String Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' +$script:GeneratorMarker = 'Source: tools/Generate-IdleStepReference.ps1' + function Resolve-IdleRepoPath { [CmdletBinding()] param( @@ -85,6 +116,27 @@ function ConvertTo-IdleMarkdownSafeText { return $normalized.Trim() } +function Ensure-IdleBlankLineBeforeMarkdownLists { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [AllowEmptyString()] + [string] $Text + ) + + # Many markdown linters require a blank line before lists. + # Also applies to: "* ", "+ ", and numbered lists "1. ". + $t = $Text -replace "`r`n", "`n" -replace "`r", "`n" + + $t = [regex]::Replace( + $t, + '(?m)(?<=\S)\n(?=(?:- |\* |\+ |\d+\. ))', + "`n`n" + ) + + return $t +} + function ConvertTo-IdleMdxSafeText { [CmdletBinding()] param( @@ -96,22 +148,19 @@ function ConvertTo-IdleMdxSafeText { # NOTE: # We intentionally sanitize ONLY help-derived text (synopsis/description), # not the full generated Markdown structure. - # - # 1) Replace simple angle tokens () that MDX might interpret as JSX. - # 2) Escape braces so MDX does not treat them as expressions. - # - # Backslashes will not show up in rendered Markdown; they merely escape characters. $t = $Text -replace "`r`n", "`n" -replace "`r", "`n" - # Replace with HTML entities. + # Replace with HTML entities (MDX/JSX safety). $t = $t -replace '<(?[A-Za-z][A-Za-z0-9_-]*)>', '<${tok}>' # Escape braces to avoid MDX expression parsing, e.g. @{ ... } or {Name}. - # We escape single braces as well as any brace characters in the text. $t = $t -replace '\{', '\{' $t = $t -replace '\}', '\}' + # Lint-friendly markdown lists. + $t = Ensure-IdleBlankLineBeforeMarkdownLists -Text $t + return $t.Trim() } @@ -168,7 +217,12 @@ function Get-IdleRequiredWithKeysFromSource { $content = Get-Content -Path $filePath -Raw -ErrorAction Stop - $m = [regex]::Match($content, 'foreach\s*\(\s*\$key\s+in\s+@\((?[^)]*)\)\s*\)', 'IgnoreCase') + $m = [regex]::Match( + $content, + 'foreach\s*\(\s*\$key\s+in\s+@\((?[^)]*)\)\s*\)', + 'IgnoreCase' + ) + if (-not $m.Success) { return @() } @@ -189,7 +243,12 @@ function Get-IdleProviderMethodHintFromDescription { # Best-effort extraction: # "must implement an EnsureAttribute method" -> EnsureAttribute - $m = [regex]::Match($DescriptionText, 'must\s+implement\s+an?\s+(?[A-Za-z0-9_]+)\s+method', 'IgnoreCase') + $m = [regex]::Match( + $DescriptionText, + 'must\s+implement\s+an?\s+(?[A-Za-z0-9_]+)\s+method', + 'IgnoreCase' + ) + if (-not $m.Success) { return $null } @@ -197,7 +256,83 @@ function Get-IdleProviderMethodHintFromDescription { return $m.Groups['Method'].Value } -function ConvertTo-IdleStepMarkdownSection { +function ConvertTo-IdleKebabCase { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Text + ) + + # Robust kebab-case conversion (prevents per-letter splitting). + # + # 1) Split acronym-to-word boundaries: "EntraID" -> "Entra-ID" + # (?<=[A-Z])(?=[A-Z][a-z]) + # 2) Split lower-to-upper boundaries: "CreateIdentity" -> "Create-Identity" + # (?<=[a-z0-9])(?=[A-Z]) + $t = $Text + + $t = [regex]::Replace($t, '(?<=[A-Z])(?=[A-Z][a-z])', '-') + $t = [regex]::Replace($t, '(?<=[a-z0-9])(?=[A-Z])', '-') + + # Normalize common separators to hyphen + $t = $t -replace '[\._\s]+', '-' + + # Collapse duplicates and lowercase + $t = $t -replace '-{2,}', '-' + $t = $t.Trim('-').ToLowerInvariant() + + return $t +} + +function ConvertTo-IdleStepSlug { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $StepType + ) + + $slug = ConvertTo-IdleKebabCase -Text $StepType + + # Remove optional IdLE-related prefixes (user-facing file names should not include "idle"). + $slug = $slug -replace '^idle-step-', '' + $slug = $slug -replace '^idle-', '' + + # Ensure the file name remains self-explanatory. + if (-not $slug.StartsWith('step-')) { + $slug = "step-$slug" + } + + if ([string]::IsNullOrWhiteSpace($slug)) { + throw "Failed to derive a slug for step type: '$StepType'." + } + + return $slug +} + +function Get-IdleCommandModuleName { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNull()] + [System.Management.Automation.CommandInfo] $CommandInfo + ) + + # Prefer ModuleName (string), then Module.Name, else Unknown. + $moduleName = $CommandInfo.ModuleName + if (-not [string]::IsNullOrWhiteSpace($moduleName)) { + return $moduleName + } + + if ($null -ne $CommandInfo.Module -and -not [string]::IsNullOrWhiteSpace($CommandInfo.Module.Name)) { + return $CommandInfo.Module.Name + } + + return 'Unknown' +} + +function New-IdleStepDocModel { [CmdletBinding()] param( [Parameter(Mandatory)] @@ -211,6 +346,7 @@ function ConvertTo-IdleStepMarkdownSection { return $null } + $moduleName = Get-IdleCommandModuleName -CommandInfo $CommandInfo $help = Get-IdleHelpSafe -CommandName $commandName $synopsis = '' @@ -241,43 +377,136 @@ function ConvertTo-IdleStepMarkdownSection { $providerMethod = Get-IdleProviderMethodHintFromDescription -DescriptionText $description $contracts = if ($providerMethod) { "Provider must implement method: $providerMethod" } else { 'Unknown' } + $slug = ConvertTo-IdleStepSlug -StepType $stepType + + return [pscustomobject]@{ + StepType = $stepType + Slug = $slug + ModuleName = $moduleName + CommandName = $commandName + Synopsis = $synopsis + Description = $description + RequiredWithKeys = $requiredWithKeys + Idempotent = $idempotent + Contracts = $contracts + } +} + +function New-IdleStepDetailPageContent { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNull()] + [pscustomobject] $Model + ) + $sb = New-Object System.Text.StringBuilder - [void]$sb.AppendLine("## $stepType") + [void]$sb.AppendLine("# $($Model.StepType)") + [void]$sb.AppendLine() + [void]$sb.AppendLine('> Generated file. Do not edit by hand.') + [void]$sb.AppendLine("> $script:GeneratorMarker") + [void]$sb.AppendLine() + + [void]$sb.AppendLine('## Summary') [void]$sb.AppendLine() - [void]$sb.AppendLine(("- **Step Name**: ``{0}``" -f $stepType)) - [void]$sb.AppendLine(("- **Implementation**: ``{0}``" -f $commandName)) - [void]$sb.AppendLine(("- **Idempotent**: ``{0}``" -f $idempotent)) - [void]$sb.AppendLine(("- **Contracts**: ``{0}``" -f $contracts)) + [void]$sb.AppendLine(("- **Step Type**: ``{0}``" -f $Model.StepType)) + [void]$sb.AppendLine(("- **Module**: ``{0}``" -f $Model.ModuleName)) + [void]$sb.AppendLine(("- **Implementation**: ``{0}``" -f $Model.CommandName)) + [void]$sb.AppendLine(("- **Idempotent**: ``{0}``" -f $Model.Idempotent)) + [void]$sb.AppendLine(("- **Contracts**: ``{0}``" -f $Model.Contracts)) [void]$sb.AppendLine(("- **Events**: Unknown")) [void]$sb.AppendLine() - [void]$sb.AppendLine("**Synopsis**") + + [void]$sb.AppendLine('## Synopsis') [void]$sb.AppendLine() - [void]$sb.AppendLine($synopsis) + [void]$sb.AppendLine($Model.Synopsis.Trim()) [void]$sb.AppendLine() - if (-not [string]::IsNullOrWhiteSpace($description)) { - [void]$sb.AppendLine("**Description**") + if (-not [string]::IsNullOrWhiteSpace($Model.Description)) { + [void]$sb.AppendLine('## Description') [void]$sb.AppendLine() - [void]$sb.AppendLine($description) + [void]$sb.AppendLine($Model.Description.Trim()) [void]$sb.AppendLine() } - [void]$sb.AppendLine("**Inputs (With.\*)**") + [void]$sb.AppendLine('## Inputs (With.*)') [void]$sb.AppendLine() - if ($requiredWithKeys.Count -eq 0) { + if ($Model.RequiredWithKeys.Count -eq 0) { [void]$sb.AppendLine('_Unknown (not detected automatically). Document required With.* keys in the step help and/or use a supported pattern._') + [void]$sb.AppendLine() } else { [void]$sb.AppendLine('| Key | Required |') [void]$sb.AppendLine('| --- | --- |') - foreach ($k in $requiredWithKeys) { + foreach ($k in $Model.RequiredWithKeys) { [void]$sb.AppendLine("| $k | Yes |") } + [void]$sb.AppendLine() } - return $sb.ToString() + # Normalize output: ensure exactly one LF at EOF. + return ($sb.ToString().TrimEnd()) + "`n" +} + +function New-IdleStepsIndexPageContent { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNull()] + [pscustomobject[]] $Models + ) + + $sb = New-Object System.Text.StringBuilder + + [void]$sb.AppendLine('# Steps') + [void]$sb.AppendLine() + [void]$sb.AppendLine('> Generated file. Do not edit by hand.') + [void]$sb.AppendLine("> $script:GeneratorMarker") + [void]$sb.AppendLine() + [void]$sb.AppendLine('| Step Type | Module | Synopsis |') + [void]$sb.AppendLine('| --- | --- | --- |') + + foreach ($m in ($Models | Sort-Object -Property StepType)) { + # Keep synopsis a single line in the table. + $syn = ($m.Synopsis -replace '\s+', ' ').Trim() + [void]$sb.AppendLine(('| [{0}](steps/{1}.md) | ``{2}`` | {3} |' -f $m.StepType, $m.Slug, $m.ModuleName, $syn)) + } + + return ($sb.ToString().TrimEnd()) + "`n" +} + +function Remove-IdleObsoleteGeneratedPages { + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string] $Directory, + + [Parameter(Mandatory)] + [ValidateNotNull()] + [string[]] $KeepFileNames + ) + + if (-not (Test-Path -Path $Directory)) { + return + } + + $keep = @{} + foreach ($k in $KeepFileNames) { $keep[$k] = $true } + + Get-ChildItem -Path $Directory -Filter '*.md' -File -ErrorAction SilentlyContinue | ForEach-Object { + if ($keep.ContainsKey($_.Name)) { + return + } + + # Safety: only delete if it is clearly generated by this script. + $text = Get-Content -Path $_.FullName -Raw -ErrorAction SilentlyContinue + if ($null -ne $text -and $text -like "*$script:GeneratorMarker*") { + Remove-Item -Path $_.FullName -Force -ErrorAction Stop + } + } } # Resolve defaults relative to this script. @@ -298,10 +527,19 @@ if (-not (Test-Path -Path $outDir)) { New-Item -Path $outDir -ItemType Directory -Force | Out-Null } +if (-not $PSBoundParameters.ContainsKey('DetailOutputDirectory') -or [string]::IsNullOrWhiteSpace($DetailOutputDirectory)) { + $DetailOutputDirectory = Join-Path -Path $outDir -ChildPath 'steps' +} + +if (-not (Test-Path -Path $DetailOutputDirectory)) { + New-Item -Path $DetailOutputDirectory -ItemType Directory -Force | Out-Null +} + # Import IdLE from working tree. Remove-Module -Name 'IdLE*' -Force -ErrorAction SilentlyContinue +Import-Module -Name $ModuleManifestPath -Force -ErrorAction Stop -# Ensure step modules are loaded (Import-Module by name does NOT load nested step modules automatically). +# Ensure step modules are loaded (Import-Module IdLE.psd1 does NOT load nested step modules automatically). foreach ($m in $StepModules) { if (Get-Module -Name $m) { continue @@ -310,7 +548,6 @@ foreach ($m in $StepModules) { Write-Verbose "Importing step module: $m" try { - # Try by module name first (works if it is already discoverable in PSModulePath). Import-Module -Name $m -Force -ErrorAction Stop continue } @@ -347,36 +584,38 @@ if (-not $stepCommands) { throw "No step commands found. Ensure step modules are included in -StepModules (currently: $($StepModules -join ', '))." } -$header = @( - '# Step Catalog' - '' - '> Generated file. Do not edit by hand.' - "> Source: tools/Generate-IdleStepReference.ps1" - '' - 'This page documents built-in IdLE steps discovered from `Invoke-IdleStep*` functions in `IdLE.Steps.*` modules.' - '' - '---' - '' -) -join "`n" - -$body = New-Object System.Text.StringBuilder -[void]$body.AppendLine($header) - -foreach ($cmd in ($stepCommands | Sort-Object)) { - $section = ConvertTo-IdleStepMarkdownSection -CommandInfo $cmd - if (-not [string]::IsNullOrWhiteSpace($section)) { - [void]$body.AppendLine($section) - [void]$body.AppendLine('---') - [void]$body.AppendLine() - } +# Build documentation models. +$models = foreach ($cmd in $stepCommands) { + New-IdleStepDocModel -CommandInfo $cmd +} + +$models = @($models | Where-Object { $null -ne $_ } | Sort-Object -Property StepType) + +if ($models.Count -eq 0) { + throw "No step documentation models produced. Ensure step functions match 'Invoke-IdleStep'." +} + +# Write per-step-type pages. +$generatedDetailNames = New-Object System.Collections.Generic.List[string] + +foreach ($m in $models) { + $fileName = "$($m.Slug).md" + $filePath = Join-Path -Path $DetailOutputDirectory -ChildPath $fileName + + $pageContent = New-IdleStepDetailPageContent -Model $m + Set-Content -Path $filePath -Value $pageContent -Encoding utf8 -NoNewline + + $generatedDetailNames.Add($fileName) | Out-Null } -# Normalize output: -# - remove trailing whitespace/newlines introduced by StringBuilder -# - enforce exactly one LF at EOF (avoids "one newline too many" / dangling blank line issues) -$content = ($body.ToString().TrimEnd()) + "`n" +# Optionally remove obsolete generated pages. +if ($CleanObsoleteDetailPages) { + Remove-IdleObsoleteGeneratedPages -Directory $DetailOutputDirectory -KeepFileNames $generatedDetailNames.ToArray() +} -Set-Content -Path $OutputPath -Value $content -Encoding utf8 -NoNewline +# Write index page. +$indexContent = New-IdleStepsIndexPageContent -Models $models +Set-Content -Path $OutputPath -Value $indexContent -Encoding utf8 -NoNewline $generatedFile = Get-Item -Path $OutputPath -"Generated step reference: $($generatedFile.FullName) ($($generatedFile.Length) bytes)" +"Generated`n`tStep reference index: $($generatedFile.FullName) ($($generatedFile.Length) bytes)`n`tDetail pages: $($models.Count) in '$DetailOutputDirectory'" diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 3be5a9bd..f21ea565 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -34,14 +34,13 @@ const config = { projectName: repoName, // Usually your repo name. onBrokenLinks: 'warn', - onBrokenMarkdownLinks: 'warn', - + // Even if you don't use internationalization, you can use this field to set // useful metadata like html lang. For example, if your site is Chinese, you // may want to replace "en" with "zh-Hans". i18n: { defaultLocale: 'en', - locales: ['en','de'], + locales: ['en'], }, presets: [ @@ -59,6 +58,7 @@ const config = { editUrl: `https://github.com/${repoOwner}/${repoName}/edit/main/`, exclude: [ '**/develop/**', + '_*template.md', '**/index.md', 'index.md'], }, @@ -184,6 +184,12 @@ const config = { // If you enable Mermaid theme above, also enable markdown mermaid: // markdown: { mermaid: true }, + markdown: { + hooks: { + onBrokenMarkdownLinks: 'warn', + onBrokenMarkdownImages: 'warn', + } + }, }; module.exports = config; diff --git a/website/package-lock.json b/website/package-lock.json index 721dbe1b..d1d58d06 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -271,6 +271,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -2117,6 +2118,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2139,6 +2141,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2248,6 +2251,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2669,6 +2673,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3672,6 +3677,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/core": "3.9.2", "@docusaurus/logger": "3.9.2", @@ -3900,6 +3906,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.9.2.tgz", "integrity": "sha512-IGUsArG5hhekXd7RDb11v94ycpJpFdJPkLnt10fFQWOVxAtq5/D7hT6lzc2fhyQKaaCE62qVajOMKL7OiAFAIA==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/core": "3.9.2", "@docusaurus/logger": "3.9.2", @@ -3940,6 +3947,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz", "integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/mdx-loader": "3.9.2", "@docusaurus/module-type-aliases": "3.9.2", @@ -4852,6 +4860,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", "license": "MIT", + "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -5595,6 +5604,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -6226,6 +6236,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -6574,6 +6585,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6641,6 +6653,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6686,6 +6699,7 @@ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.47.0.tgz", "integrity": "sha512-AGtz2U7zOV4DlsuYV84tLp2tBbA7RPtLA44jbVH4TTpDcc1dIWmULjHSsunlhscbzDydnjuFlNhflR3nV4VJaQ==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/abtesting": "1.13.0", "@algolia/client-abtesting": "5.47.0", @@ -7162,6 +7176,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8180,6 +8195,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -8499,6 +8515,7 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -8920,6 +8937,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -10126,6 +10144,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14733,6 +14752,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -15343,6 +15363,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -16246,6 +16267,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -17062,6 +17084,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -17071,6 +17094,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -17126,6 +17150,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/react": "*" }, @@ -17154,6 +17179,7 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -18968,7 +18994,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsyringe": { "version": "4.10.0", @@ -19391,6 +19418,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -19651,6 +19679,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", diff --git a/website/sidebars.js b/website/sidebars.js index 53bfe6e2..d29c613f 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -31,7 +31,7 @@ const sidebars = { label: 'Use IdLE', collapsed: false, items: [ - 'use/intro', + 'use/intro-use', 'use/installation', 'use/quickstart', 'use/workflows', @@ -44,12 +44,12 @@ const sidebars = { label: 'Extend IdLE', collapsed: false, items: [ - 'extend/intro', + 'extend/intro-extend', 'extend/extensibility', - 'extend/providers', - 'extend/steps', - 'extend/events', // Add later if/when you create them: + // 'extend/providers', + // 'extend/steps', + // 'extend/events', // 'extend/auth-sessions', // 'extend/testing', ], @@ -59,11 +59,11 @@ const sidebars = { label: 'Reference', collapsed: true, items: [ - 'reference/intro', + 'reference/intro-reference', 'reference/cmdlets', { type: 'category', - label: 'Cmdlets', + label: 'Cmdlet Reference', collapsed: true, items: [ 'reference/cmdlets/Export-IdlePlan', @@ -75,16 +75,36 @@ const sidebars = { ], }, 'reference/steps', - 'reference/capabilities', { type: 'category', - label: 'Providers', + label: 'Step Reference', + collapsed: true, + items: [ + 'reference/steps/step-create-identity', + 'reference/steps/step-delete-identity', + 'reference/steps/step-disable-identity', + 'reference/steps/step-enable-identity', + 'reference/steps/step-emit-event', + 'reference/steps/step-ensure-attribute', + 'reference/steps/step-ensure-entitlement', + 'reference/steps/step-move-identity', + 'reference/steps/step-trigger-directory-sync', + ] + }, + 'reference/providers', + { + type: 'category', + label: 'Provider Reference', collapsed: true, items: [ 'reference/providers/provider-ad', 'reference/providers/provider-entraID', + 'reference/providers/provider-directorysync-entraconnect', + 'reference/providers/provider-exchangeonline', + 'reference/providers/provider-mock', ], }, + 'reference/capabilities', { type: 'category', label: 'Specs',