PowerShell Manage Microsoft 365 Users with MSOnline

Bulk license assignment for 50 new hires, finding unlicensed accounts before the monthly billing cycle, or disabling a departed employee’s Microsoft 365 access in seconds — these are the everyday tasks that justify automating PowerShell Office 365 users management. This post focuses on practical M365 user administration using the MSOnline module alongside notes on its Microsoft.Graph successor, covering user queries, license assignment, account management, and reporting.
Connect to MSOnline
The MSOnline module connects to the Azure AD/M365 tenant. It requires modern authentication and will prompt for credentials and MFA if enabled on the account.
# Install MSOnline module if not already present
Install-Module -Name MSOnline -Scope CurrentUser -Force
# Connect to your tenant
Connect-MsolService
# Verify connection — returns your tenant domain
Get-MsolDomain | Where-Object IsDefault -eq $true
Note: Microsoft is retiring MSOnline. For new scripts, prefer the Microsoft.Graph module (see the Graph API post). MSOnline continues to function in existing environments but will eventually require migration.
Get All M365 Users
Get-MsolUser returns user accounts from your Microsoft 365 tenant. By default it returns up to 500 users — use -All to retrieve the full list. Results include cloud-only and synced-from-AD accounts.
# All users
Get-MsolUser -All | Select-Object DisplayName, UserPrincipalName, IsLicensed, BlockCredential
# Only enabled accounts
Get-MsolUser -All | Where-Object BlockCredential -eq $false |
Select-Object DisplayName, UserPrincipalName, IsLicensed
Get Licensed vs Unlicensed Users
Filter on the IsLicensed property to identify users who need a license assigned or who are holding a license unnecessarily.
# Unlicensed users (enabled accounts with no license)
Get-MsolUser -All -UnlicensedUsersOnly |
Select-Object DisplayName, UserPrincipalName, Country |
Export-Csv -Path "C:\Logs\unlicensed-users.csv" -NoTypeInformation
# Licensed users
Get-MsolUser -All | Where-Object IsLicensed -eq $true |
Select-Object DisplayName, UserPrincipalName,
@{ N="Licenses"; E={ ($_.Licenses.AccountSkuId) -join "; " } }
Assign a License
License assignment requires the AccountSkuId identifier, which is in the format TenantName:SKU_NAME. Retrieve available SKUs with Get-MsolAccountSku before assigning.
# See available licenses and remaining quantities
Get-MsolAccountSku | Select-Object AccountSkuId, ActiveUnits, ConsumedUnits,
@{ N="Available"; E={ $_.ActiveUnits - $_.ConsumedUnits } }
AccountSkuId ActiveUnits ConsumedUnits Available
------------ ----------- ------------- ---------
contoso:ENTERPRISEPACK 500 487 13
contoso:EXCHANGESTANDARD 50 22 28
# Assign a license to a user
Set-MsolUserLicense `
-UserPrincipalName "[email protected]" `
-AddLicenses "contoso:ENTERPRISEPACK"
# Assign licenses to multiple users from a list
$upns = @("[email protected]", "[email protected]", "[email protected]")
foreach ($upn in $upns) {
Set-MsolUserLicense -UserPrincipalName $upn -AddLicenses "contoso:ENTERPRISEPACK"
Write-Output "Licensed: $upn"
}
Disable and Delete M365 User
Blocking a user prevents sign-in without deleting the account (useful during offboarding review). Deletion removes the account but it is recoverable from the recycle bin for 30 days.
# Block sign-in (disable account)
Set-MsolUser -UserPrincipalName "[email protected]" -BlockCredential $true
# Remove license first (optional but good practice — frees the license)
Set-MsolUserLicense -UserPrincipalName "[email protected]" -RemoveLicenses "contoso:ENTERPRISEPACK"
# Delete the user (soft-delete — recoverable for 30 days)
Remove-MsolUser -UserPrincipalName "[email protected]" -Confirm:$false
Export User Report to CSV
A complete user export with license and account status is the most common M365 admin report request.
$reportPath = "C:\Logs\m365-users-$(Get-Date -Format yyyyMMdd).csv"
Get-MsolUser -All |
Select-Object DisplayName, UserPrincipalName, IsLicensed, BlockCredential,
@{ N="Licenses"; E={ ($_.Licenses.AccountSkuId) -join "; " } },
@{ N="CreatedDate"; E={ $_.WhenCreated } } |
Export-Csv -Path $reportPath -NoTypeInformation
Write-Output "Report saved to $reportPath"
Common Errors and Fixes
- MSOnline being replaced by Microsoft.Graph: Microsoft deprecated the MSOnline module and its retirement date has been announced for late 2025. For any new scripting, use the
Microsoft.Graphmodule with equivalent cmdlets likeGet-MgUser,New-MgUser, and license assignment viaSet-MgUserLicense. Existing MSOnline scripts will need migration — the command structures are similar but not identical. - License SKU identifier format is non-obvious: The
AccountSkuIdformat (TenantName:PRODUCT_CODE) is not user-friendly. ThePRODUCT_CODEpart (likeENTERPRISEPACKfor E3,SPBfor Microsoft 365 Business Premium) must match exactly. RunGet-MsolAccountSkuon your tenant to see your exact SKU identifiers — they include your tenant name prefix which differs per organization.
Related Cmdlets / See Also
- PowerShell Connect to Microsoft 365 with ExchangeOnline
- PowerShell Microsoft Graph API: Get M365 Data
Wrapping Up
MSOnline covers the immediate licensing and user management needs in M365 — but start planning your migration to Microsoft.Graph for any scripts you expect to run beyond 2025. As a next step, build a monthly license review script that identifies users who haven’t signed in for 60 days and are consuming a paid license, giving you the data to reclaim unused licenses before the next billing cycle.


