Skip to main content

Provider Reference - Microsoft Entra ID (IdLE.Provider.EntraID)

Summary

  • Module: IdLE.Provider.EntraID
  • What it’s for: Entra ID user lifecycle + group and Administrative Unit 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 and Administrative Units
  • Mover: update org attributes and managed groups (covered as optional patterns inside the Joiner template)
  • Leaver: disable account, revoke sessions, optional cleanup (groups, Administrative Units, 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, Administrative Units)
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 AccessToken property
  • 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:

PropertyTypeNotes
PSTypeNamestringAlways IdLE.Identity.
IdentityKeystringThe identity key used by the workflow (typically the Entra user id).
EnabledboolDerived from Entra user accountEnabled.
AttributeshashtableKey/value bag; keys are strings; values are typically string.

Attributes keys populated by this provider (when present on the user object):

Attribute keyTypeSource (Graph field)
GivenNamestringgivenName
Surnamestringsurname
DisplayNamestringdisplayName
UserPrincipalNamestringuserPrincipalName
Mailstringmail
Departmentstringdepartment
JobTitlestringjobTitle
OfficeLocationstringofficeLocation
CompanyNamestringcompanyName

Attribute access: Profile attributes are nested under the Attributes key. In Conditions, use Request.Context.Views.Identity.Profile.Attributes.DisplayName (or the scoped Request.Context.Providers.<ProviderAlias>.<AuthSessionKey>.Identity.Profile.Attributes.DisplayName), not Request.Context.Views.Identity.Profile.DisplayName (or Request.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 entitlement (group membership or Administrative Unit membership):

Group entitlements (Kind = 'Group'):

PropertyTypeNotes
KindstringAlways Group.
IdstringEntra group object ID (GUID).
Mailstring or $nullGroup mail (if returned by Graph).

Administrative Unit entitlements (Kind = 'AdministrativeUnit'):

PropertyTypeNotes
KindstringAlways AdministrativeUnit.
IdstringEntra Administrative Unit object ID (GUID).

Notes:

  • The output paths are fixed by the engine and cannot be changed.
  • Each entry is automatically annotated with SourceProvider and SourceAuthSessionName metadata.
  • 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 the IdLE.Identity.Delete capability (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 group entitlement steps)
  • Administrative Units: read/write memberships — only required when using Kind = 'AdministrativeUnit' in entitlement steps. Group-only workflows do not need these permissions because ListEntitlements skips AU Graph calls when Kind = 'Group' is specified.

Exact permission names depend on your auth model (delegated vs application) and what operations you enable.

CapabilityPermission (Application)
List/Read usersUser.Read.All
Create/update/disable usersUser.ReadWrite.All
List group membershipsGroup.Read.All
Grant/revoke group membershipsGroupMember.ReadWrite.All
List AU memberships (Kind = 'AdministrativeUnit')AdministrativeUnit.Read.All
Grant/revoke AU memberships (Kind = 'AdministrativeUnit')AdministrativeUnit.ReadWrite.All

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.

examples/workflows/templates/entraid-joiner.psd1
@{
Name = 'EntraID Joiner - Complete Onboarding'
LifecycleEvent = 'Joiner'
Description = 'Creates or updates an Entra ID user with baseline attributes, group memberships, and Administrative Unit assignments. Includes optional mover patterns.'

Steps = @(
@{
Name = 'CreateEntraIDUser'
Type = 'IdLE.Step.CreateIdentity'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }

IdentityKey = '{{Request.IdentityKeys.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}}'
}
}
}
}

# Baseline groups: add one EnsureEntitlement step per group.
@{
Name = 'AddToAllEmployeesGroup'
Type = 'IdLE.Step.EnsureEntitlement'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Entitlement = @{
Kind = 'Group'
Id = '{{Request.Intent.AllEmployeesGroupId}}'
}
State = 'Present'
}
}

@{
Name = 'AddToDepartmentGroup'
Type = 'IdLE.Step.EnsureEntitlement'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Entitlement = @{
Kind = 'Group'
Id = '{{Request.Intent.DepartmentGroupId}}'
}
State = 'Present'
}
}

# Baseline Administrative Unit: controls which scoped admins can manage this user.
# AUs can be referenced by their GUID objectId or by displayName (tenant-unique names only).
@{
Name = 'AddToDepartmentAdministrativeUnit'
Type = 'IdLE.Step.EnsureEntitlement'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Entitlement = @{
Kind = 'AdministrativeUnit'
Id = '{{Request.Intent.DepartmentAdministrativeUnitId}}'
}
State = 'Present'
}
}

@{
Name = 'EnableAccount'
Type = 'IdLE.Step.EnableIdentity'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.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.IdentityKeys.UserPrincipalName}}'

Attributes = @{
Department = '{{Request.Intent.NewDepartment}}'
JobTitle = '{{Request.Intent.NewJobTitle}}'
OfficeLocation = '{{Request.Intent.NewOfficeLocation}}'
Manager = '{{Request.Intent.NewManagerObjectId}}'
}
}
}

