Forum Discussion

Deleted's avatar
Deleted
Not applicable
Oct 06, 2026

Automating Microsoft Entra ID User Lifecycle Management with PowerShell and Microsoft Graph

Introduction — The Problem

Managing user accounts throughout the employee lifecycle is one of the common responsibilities of an Identity or Microsoft 365 administrator.

When an employee joins an organization, administrators typically create the required identity, assign licenses, configure access, and provision devices and applications.

The more challenging part often comes when an employee leaves.

A typical offboarding process may require administrators to:

  • Identify employees whose employment has ended
  • Disable the Microsoft Entra ID account
  • Remove directly assigned licenses
  • Record what actions were performed
  • Notify administrators or the appropriate support team
  • Maintain evidence for auditing and troubleshooting

If these tasks are performed manually, there is a risk of inconsistent execution, delayed account disablement, forgotten licenses, and incomplete audit records.

To explore this problem from an administrator's perspective, I built a PowerShell-based automation project using Microsoft Graph.

The project is:

Microsoft Entra ID User Lifecycle Automation with PowerShell

The goal is to provide a controlled and auditable workflow for detecting expired users and performing defined offboarding actions.

1. Why User Lifecycle Automation Matters

Identity lifecycle management is not simply about creating and deleting accounts.

It is about controlling access throughout the user's relationship with the organization.

A simplified lifecycle can be represented as:

Joiner ↓ User Account Created ↓ Access & Licenses Assigned ↓ Employee Active ↓ Mover / Role Change ↓ Employee Leaves ↓ Offboarding ↓ Access Revocation

For the leaver stage, organizations need reliable processes to make sure that accounts do not remain active after an employee's departure.

Microsoft Entra ID provides native Lifecycle Workflows capabilities for Joiner-Mover-Leaver scenarios. These workflows use tasks and execution conditions to automate lifecycle activities.

This project takes a different approach.

Instead of replacing native Microsoft capabilities, it demonstrates how administrators can build a custom PowerShell and Microsoft Graph automation layer when they need scripting flexibility, custom reporting, administrator-controlled execution, or a learning platform for Graph automation.

2. Solution Architecture

The project is designed around a simple lifecycle pipeline:

Employee Created ↓ Expiry / Leave Date Assigned ↓ Daily Automation Runs ↓ Find Expired Users ↓ Validate User ↓ Disable Entra ID Account ↓ Remove Direct Licenses ↓ Create Audit Log ↓ Send Administrator Notification

The workflow is divided into separate PowerShell scripts instead of placing everything into a single large script.

This separation provides several advantages:

  • Easier troubleshooting
  • Easier testing
  • Clear permissions per function
  • Reusable individual components
  • Better maintainability
  • Easier future integration with scheduling or orchestration systems

The project also includes an orchestrator script that coordinates the individual lifecycle stages.

3. Technology Stack

The project uses the following technologies:

Microsoft Entra ID

Microsoft Entra ID provides the identity platform where users, accounts, licenses, and lifecycle-related attributes are managed.

The project uses the user's:

employeeLeaveDateTime

property as the lifecycle date.

Microsoft documents employeeLeaveDateTime as an attribute that can be used with leaver lifecycle workflows.

Microsoft Graph

Microsoft Graph provides the API layer used by the PowerShell scripts.

The project uses Graph operations for:

  • Reading users
  • Updating user attributes
  • Disabling accounts
  • Reading license information
  • Removing directly assigned licenses
  • Sending administrator notifications

PowerShell

PowerShell provides the automation and orchestration layer.

The project is designed for Windows PowerShell 5.1+ and uses Microsoft Graph PowerShell modules.

4. Automation Workflow

The complete workflow consists of several stages.

Stage 1 — Assign an Expiry Date

The administrator can assign an employee leave date to an Entra ID user.

The project stores this information using:

employeeLeaveDateTime

For example:

Employee Leave Date ↓ 2026-10-01

The project then uses this value during its daily detection process.

Microsoft Graph supports updating employeeLeaveDateTime using Update-MgUser.

Stage 2 — Detect Expired Users

The detection script retrieves users and checks their configured leave dates.

The basic logic is:

Leave Date < Today's Date ↓ Expired

Users whose leave date is today are not treated as expired by this implementation.

The detection stage is intentionally read-only.

