Skip to content

provider: trigger and wait for Entra Connect sync cycles (remote) #49

@blindzero

Description

@blindzero

Goal

Provide a first-party step pack that can trigger an Entra ID Connect (ADSync) sync cycle on an on-premises server and optionally wait until the cycle is finished.

Constraints:

  • This cannot be triggered “from Entra in the cloud”; it must run against the on-premises Entra Connect server.
  • Start-ADSyncSyncCycle typically requires an elevated/admin execution context.
  • No interactive prompts (headless engine). Elevation and authentication are the host’s responsibility via AuthSessionBroker.

Scope

In scope

  • New step pack (separate module): IdLE.Steps.DirectorySync.EntraConnect
  • New provider implementation module: IdLE.Provider.DirectorySync.EntraConnect
  • Provider contract (lightweight PowerShell object contract)
  • Auth session contract for remote execution (Wrapper / Handle)
  • Tests (unit + provider contract tests) and docs/examples

Out of scope

  • Host-side implementation details for creating an elevated remote session (that belongs to the host).
  • Running real ADSync in CI.

Design

1) Step Type and module

  • Step.Type: IdLE.Step.TriggerDirectorySync
  • Step implementation function: Invoke-IdleStepTriggerDirectorySync
  • Module: src/IdLE.Steps.DirectorySync.EntraConnect/

2) Step metadata (RequiredCapabilities)

Because #89 removes workflow RequiresCapabilities, the step pack must provide metadata for this step:

  • RequiredCapabilities = @('IdLE.DirectorySync.Trigger', 'IdLE.DirectorySync.Status')

Rationale:

  • Keeping this static avoids conditional capability logic in the engine.
  • Even when With.Wait = $false, advertising IdLE.DirectorySync.Status is acceptable and keeps planning deterministic.

3) Step inputs (With.*)

With must be a hashtable and is validated by the step.

Required:

  • With.AuthSessionName (string)
    • Routing key for the AuthSessionBroker (recommended default in examples: EntraConnect).
  • With.PolicyType (string)
    • Allowed: Delta, Initial (case-insensitive).

Optional:

  • With.Provider (string) — provider alias in Context.Providers (default: DirectorySync).
  • With.Wait (bool) — default: $false.
  • With.TimeoutSeconds (int) — default: 600.
  • With.PollIntervalSeconds (int) — default: 10.
  • With.AuthSessionOptions (hashtable, data-only) — forwarded to broker (e.g., { Role = 'EntraConnectAdmin' }).

4) Provider contract

The step calls a provider instance via Context.Providers[<ProviderAlias>].

Provider must implement:

  • StartSyncCycle(PolicyType, AuthSession)

    • Triggers a sync cycle.
    • Returns an object with at least:
      • Started (bool)
      • Message (string, optional)
  • GetSyncCycleState(AuthSession)

    • Returns an object with at least:
      • InProgress (bool)
      • State (string; e.g., Idle, InProgress, Unknown)
      • Details (hashtable or null; optional)

Notes:

  • The contract is intentionally not tied to a specific cmdlet name (e.g. Get-ADSyncScheduler). The EntraConnect provider may use Get-ADSyncScheduler or another reliable signal; the contract only cares about InProgress.

5) AuthSession contract (Wrapper / Handle)

The host’s AuthSessionBroker must return an AuthSession whose payload is a remote execution handle for the Entra Connect server.

Minimum expected shape (trusted runtime object; not part of workflow data):

  • Method: InvokeCommand(CommandName, Parameters)

    • Executes a named command on the Entra Connect server in the required privileged context.
    • CommandName is a string.
    • Parameters is a hashtable (data-only).
  • Method: Dispose() (optional but recommended)

The provider uses only this handle (via AuthSession) and does not create its own sessions.

6) Elevation requirement (explicit)

If the remote execution context is not privileged enough to call Start-ADSyncSyncCycle, the provider must fail deterministically:

  • Throw an error that is actionable and clearly indicates:
    • missing privileges / elevation
    • that the host must provide an elevated session via AuthSessionBroker

No interactive escalation (no UAC prompts, no credential prompts).

7) Step behavior

  • Validate inputs early.
  • Acquire auth session via:
    • Context.AcquireAuthSession(With.AuthSessionName, With.AuthSessionOptions)
  • Call provider StartSyncCycle(With.PolicyType, AuthSession).
  • Emit events (see below).
  • If With.Wait is $true:
    • poll GetSyncCycleState(AuthSession) until InProgress = $false or timeout
    • on timeout: fail with a clear error (TimeoutSeconds, last known state)

8) Events

Use the standard event sink:

  • DirectorySyncTriggered (after trigger call returns)
  • DirectorySyncWaiting (first poll)
  • DirectorySyncPoll (optional, only if not too chatty; include attempt + state)
  • DirectorySyncCompleted (when finished)
  • DirectorySyncFailed (on any failure)

Event Data must be data-only and must not include secrets.

Implementation tasks

Step pack: IdLE.Steps.DirectorySync.EntraConnect

Provider: IdLE.Provider.DirectorySync.EntraConnect

  • Implement GetCapabilities() returning:

    • IdLE.DirectorySync.Trigger
    • IdLE.DirectorySync.Status
  • Implement provider methods using the AuthSession remote execution handle:

    • Ensure the ADSync module is available in the remote context (import as needed inside the remote invocation).
    • Trigger: Start-ADSyncSyncCycle -PolicyType <Delta|Initial>
    • Status: determine InProgress via a reliable signal (implementation detail).

Tests

  • Unit tests for the step:

    • validates required keys
    • triggers without wait
    • triggers and waits (poll loop) until complete
    • timeout path
    • provider missing method surfaces actionable error
  • Provider contract tests:

    • GetCapabilities() returns the required capability ids
    • StartSyncCycle and GetSyncCycleState accept AuthSession parameter and behave with a mocked remote handle

No live ADSync / no real remoting in default CI.

Docs / examples

  • Add a minimal example workflow step using IdLE.Step.TriggerDirectorySync.

  • Example should demonstrate:

    • AuthSessionName = 'EntraConnect'
    • AuthSessionOptions = @{ Role = 'EntraConnectAdmin' } (if needed)
    • PolicyType = 'Delta'
    • Wait = $true (optional)
  • Update relevant docs pages if provider/step contracts are introduced or changed.

  • Regenerate step reference if new step is added.

Acceptance criteria

  • New step pack exists and is loadable via the IdLE meta module.
  • Step triggers a sync cycle via provider and returns IdLE.StepResult with deterministic status.
  • Optional wait works via polling, with configurable timeout and poll interval.
  • Step does not perform authentication; it uses AuthSessionBroker via Context.AcquireAuthSession.
  • Provider only uses the provided AuthSession remote handle; no self-auth.
  • Missing privilege/elevation produces a clear, actionable error.
  • Pester and PSScriptAnalyzer are green.
  • Examples and docs updated; generated references regenerated where required.

Definition of Done

  • Pester green (./tools/Invoke-IdlePesterTests.ps1)
  • PSScriptAnalyzer green (./tools/Invoke-IdleScriptAnalyzer.ps1)
  • Docs/examples updated as part of the same PR
  • Generated references regenerated if impacted (./tools/Generate-IdleStepReference.ps1)

Dependencies / References

Metadata

Metadata

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions