Problem Statement
In leaver (and some mover) workflows we must remove all entitlements (e.g., group memberships) from an identity except:
- the provider/system default entitlement(s) that must always remain (e.g., AD Domain Users / primary group handling)
- one or more custom “keep” entitlements (allowlist)
- optionally additional “keep” entitlements identified by a pattern (agent-safe, wildcard-based)
With the current step model, IdLE.Step.EnsureEntitlement is intentionally atomic (one entitlement → desired state). Implementing “remove all except …” would require external host logic to:
- list current entitlements,
- compute a delta set,
- emit many
EnsureEntitlement -State Absent steps.
This breaks portability and pushes essential lifecycle logic out of IdLE workflows.
Proposed Solution
Introduce a new provider-agnostic step type:
- StepType:
IdLE.Step.PruneEntitlements
- Purpose: Converge the identity’s entitlements by removing all non-kept entitlements (“prune”), optionally ensuring the explicitly kept entitlements are present.
Capability gating (explicit provider opt-in)
Add a dedicated capability so providers can explicitly opt in:
- Capability:
IdLE.Entitlement.Prune
Reason: while prune can be composed from List + Revoke (+ Grant), we explicitly want providers to decide whether they support a bulk destructive operation.
Parameters (agent-safe)
-Provider (string, required)
-Identity (Idle identity reference, required)
-Kind (string, required)
- entitlement kind, e.g.
Group, Role, License (provider-defined)
-Keep (array, optional)
- explicit entitlement references to keep (same shape as
EnsureEntitlement uses for a single entitlement)
-KeepPattern (array of string, optional)
- wildcard patterns only (PowerShell
-like semantics), used to keep entitlements matching the pattern
- patterns must be treated as agent-safe (no regex, no scriptblocks)
-EnsureKeepEntitlements (switch, optional)
- if set, ensure entitlements from
-Keep are present (grant missing)
Guardrails / Validation
- At least one of
-Keep or -KeepPattern must be provided.
-KeepPattern supports wildcard matching only (e.g. LEAVER-*, CN=LEAVER-*,OU=Groups,DC=...).
-EnsureKeepEntitlements applies only to explicit -Keep items (patterns cannot be “ensured”).
- Provider must handle “non-removable” entitlements safely (e.g., AD primary group / Domain Users). The step must not fail the whole workflow for these; instead, it should:
- skip with a structured warning event and continue
- include the skipped reason in plan/export and result
Provider contract usage
The step is provider-agnostic and uses existing entitlement primitives, plus the new capability flag:
- required provider ops:
ListEntitlements(identity, kind)
RevokeEntitlement(identity, entitlement, kind)
- conditional provider op (only if
-EnsureKeepEntitlements):
GrantEntitlement(identity, entitlement, kind)
Planned behavior (idempotent)
- List current entitlements (for
Kind).
- Build keep-set from:
- explicit
Keep
- any current entitlements matching any
KeepPattern
- provider/system “always keep” (e.g., AD primary group semantics)
- Compute remove-set = current − keep-set.
- Revoke each entitlement in remove-set.
- If
EnsureKeepEntitlements is set:
- compute missing-set = Keep − current
- grant each entitlement in missing-set
- Emit structured events for:
- plan intent (kept, pruned, ensured)
- each revoke/grant action
- skipped items (non-removable, permission denied, etc.)
Alternatives Considered
A) Extend IdLE.Step.EnsureEntitlement with -RemoveAllExcept / -KeepPattern
Rejected. It blurs semantics of an atomic step, increases risk of accidental destructive behavior, complicates validation/modes, and makes docs harder (“EnsureEntitlement sometimes means prune”).
B) Host-only delta computation (build many EnsureEntitlement steps externally)
Rejected. Breaks portability: the core lifecycle semantics live outside IdLE workflows and differs per host.
C) Provider-specific step (e.g., AD-only prune group memberships)
Rejected (for now). We want a generic capability and step. Providers can opt in/out via capability.
Impact
- Backwards compatibility: No breaking changes. New step + new capability only.
- Existing workflows: Unaffected unless users adopt the new step.
- Provider implementations: Providers can ignore this until they opt-in by exposing
IdLE.Entitlement.Prune and implementing the required primitives consistently.
Additional Context
Primary example (AD Leaver)
Remove all group memberships except:
Domain Users / primary group related membership behavior (provider/system keep)
- one custom leaver group (explicit keep)
- optionally groups matching
LEAVER-*
Example intent (pseudo):
Kind: Group
Keep: [ "CN=LEAVER-RETAIN,OU=Groups,DC=contoso,DC=com" ]
KeepPattern: [ "CN=LEAVER-*,OU=Groups,DC=contoso,DC=com" ]
EnsureKeepEntitlements: $true
Acceptance Criteria
Security / Safety Notes
- Pattern matching must use wildcard
-like semantics only; no regex and no scriptblocks.
- Step must be explicit about destructive behavior (“removes all except …”).
- Emit clear audit events: what was removed, what was kept, what was skipped and why.
Problem Statement
In leaver (and some mover) workflows we must remove all entitlements (e.g., group memberships) from an identity except:
With the current step model,
IdLE.Step.EnsureEntitlementis intentionally atomic (one entitlement → desired state). Implementing “remove all except …” would require external host logic to:EnsureEntitlement -State Absentsteps.This breaks portability and pushes essential lifecycle logic out of IdLE workflows.
Proposed Solution
Introduce a new provider-agnostic step type:
IdLE.Step.PruneEntitlementsCapability gating (explicit provider opt-in)
Add a dedicated capability so providers can explicitly opt in:
IdLE.Entitlement.PruneReason: while prune can be composed from
List + Revoke (+ Grant), we explicitly want providers to decide whether they support a bulk destructive operation.Parameters (agent-safe)
-Provider(string, required)-Identity(Idle identity reference, required)-Kind(string, required)Group,Role,License(provider-defined)-Keep(array, optional)EnsureEntitlementuses for a single entitlement)-KeepPattern(array of string, optional)-likesemantics), used to keep entitlements matching the pattern-EnsureKeepEntitlements(switch, optional)-Keepare present (grant missing)Guardrails / Validation
-Keepor-KeepPatternmust be provided.-KeepPatternsupports wildcard matching only (e.g.LEAVER-*,CN=LEAVER-*,OU=Groups,DC=...).-EnsureKeepEntitlementsapplies only to explicit-Keepitems (patterns cannot be “ensured”).Provider contract usage
The step is provider-agnostic and uses existing entitlement primitives, plus the new capability flag:
ListEntitlements(identity, kind)RevokeEntitlement(identity, entitlement, kind)-EnsureKeepEntitlements):GrantEntitlement(identity, entitlement, kind)Planned behavior (idempotent)
Kind).KeepKeepPatternEnsureKeepEntitlementsis set:Alternatives Considered
A) Extend
IdLE.Step.EnsureEntitlementwith-RemoveAllExcept/-KeepPatternRejected. It blurs semantics of an atomic step, increases risk of accidental destructive behavior, complicates validation/modes, and makes docs harder (“EnsureEntitlement sometimes means prune”).
B) Host-only delta computation (build many EnsureEntitlement steps externally)
Rejected. Breaks portability: the core lifecycle semantics live outside IdLE workflows and differs per host.
C) Provider-specific step (e.g., AD-only prune group memberships)
Rejected (for now). We want a generic capability and step. Providers can opt in/out via capability.
Impact
IdLE.Entitlement.Pruneand implementing the required primitives consistently.Additional Context
Primary example (AD Leaver)
Remove all group memberships except:
Domain Users/ primary group related membership behavior (provider/system keep)LEAVER-*Example intent (pseudo):
Kind: GroupKeep: [ "CN=LEAVER-RETAIN,OU=Groups,DC=contoso,DC=com" ]KeepPattern: [ "CN=LEAVER-*,OU=Groups,DC=contoso,DC=com" ]EnsureKeepEntitlements: $trueAcceptance Criteria
IdLE.Step.PruneEntitlementsexists and is discoverable via metadata/catalog.IdLE.Entitlement.Pruneexists and is used for gating.Security / Safety Notes
-likesemantics only; no regex and no scriptblocks.