Provider Reference - Microsoft Entra ID (IdLE.Provider.EntraID)
Summary
- Module:
IdLE.Provider.EntraID - What it’s for: Entra ID user lifecycle + group entitlements (Microsoft Graph API)
- Targets: Microsoft Entra ID (formerly Azure AD) via Microsoft Graph (v1.0)
When to use
Use this provider when your workflow needs to manage Entra ID user accounts, for example:
- Joiner: create or update a user, set baseline attributes, assign baseline groups
- Mover: update org attributes and managed groups (covered as optional patterns inside the Joiner template)
- Leaver: disable account, revoke sessions, optional cleanup (groups, delete)
Non-goals:
- Obtaining tokens or storing secrets (handled by your runtime + AuthSessionBroker pattern)
- Exchange Online mailbox configuration (use the Exchange Online provider/steps)
Getting started
Requirements
- Your runtime must be able to supply a Microsoft Graph auth session (token/session object) to IdLE
- Graph permissions must allow the actions you intend to run (users + groups)
Install (PowerShell Gallery)
Install-Module IdLE.Provider.EntraID -Scope CurrentUser
Import
Import-Module IdLE.Provider.EntraID
Quickstart
Create provider (safe defaults):
$provider = New-IdleEntraIDIdentityProvider
Typical alias pattern:
$providers = @{
Identity = $provider
}
In a workflow template, reference your auth session via steps (example):
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
}
Keep tokens/secrets out of workflow files. Resolve them in the host/runtime and provide them via the broker.
Authentication
This provider expects Graph authentication to be supplied at runtime (AuthSessionBroker pattern). Common session shapes used by hosts include:
- raw access token string (Bearer token)
- object with an
AccessTokenproperty - object that can produce a token (e.g.,
GetAccessToken())
Recommended wiring in examples:
AuthSessionName = 'MicrosoftGraph'AuthSessionOptions = @{ Role = 'Admin' }for routing (optional)- Use a more privileged role only for privileged actions (e.g. delete)
Context Resolvers
This provider supports Context Resolvers for the allowlisted, read-only capabilities below.
Capability: IdLE.Identity.Read
Writes to scoped path: Request.Context.Providers.<ProviderAlias>.<AuthSessionKey>.Identity.Profile
Engine-defined View: Request.Context.Views.Identity.Profile
Type: PSCustomObject (PSTypeName = 'IdLE.Identity')
Top-level properties:
| Property | Type | Notes |
|---|---|---|
PSTypeName | string | Always IdLE.Identity. |
IdentityKey | string | The identity key used by the workflow (typically the Entra user id). |
Enabled | bool | Derived from Entra user accountEnabled. |
Attributes | hashtable | Key/value bag; keys are strings; values are typically string. |
Attributes keys populated by this provider (when present on the user object):
| Attribute key | Type | Source (Graph field) |
|---|---|---|
GivenName | string | givenName |
Surname | string | surname |
DisplayName | string | displayName |
UserPrincipalName | string | userPrincipalName |
Mail | string | mail |
Department | string | department |
JobTitle | string | jobTitle |
OfficeLocation | string | officeLocation |
CompanyName | string | companyName |
Attribute access: Profile attributes are nested under the
Attributeskey. In Conditions, useRequest.Context.Views.Identity.Profile.Attributes.DisplayName(or the scopedRequest.Context.Providers.<ProviderAlias>.<AuthSessionKey>.Identity.Profile.Attributes.DisplayName), notRequest.Context.Views.Identity.Profile.DisplayName(orRequest.Context.Providers.<ProviderAlias>.<AuthSessionKey>.Identity.Profile.DisplayName).
Capability: IdLE.Entitlement.List
Writes to scoped path: Request.Context.Providers.<ProviderAlias>.<AuthSessionKey>.Identity.Entitlements
Engine-defined View: Request.Context.Views.Identity.Entitlements
Type: object[] (array of PSCustomObject, PSTypeName = 'IdLE.Entitlement')
Each element represents one Entra ID group membership:
| Property | Type | Notes |
|---|---|---|
PSTypeName | string | Always IdLE.Entitlement. |
Kind | string | Always Group. |
Id | string | Entra group id. |
Mail | string or $null | Group mail (if returned by the adapter). |
Notes:
- The output paths are fixed by the engine and cannot be changed.
- Each entry is automatically annotated with
SourceProviderandSourceAuthSessionNamemetadata. - Use the global View (
Request.Context.Views.Identity.Entitlements) in Conditions when you don't need to filter by provider. Use the scoped path when you need results from a specific provider only. - See Context Resolvers for the full path reference.
Configuration
Provider constructor / factory
New-IdleEntraIDIdentityProvider
High-signal parameters
-AllowDelete— opt-in to enable theIdLE.Identity.Deletecapability (disabled by default for safety)
Provider-specific options reference
This provider has no provider-specific option bag. Configuration is done through constructor parameters; authentication is handled by your runtime via the broker.
Required Microsoft Graph permissions
At minimum, you typically need:
- Users: read/write (create/update/disable/delete if enabled)
- Groups: read/write memberships (if you use entitlement steps)
Exact permission names depend on your auth model (delegated vs application) and what operations you enable.
Examples (canonical templates)
These are the two canonical Entra ID templates, intended to be embedded directly in documentation. Mover scenarios are integrated as optional patterns in the Joiner template.
@{
Name = 'EntraID Joiner - Complete Onboarding'
LifecycleEvent = 'Joiner'
Description = 'Creates or updates an Entra ID user with baseline attributes and group memberships. Includes optional mover patterns.'
Steps = @(
@{
Name = 'CreateEntraIDUser'
Type = 'IdLE.Step.CreateIdentity'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
# Using UPN keeps it human-friendly in templates.
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
Attributes = @{
UserPrincipalName = '{{Request.Intent.UserPrincipalName}}'
DisplayName = '{{Request.Intent.DisplayName}}'
GivenName = '{{Request.Intent.GivenName}}'
Surname = '{{Request.Intent.Surname}}'
Mail = '{{Request.Intent.Mail}}'
# Optional org attributes (safe when empty)
Department = '{{Request.Intent.Department}}'
JobTitle = '{{Request.Intent.JobTitle}}'
OfficeLocation = '{{Request.Intent.OfficeLocation}}'
CompanyName = '{{Request.Intent.CompanyName}}'
# Password profile is typically relevant for "new user" scenarios.
# Your host can generate and provide a temporary password in Request.Intent.
PasswordProfile = @{
forceChangePasswordNextSignIn = $true
password = '{{Request.Intent.TemporaryPassword}}'
}
}
}
}
@{
Name = 'AddToBaselineGroups'
Type = 'IdLE.Step.EnsureEntitlement'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
# Using UPN keeps it human-friendly in templates.
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
# Baseline groups should be explicit and driven by request input (no hardcoding).
Desired = @(
@{
Kind = 'Group'
Id = '{{Request.Intent.AllEmployeesGroupId}}'
DisplayName = '{{Request.Intent.AllEmployeesGroupName}}'
}
@{
Kind = 'Group'
Id = '{{Request.Intent.DepartmentGroupId}}'
DisplayName = '{{Request.Intent.DepartmentGroupName}}'
}
)
}
}
@{
Name = 'EnableAccount'
Type = 'IdLE.Step.EnableIdentity'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
}
}
# ----------------------------
# Mover patterns (optional)
# Enable by setting: Request.Intent.IsMover = $true
# ----------------------------
@{
Name = 'Mover_UpdateOrgAttributes'
Type = 'IdLE.Step.EnsureAttributes'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.IsMover'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
Attributes = @{
Department = '{{Request.Intent.NewDepartment}}'
JobTitle = '{{Request.Intent.NewJobTitle}}'
OfficeLocation = '{{Request.Intent.NewOfficeLocation}}'
Manager = '{{Request.Intent.NewManagerObjectId}}'
}
}
}
@{
Name = 'Mover_AdjustManagedGroups'
Type = 'IdLE.Step.EnsureEntitlement'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.IsMover'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
# Optional: add department/project groups as part of a move.
Desired = @(
@{
Kind = 'Group'
Id = '{{Request.Intent.DepartmentGroupId}}'
DisplayName = '{{Request.Intent.DepartmentGroupName}}'
}
@{
Kind = 'Group'
Id = '{{Request.Intent.ProjectGroupId}}'
DisplayName = '{{Request.Intent.ProjectGroupName}}'
}
)
}
}
@{
Name = 'EmitCompletionEvent'
Type = 'IdLE.Step.EmitEvent'
With = @{
Message = 'EntraID user {{Request.Intent.UserPrincipalName}} created/updated successfully.'
}
}
)
}
@{
Name = 'EntraID Leaver - Offboarding (Optional Cleanup)'
LifecycleEvent = 'Leaver'
Description = 'Disables the user, revokes active sessions, and performs optional cleanup (group revoke and delete).'
Steps = @(
@{
Name = 'DisableAccount'
Type = 'IdLE.Step.DisableIdentity'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
# Prefer ObjectId for leaver (stable), but you may also use UPN if your provider supports it.
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
}
}
@{
Name = 'RevokeActiveSessions'
Type = 'IdLE.Step.RevokeIdentitySessions'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
}
}
@{
Name = 'StampOffboardingMarker'
Type = 'IdLE.Step.EnsureAttributes'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
Attributes = @{
DisplayName = '{{Request.Intent.DisplayName}} (LEAVER)'
Manager = $null
}
}
}
# Optional & potentially disruptive:
# Setting Desired = @() will remove *all* group memberships the provider manages.
@{
Name = 'RevokeAllGroupMemberships_Optional'
Type = 'IdLE.Step.EnsureEntitlement'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.RevokeAllGroupMemberships'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
Entitlement = @{
Kind = 'Group';
Id = '*'
}
State = 'Absent'
}
}
# Optional & potentially disruptive:
# PruneEntitlementsEnsureKeep removes all groups except the keep set AND ensures
# explicit Keep items are present. Use PruneEntitlements if you only need removal.
@{
Name = 'PruneGroupMemberships_Optional'
Type = 'IdLE.Step.PruneEntitlementsEnsureKeep'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.PruneGroupMemberships'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
Kind = 'Group'
# Retain this specific leaver group and ensure it is present.
Keep = @(
@{ Kind = 'Group'; Id = '{{Request.Intent.LeaverRetainGroupId}}' }
)
# Pattern-based retention is not supported by PruneEntitlementsEnsureKeep. Use a
# separate IdLE.Step.PruneEntitlements step earlier if you must protect wildcard
# matches without granting them.
}
}
# Optional delete (requires provider to be created with -AllowDelete)
@{
Name = 'DeleteAccount_Optional'
Type = 'IdLE.Step.DeleteIdentity'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.DeleteAfterDisable'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Tier0' }
IdentityKey = '{{Request.Intent.UserPrincipalName}}'
}
}
@{
Name = 'EmitCompletionEvent'
Type = 'IdLE.Step.EmitEvent'
With = @{
Message = 'EntraID user {{Request.Intent.UserPrincipalName}} offboarding completed.'
}
}
)
}
Troubleshooting
- 401/403 from Microsoft Graph: token missing/expired or insufficient Graph permissions for the requested operation.
- Auth session not found: check
AuthSessionNamematches your runtime/broker configuration. - Delete doesn’t work: deletion is opt-in. Create the provider with
-AllowDeleteand only use delete with a privileged auth role. - Group cleanup is disruptive: only enable revoke/remove operations when you fully understand the impact (prefer managed allow-lists).