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 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-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 Entra ID group membership:

PropertyTypeNotes
PSTypeNamestringAlways IdLE.Entitlement.
KindstringAlways Group.
IdstringEntra group id.
Mailstring or $nullGroup 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 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 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.

examples/workflows/templates/entraid-joiner.psd1
@{
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.'
}
}
)
}
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' }

# 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 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).