The MSOnline and AzureAD PowerShell modules are retired, so any script that starts with Connect-MsolService or Connect-AzureAD has to be rewritten against Microsoft Graph PowerShell (or Microsoft Entra PowerShell). The work is a cmdlet-by-cmdlet translation using Microsoft's cmdlet map, plus fixes for four patterns that behave differently in Graph: authentication and consent, property selection, paging and filters, and licence and password operations.
Who this is for and what you will have at the end
This guide is for Microsoft 365 and Entra ID administrators who own scheduled tasks, runbooks or ad-hoc scripts written for the old modules and now see them fail. By the end you will have:
- A list of every user, app and script still calling the retired modules.
- Microsoft Graph PowerShell installed and connected with only the permissions each script needs.
- Working replacements for the most common MSOnline and AzureAD operations: user queries, licence assignment, account blocking, password resets, group membership and per-user MFA.
- A repeatable process for the remaining cmdlets in your estate.
What retired, and when
| Milestone | MSOnline | AzureAD and AzureAD-Preview |
|---|---|---|
| Deprecated | March 30, 2024 | March 30, 2024 |
| End of support | March 30, 2025 | March 30, 2025 |
| Retirement | Rolled out from early April 2025, complete for all tenants and clouds by late May 2025 | Retirement announced to start in mid-October 2025, after outage tests in September 2025 |
Two dependencies are easy to miss. Microsoft Entra Connect Sync used MSOnline in its installation wizard, and Microsoft asked customers to upgrade before April 7, 2025; versions older than 2.4.18.0 were affected. And any third-party tool or vendor script that silently imported MSOnline broke on the same schedule as your own scripts.
Prerequisites
- PowerShell 7 or later (recommended), or Windows PowerShell 5.1 with .NET Framework 4.7.2 or later and an execution policy of RemoteSigned or less restrictive.
- An account that can consent to the delegated permissions your scripts need, or an app registration with admin-consented application permissions for unattended jobs.
- Access to the Microsoft Entra admin center to read Recommendations and sign-in logs.
- The source of every script you plan to migrate, ideally in version control.
Step 1: Find everything that still calls the old modules
Start with the tenant, not the file share. Microsoft Entra publishes a recommendation named Migrate from the retiring MSOnline and AzureAD PowerShell usage to Microsoft Graph PowerShell. In the Microsoft Entra admin center go to Identity > Overview > Recommendations. The recommendation reports usage in the last 30 days, and its More details view lists the operations called, the last request time and how many users ran them.
Then check sign-in logs. Both modules sign in under the same application name, so filter on it:
- Go to Identity > Monitoring & health > Sign-in logs.
- On the non-interactive sign-ins tab, add an Application filter for Azure Active Directory PowerShell.
- Repeat the same filter for interactive sign-ins.
The accounts you find are your script owners and service accounts. Finally, search your script repositories and automation accounts for the module and cmdlet prefixes:
Get-ChildItem -Path 'D:\Scripts' -Recurse -Include *.ps1, *.psm1 |
Select-String -Pattern 'Connect-MsolService|Connect-AzureAD|-Msol|-AzureAD|Import-Module\s+(MSOnline|AzureAD)' |
Select-Object Path, LineNumber, Line |
Export-Csv .\legacy-cmdlet-usage.csv -NoTypeInformationMicrosoft's migration guidance suggests documenting each script's purpose, where it runs, how often, its business importance and which cmdlets it calls, then starting with the simplest, least critical scripts. Also ask whether each script is still needed at all; some MSOnline-era jobs duplicate what group-based licensing or Entra ID lifecycle features now do natively.
Step 2: Install Microsoft Graph PowerShell
The SDK ships as two modules: Microsoft.Graph (v1.0 endpoint) and Microsoft.Graph.Beta. Install the v1.0 module for production scripts:
Install-Module Microsoft.Graph -Scope CurrentUser -Repository PSGallery -Force
Get-InstalledModule Microsoft.GraphThe full module pulls in more than 47 sub-modules. On build agents and automation workers, install only what you use, for example Microsoft.Graph.Authentication, Microsoft.Graph.Users, Microsoft.Graph.Users.Actions and Microsoft.Graph.Groups. Install the beta module only when a script needs an API that exists only in beta, because Microsoft states that beta endpoints can change without notice.
Step 3: Replace the connection
Connect-MsolService and Connect-AzureAD both map to Connect-MgGraph. The big change is consent: the old modules were pre-authorized, while Graph PowerShell requests only the scopes you ask for.
For interactive admin work, request the scopes the script needs:
Connect-MgGraph -Scopes 'User.ReadWrite.All', 'Organization.Read.All'
Get-MgContext | Select-Object -ExpandProperty ScopesFor unattended jobs, use an app registration with a certificate rather than a stored user password:
Connect-MgGraph -ClientId '<app-id>' -TenantId '<tenant-id>' -CertificateThumbprint '<thumbprint>'In Azure Automation or on an Azure VM, a managed identity removes the certificate as well:
Connect-MgGraph -IdentityApplication permissions for app-only and managed identity connections are granted on the app or service principal (with admin consent), not with -Scopes. To find the permissions a cmdlet needs, use Find-MgGraphCommand:
Find-MgGraphCommand -Command Set-MgUserLicense | Select-Object -First 1 -ExpandProperty PermissionsMicrosoft also documents creating a dedicated app registration for delegated Graph PowerShell use, with Assignment required set to Yes, so only named admins can sign in through it. That keeps consented permissions isolated from the default Microsoft Graph PowerShell app. For broader least-privilege design, see the zero trust remote access architecture.
Step 4: Map the cmdlets
Microsoft publishes a full map from AzureAD and MSOnline cmdlets to Graph cmdlets. The entries you will hit most often:
| MSOnline | AzureAD | Microsoft Graph PowerShell |
|---|---|---|
| Connect-MsolService | Connect-AzureAD | Connect-MgGraph |
| Get-MsolUser | Get-AzureADUser | Get-MgUser |
| New-MsolUser | New-AzureADUser | New-MgUser |
| Set-MsolUser | Set-AzureADUser | Update-MgUser |
| Remove-MsolUser | Remove-AzureADUser | Remove-MgUser |
| Set-MsolUserPrincipalName | - | Update-MgUser |
| Set-MsolUserLicense | Set-AzureADUserLicense | Set-MgUserLicense |
| Get-MsolAccountSku | Get-AzureADSubscribedSku | Get-MgSubscribedSku |
| Set-MsolUserPassword | Set-AzureADUserPassword | Reset-MgUserAuthenticationMethodPassword or Update-MgUser |
| Get-MsolGroup | Get-AzureADGroup | Get-MgGroup |
| Get-MsolGroupMember | Get-AzureADGroupMember | Get-MgGroupMember |
| Add-MsolGroupMember | Add-AzureADGroupMember | New-MgGroupMemberByRef |
| Get-MsolRole | Get-AzureADDirectoryRole | Get-MgDirectoryRole |
| Get-MsolRoleMember | Get-AzureADDirectoryRoleMember | Get-MgDirectoryRoleMember |
| Get-MsolDomain | Get-AzureADDomain | Get-MgDomain |
| Get-MsolCompanyInformation | Get-AzureADTenantDetail | Get-MgOrganization |
| Get-MsolDevice | Get-AzureADDevice | Get-MgDevice |
Two more mappings that scripts often need: Restore-MsolUser becomes Restore-MgDirectoryDeletedItem, and Revoke-AzureADUserAllRefreshToken becomes Revoke-MgUserSignInSession. Some legacy cmdlets have no Graph PowerShell cmdlet in the map, including Get-MsolUserByStrongAuthentication and Reset-MsolStrongAuthenticationMethodByUpn, and several Application Proxy management cmdlets. For those, call the underlying API with Invoke-MgGraphRequest or use the admin center.
Step 5: Rewrite the patterns that behave differently
A straight rename rarely works. These are the changes that cause most broken migrations.
Properties and paging
Get-MgUser returns only a default subset of user properties, and without -All it returns only the first page of results. Ask for what you need explicitly:
Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled, UsageLocation, AssignedLicenses |
Select-Object DisplayName, UserPrincipalName, AccountEnabled, UsageLocationFilters and search
-SearchString from the AzureAD module becomes -Search together with -ConsistencyLevel eventual, or a server-side -Filter. Advanced queries such as counting need -ConsistencyLevel eventual and a count variable:
Get-MgUser -Filter "startsWith(DisplayName, 'a')" -ConsistencyLevel eventual -CountVariable matchCount -All
Get-MgUser -Search '"DisplayName:Conf"' -ConsistencyLevel eventual -CountVariable matchCountLicences
Set-MgUserLicense takes hash tables of SKU IDs. Microsoft's examples always pass both -AddLicenses and -RemoveLicenses, using an empty value for the one you don't need. Users still need a UsageLocation before a licence can be assigned.
Connect-MgGraph -Scopes 'User.ReadWrite.All', 'Organization.Read.All'
$e5 = Get-MgSubscribedSku -All | Where-Object SkuPartNumber -eq 'SPE_E5'
Update-MgUser -UserId 'adele@contoso.com' -UsageLocation 'US'
Set-MgUserLicense -UserId 'adele@contoso.com' -AddLicenses @{ SkuId = $e5.SkuId } -RemoveLicenses @()To swap one SKU for another, remove the old one with -RemoveLicenses @($oldSku.SkuId) and -AddLicenses @{}, then add the new one.
Blocking sign-in and resetting passwords
Set-MsolUser -BlockCredential $true becomes an update of accountEnabled:
Update-MgUser -UserId 'adele@contoso.com' -BodyParameter @{ accountEnabled = $false }For password resets, update passwordProfile. The least privileged permission for this property is User-PasswordProfile.ReadWrite.All, and in delegated scenarios the signed-in admin also needs a role that is allowed to reset that user's password.
$params = @{
passwordProfile = @{
forceChangePasswordNextSignIn = $true
password = '<temporary-password>'
}
}
Update-MgUser -UserId 'adele@contoso.com' -BodyParameter $paramsPer-user MFA
Scripts that used Set-MsolUser -StrongAuthenticationRequirements need a different approach. Per-user MFA state is exposed in the Microsoft Graph beta API through the perUserMfaState property of /users/{id}/authentication/requirements, with the values disabled, enabled and enforced. Check the Update authentication method states API page for the permission it requires, then call it with Invoke-MgGraphRequest:
$uri = 'https://graph.microsoft.com/beta/users/adele@contoso.com/authentication/requirements'
Invoke-MgGraphRequest -Method GET -Uri $uri
Invoke-MgGraphRequest -Method PATCH -Uri $uri -Body (@{ perUserMfaState = 'enabled' } | ConvertTo-Json)Before porting this logic, check whether you still need it. Microsoft recommends Conditional Access (Entra ID P1 or P2) or security defaults over per-user MFA, and warns not to enable per-user MFA alongside Conditional Access policies.
Optional shortcut: Microsoft Entra PowerShell compatibility mode
For large AzureAD script libraries, Microsoft Entra PowerShell offers a compatibility mode that Microsoft describes as having over 98% compatibility with the AzureAD module. Replace the Connect-AzureAD line with three lines and the rest of the script can keep its AzureAD cmdlet names:
Import-Module -Name Microsoft.Entra.Users
Connect-Entra -Scopes 'User.Read.All'
Enable-EntraAzureADAlias
Get-AzureADUser -Top 5Test-EntraScript checks a script and lists incompatible commands with their line numbers. Known issues include -Filter and -SearchString not always working, and output objects that differ slightly from the AzureAD ones. Treat compatibility mode as a bridge for AzureAD scripts: the aliases cover AzureAD cmdlet names, so MSOnline scripts still need their cmdlets mapped, and scripts rewritten natively for Graph PowerShell don't need to move again.
Verification
For each migrated script:
- Run it in a test tenant or against a pilot group with
-WhatIfwhere the cmdlet supports it. - Compare output with a last known good export from the legacy script. Use
Get-Memberto see how property names changed on the Graph objects. - Run
Get-MgContextinside scheduled jobs and log theAuthType,AppNameandScopes, so an unexpected delegated sign-in or missing permission shows up in the job log. - After cutover, revisit the Entra recommendation and the Azure Active Directory PowerShell sign-in filter. Both should go quiet once nothing calls the old modules.
Troubleshooting
The term 'Connect-MsolService' is not recognized on a new build agent means the old module isn't installed, which is now expected. Don't reinstall it; finish migrating the script.
403 Forbidden or Insufficient privileges from a Graph cmdlet almost always means a missing permission. Run Find-MgGraphCommand -Command <cmdlet> | Select-Object -First 1 -ExpandProperty Permissions, reconnect with the right -Scopes for delegated access, or grant and consent the application permission for app-only access.
Properties come back empty (for example UsageLocation or AccountEnabled) because they aren't in the default property set. Add them to -Property.
Only some users returned usually means -All is missing, so only the first page came back.
Advanced query errors on -Filter, -Search or counts usually need -ConsistencyLevel eventual plus -CountVariable.
Licence assignment fails for synchronized users when UsageLocation is empty. Accounts synchronized from on-premises Active Directory don't have a location by default; set it in the source or with Update-MgUser.
To capture the request ID and timestamp for a support case, rerun the failing command with -Debug.
Checklist
- Entra recommendation and sign-in logs reviewed; every caller identified.
- Scripts inventoried, prioritised and retired where native features replace them.
Microsoft.Graph(or only the required sub-modules) installed on every runner.- Unattended jobs moved to certificate or managed identity authentication with least-privilege application permissions.
- Every legacy cmdlet mapped;
-Property,-All, filters and licence hash tables fixed. - Per-user MFA logic replaced with the beta API or, preferably, Conditional Access.
- Old modules uninstalled from runners so nothing falls back to them.
References
- Migrate from Azure AD PowerShell to Microsoft Graph PowerShell
- Find Azure AD and MSOnline cmdlets in Microsoft Graph PowerShell
- Install the Microsoft Graph PowerShell SDK
- Use Microsoft Graph PowerShell authentication commands
- Use Find-MgGraphCommand
- Error handling and troubleshooting cmdlets
- Get-MgUser reference
- Assign Microsoft 365 licenses to user accounts with PowerShell
- Block Microsoft 365 user accounts with PowerShell
- Update user (Microsoft Graph)
- Enable per-user multifactor authentication
- Run legacy scripts in compatibility mode - Microsoft Entra PowerShell
- Microsoft Entra PowerShell FAQ
- Action required: MSOnline and AzureAD PowerShell retirement - 2025 info and resources
- Important update: AzureAD PowerShell retirement