It does not:

  • Disable users
  • Remove licenses
  • Modify accounts
  • Send notifications
  • Write lifecycle audit events

Instead, it generates a report that can be reviewed before taking action.

Stage 3 — Disable Expired Users

Once an expired and enabled user is identified, the disable stage can be executed.

The project re-checks the user before making the change.

This is important because the state may have changed between detection and execution.

The script verifies that:

User still exists ↓ Account is still enabled ↓ Leave date is still expired ↓ Disable account

This reduces the chance of acting on stale information.

Stage 4 — Remove Directly Assigned Licenses

After successful account disablement, the automation can remove directly assigned licenses.

The project deliberately distinguishes between:

Direct license assignment

and

Group-based license assignment

For direct assignments, Microsoft Graph's Set-MgUserLicense API supports adding and removing licenses.

For group-based licensing, removing the user's license directly is generally not the correct lifecycle operation.

Instead, the appropriate approach is to manage the user's membership in the licensing group according to the organization's access model.

Therefore, this project focuses on directly assigned licenses.

Stage 5 — Audit

Every important lifecycle action is recorded in an audit CSV.

The audit information includes fields such as:

  • Event ID
  • Timestamp
  • Action
  • Display Name
  • User Principal Name
  • Employee Leave Date
  • Previous Account Status
  • New Account Status
  • Result
  • Performed By
  • Windows User
  • Computer Name
  • Tenant ID
  • Notification Status
  • Notification Timestamp

This provides an execution history that can be used for:

  • Troubleshooting
  • Operational review
  • Compliance evidence
  • Change tracking
  • Incident investigation

The audit process also handles migration from an older audit schema so that historical records can be retained when the schema evolves.

Stage 6 — Administrator Notification

After successful lifecycle actions are recorded, the notification script can send a summary to the administrator.

The notification contains information such as:

  • Event ID
  • Timestamp
  • Action
  • User
  • Leave date
  • Previous account status
  • New account status
  • Result
  • Performed By
  • Computer

The project uses Microsoft Graph's Send-MgUserMail functionality for email notification.

This means the automation does not require a separate SMTP implementation.

5. Script-by-Script Explanation

The repository is structured into individual scripts.

01 — Set User Expiry Date

Scripts/01-Set-UserExpiryDate.ps1

Purpose:

Set or update the user's employeeLeaveDateTime.

This provides the lifecycle date that the detection process evaluates.

02 — Get Expired Users

Scripts/02-Get-ExpiredUsers.ps1

Purpose:

Detect users whose configured leave date has passed.

Characteristics:

  • Read-only
  • Retrieves users from Microsoft Entra ID
  • Checks employeeLeaveDateTime
  • Identifies expired users
  • Generates a CSV report

Required permission:

User.Read.All

03 — Disable Expired Users

Scripts/03-Disable-ExpiredUsers.ps1

Purpose:

Disable expired accounts that are still enabled.

The script performs validation before changing the account.

It also supports controlled execution and preview scenarios.

04 — Remove User Licenses

Scripts/04-Remove-UserLicenses.ps1

Purpose:

Remove directly assigned licenses from a user.

The script:

  1. Retrieves the user
  2. Retrieves assigned licenses
  3. Maps license IDs to license information
  4. Displays the licenses
  5. Confirms the action when running interactively
  6. Removes direct assignments
  7. Re-reads the user
  8. Verifies the result
  9. Generates a report

Required permission includes:

LicenseAssignment.ReadWrite.All

Microsoft documents this permission for direct license assignment operations through Graph.

05 — Write Audit Log

Scripts/05-Write-AuditLog.ps1

Purpose:

Create a structured record of lifecycle actions.

The script is responsible for maintaining the audit trail rather than performing the lifecycle action itself.

This separation makes it possible to reuse the audit mechanism for future lifecycle operations.

06 — Send Administrator Notification

Scripts/06-Send-AdminNotification.ps1

Purpose:

Send an administrator notification containing successful lifecycle actions.

The notification process also updates the audit records to indicate that the corresponding events were notified.

Run — Lifecycle Orchestrator

Scripts/Run-EntraUserLifecycle.ps1

This is the main orchestration script.

It coordinates the lifecycle stages:

02 Detection ↓ 03 Disable ↓ 04 License Removal ↓ 05 Audit ↓ 06 Notification

The orchestrator is designed to keep the individual scripts modular while providing a single operational entry point.

6. Safety Model