# Add one EnsureEntitlement step per group change required on a mover.
@{
Name = 'Mover_AddToDepartmentGroup'
Type = 'IdLE.Step.EnsureEntitlement'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.IsMover'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Entitlement = @{
Kind = 'Group'
Id = '{{Request.Intent.DepartmentGroupId}}'
}
State = 'Present'
}
}

@{
Name = 'Mover_AddToProjectGroup'
Type = 'IdLE.Step.EnsureEntitlement'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.IsMover'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Entitlement = @{
Kind = 'Group'
Id = '{{Request.Intent.ProjectGroupId}}'
}
State = 'Present'
}
}

# Reassign to the new department's Administrative Unit on a move.
@{
Name = 'Mover_AddToNewDepartmentAdministrativeUnit'
Type = 'IdLE.Step.EnsureEntitlement'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.IsMover'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Entitlement = @{
Kind = 'AdministrativeUnit'
Id = '{{Request.Intent.NewDepartmentAdministrativeUnitId}}'
}
State = 'Present'
}
}

@{
Name = 'EmitCompletionEvent'
Type = 'IdLE.Step.EmitEvent'
With = @{
Message = 'EntraID user {{Request.IdentityKeys.UserPrincipalName}} created/updated successfully.'
}
}
)
}
examples/workflows/templates/entraid-leaver.psd1
@{
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' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
}
}

@{
Name = 'RevokeActiveSessions'
Type = 'IdLE.Step.RevokeIdentitySessions'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
}
}

@{
Name = 'StampOffboardingMarker'
Type = 'IdLE.Step.EnsureAttributes'
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Attributes = @{
DisplayName = '{{Request.Intent.DisplayName}} (LEAVER)'
Manager = $null
}
}
}

# Optional: remove ALL group memberships — use when no specific groups need to be retained.
# PruneEntitlements with an empty Keep list removes every group the provider sees.
@{
Name = 'RevokeAllGroupMemberships_Optional'
Type = 'IdLE.Step.PruneEntitlements'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.RevokeAllGroupMemberships'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Kind = 'Group'
Keep = @()
}
}

# Optional: remove all groups EXCEPT a retain set AND ensure retain set is present.
# PruneEntitlementsEnsureKeep removes all groups except the keep set AND ensures
# explicit Keep items are present. Use PruneEntitlements (above) 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.IdentityKeys.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: remove the user from all Administrative Units.
# Use when scoped admin visibility must be revoked as part of offboarding.
# This is remove-only: no AU is added. Existing memberships NOT in Keep are removed;
# Keep entries are protected but NOT granted if missing.
# Use PruneAdministrativeUnitMemberships_Optional (below) when you also need to guarantee
# a specific AU is present after the prune.
@{
Name = 'RevokeAdministrativeUnitMemberships_Optional'
Type = 'IdLE.Step.PruneEntitlements'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.RevokeAdministrativeUnitMemberships'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Kind = 'AdministrativeUnit'
Keep = @()
}
}

# Optional: remove all AU memberships EXCEPT a retain set AND ensure retain set is present.
# PruneEntitlementsEnsureKeep removes all AU memberships except the keep set AND grants
# any Keep AU that is not currently assigned.
# Use PruneEntitlements (above) if you only need removal with no guaranteed grant.
@{
Name = 'PruneAdministrativeUnitMemberships_Optional'
Type = 'IdLE.Step.PruneEntitlementsEnsureKeep'
Condition = @{
All = @(
@{
Equals = @{
Path = 'Request.Intent.PruneAdministrativeUnitMemberships'
Value = $true
}
}
)
}
With = @{
AuthSessionName = 'MicrosoftGraph'
AuthSessionOptions = @{ Role = 'Admin' }
IdentityKey = '{{Request.IdentityKeys.UserPrincipalName}}'
Kind = 'AdministrativeUnit'

# This AU is retained AND guaranteed to be present after the step.
# Reference by objectId (GUID) or by displayName (must be tenant-unique).
Keep = @(
@{ Kind = 'AdministrativeUnit'; Id = '{{Request.Intent.RetainAdministrativeUnitId}}' }
)
}
}

# 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.IdentityKeys.UserPrincipalName}}'
}
}

@{
Name = 'EmitCompletionEvent'
Type = 'IdLE.Step.EmitEvent'
With = @{
Message = 'EntraID user {{Request.IdentityKeys.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 AuthSessionName matches your runtime/broker configuration.
  • Delete doesn’t work: deletion is opt-in. Create the provider with -AllowDelete and 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).
  • Administrative Unit not found: the AU must exist in Entra before the workflow runs. When referencing by objectId, confirm the GUID is correct. When referencing by displayName, confirm the name matches exactly and AdministrativeUnit.Read.All permission is granted.
  • Multiple AUs match displayName: AU display names are not unique in Entra. If multiple AUs share the same name, use the objectId (GUID) instead to ensure deterministic lookup.