One of the most important design goals of the project is preventing accidental destructive operations.

Disabling an account and removing licenses are destructive administrative actions.

For that reason, the project supports multiple execution modes.

WhatIf Mode

The administrator can execute the workflow in preview mode.

The purpose is to answer:

"What would this automation do if I ran it for real?"

The WhatIf process should:

  • Detect expired users
  • Display the users that would be affected
  • Show planned actions
  • Avoid modifying Entra ID
  • Avoid removing licenses
  • Avoid creating lifecycle audit events
  • Avoid sending lifecycle notification emails

This makes WhatIf useful for initial validation and troubleshooting.

Interactive Confirmation

Normal execution uses administrator confirmation before destructive actions.

The lifecycle process separates the decisions:

Disable expired accounts? ↓ Yes / No ↓ Disable accounts ↓ Remove direct licenses? ↓ Yes / No

This is intentionally different from automatically performing every action immediately.

Force Mode

For controlled automation scenarios, the project also supports:

-Force

Force mode bypasses interactive confirmation.

This is useful when the orchestrator is executed through a scheduled task or another controlled automation mechanism.

The administrator should only use Force mode after validating the environment and lifecycle rules.

7. License Management

License management requires particular attention.

A user can have licenses assigned directly or through group-based licensing.

For example:

User ├── Direct License └── Group-Based License

This project handles the direct license scenario.

Direct licenses can be removed using Microsoft Graph.

Group-based licensing should instead be managed through the appropriate licensing group.

This distinction is important because simply removing a license from a user does not necessarily represent the correct access-management action when the license is inherited through group membership.

The project therefore avoids presenting license removal as a universal "remove all access" operation.

8. Audit Logging

Automation without auditability can create another operational problem.

If an automation disables an account at 2:00 AM, administrators should be able to determine:

  • Which user was affected?
  • When did the action occur?
  • What action was performed?
  • Was the operation successful?
  • Who executed the automation?
  • From which computer?
  • Was a notification sent?

The project records these details in a structured CSV audit file.

Example lifecycle event:

Action: Disable Result: Success Previous Status: Enabled New Status: Disabled Performed By: Administrator Computer: Management Server

A second event can record license removal:

Action: RemoveLicense Result: Success

This provides a simple but useful operational audit trail.

9. Administrator Notifications

After successful actions are completed, administrators should not need to manually open CSV files every day just to determine whether the automation worked.

The notification component provides a summary of successful actions.

The email can contain multiple lifecycle events in a single message.

For example:

Entra ID Lifecycle Automation - 2 Action(s) Action Result ------------------------- Disable Success RemoveLicense Success

The message also contains contextual information such as the administrator account and computer from which the automation was executed.

The project uses Microsoft Graph mail functionality rather than implementing a separate SMTP service. Microsoft provides Send-MgUserMail for sending messages through Graph.

10. Permissions & Least Privilege

Permission design is an important part of Microsoft Graph automation.

The project separates permissions according to script responsibilities.

A simplified model is:

ScriptPurposeRequired Permission
01Set expiry dateUser.ReadWrite.All / lifecycle attribute permissions depending on implementation
02Detect usersUser.Read.All
03Disable usersUser.Read.All + User.ReadWrite.All
04Remove direct licensesUser.Read.All + LicenseAssignment.ReadWrite.All
05AuditLocal file access
06Send notificationUser.Read.All + Mail.Send

The actual production permission model should be reviewed and reduced according to the authentication method and organizational requirements.

Microsoft's documentation recommends using the least-privileged permission available for the required operation.

For example, the detection script only needs to read users, so it should not require write permissions simply because another script in the project needs them.

This separation is one of the areas I intend to improve further as the project evolves.

11. Testing

Before using lifecycle automation against production users, testing is essential.

The project was tested using controlled test accounts.

The testing process included:

Expiry Date Testing

A test user's employeeLeaveDateTime was configured.

The detection script was then used to verify that the user appeared in the expected report.

Detection Testing

The detection process was tested against users with:

  • No expiry date
  • Future expiry date
  • Current date
  • Expired date
  • Expired and enabled status
  • Expired and already disabled status

WhatIf Testing

The full orchestration process was tested in WhatIf mode to verify that expired users were detected without performing destructive operations.

Disable Testing

A controlled test user was used to verify account disablement.

The automation successfully changed the account from:

Enabled

to:

Disabled

License Testing

A test license was assigned directly to the test account.

The automation then detected and removed the direct license.

The result was verified by reading the user's assigned licenses again.

Audit Testing

The resulting Disable and RemoveLicense actions were recorded in the audit log.

Notification Testing

The notification process successfully generated an administrator email containing the lifecycle actions and their results.

The testing approach is documented in the repository's Test Plan.

12. GitHub Repository

The complete project, including scripts and documentation, is available on GitHub:

https://github.com/habibnp01-prog/Entra-ID-User-Lifecycle-Automation?utm_source=chatgpt.com

The repository contains:

README.md CHANGELOG.md CONTRIBUTING.md Scripts/ ├── 01-Set-UserExpiryDate.ps1 ├── 02-Get-ExpiredUsers.ps1 ├── 03-Disable-ExpiredUsers.ps1 ├── 04-Remove-UserLicenses.ps1 ├── 05-Write-AuditLog.ps1 ├── 06-Send-AdminNotification.ps1 └── Run-EntraUserLifecycle.ps1 Documentation/ ├── Architecture.md ├── Installation.md ├── Permissions.md ├── Security.md ├── Test-Plan.md └── Troubleshooting.md

The repository is intended not only to demonstrate the final automation but also to document how an administrator can understand, test, troubleshoot, and extend it.

13. Future Improvements

The current implementation focuses on a controlled and understandable core lifecycle.

There are several areas that can be improved in future releases.

Certificate-Based Authentication

The current implementation can be used interactively with Microsoft Graph.

A future production-oriented version can use application authentication with a certificate instead of relying on an interactive administrator session.

Least-Privilege Application Permissions

The application permission model can be further refined so that each automation scenario receives only the permissions it actually requires.

Scheduled Execution

The orchestrator can be integrated with:

  • Windows Task Scheduler
  • Azure Automation
  • Azure Functions
  • Other enterprise scheduling platforms

Configuration-Driven Execution

Future versions can move administrator settings into a controlled configuration file.

For example:

Admin Email Report Location Notification Settings Execution Mode Lifecycle Rules

Improved Group-Based Licensing

A future version can provide controlled handling of group-based license assignment by identifying licensing groups and managing group membership according to defined rules.

Pester Testing

Automated PowerShell unit tests can be added to validate functions and lifecycle logic.

GitHub Actions

The project can use GitHub Actions to automatically validate PowerShell scripts when changes are submitted.

Additional Lifecycle Actions

Future lifecycle stages could include:

  • Group membership removal
  • Microsoft Teams access review
  • Application access review
  • Device cleanup
  • Session/token revocation
  • Manager notification
  • Service desk ticket creation

These features would need careful security and permission analysis before implementation.

14. Conclusion

User lifecycle management is a fundamental part of identity administration.

A reliable offboarding process should not depend entirely on an administrator remembering every step.

This project demonstrates how Microsoft Entra ID, Microsoft Graph, and PowerShell can be combined to create a controlled lifecycle automation workflow.

The key design principles behind the project are:

  • Automate repetitive administrative tasks
  • Separate detection from execution
  • Validate before destructive actions
  • Provide WhatIf capability
  • Require confirmation for manual destructive operations
  • Support controlled Force execution
  • Maintain an audit trail
  • Notify administrators
  • Distinguish direct licensing from group-based licensing
  • Document permissions
  • Test before production deployment

Microsoft Entra Lifecycle Workflows already provides native capabilities for Joiner-Mover-Leaver automation, and organizations should evaluate those capabilities first when they meet the requirement.

The purpose of this project is not to claim that custom PowerShell should replace native Entra Lifecycle Workflows.

Instead, it demonstrates how administrators can understand the underlying identity lifecycle process, work with Microsoft Graph, build controlled automation, and extend the solution for scenarios where custom scripting and operational control are useful.

I would welcome feedback from other Microsoft Entra ID, Microsoft 365, Identity, and PowerShell administrators on the architecture, security model, testing approach, and ideas for future improvements.

GitHub:
https://github.com/habibnp01-prog/Entra-ID-User-Lifecycle-Automation?utm_source=chatgpt.com

#MicrosoftEntraID #MicrosoftGraph #PowerShell #Microsoft365 #Identity #IAM #Automation #Azure #CyberSecurity #IdentityManagement #MicrosoftCommunity

No Replies