Entra / Microsoft 365 · Licensing
Remove licenses disabled accounts
Example of how to remove licenses from disabled Entra ID accounts.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -NoWelcome -Scopes Directory.ReadWrite.All# Define service plans that we shouldn't disable
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -NoWelcome -Scopes Directory.ReadWrite.All# Define service plans that we shouldn't disable[array]$Exclusions = "ENTERPRISEPACK", "SPE_E3", "SPE_E5", "SPE_E5_NOPSTNCONF", "SPB", "ENTERPRISEPREMIUM_NOPSTNCONF", "ENTERPRISE_PREMIUM"Clear-Host# Look for disabled accountsWrite-Host "Checking for disabled Entra ID user accounts..."[array]$Accounts = Get-MgUser -All -Filter "accountEnabled eq false and userType eq 'member'" -Property `AccountEnabled, Id, DisplayName, userPrincipalName, assignedLicenses, licenseAssignmentStatesIf (!($Accounts)) {Write-Host "No disabled accounts found - exiting"Break} Else {Write-Host ("Processing {0} disabled Entra ID user accounts" -f $Accounts.count)}[int]$LicenseRemovalCount = 0$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($Account in $Accounts) {# Get the currently assigned licensesWrite-Host ("Checking licenses for disabled account {0}" -f $Account.userPrincipalName) -ForegroundColor Yellow[array]$CurrentLicensesStage1 = Get-MgUserLicenseDetail -UserId $Account.Id | Select-Object SkuId, SkuPartNumber[array]$LicensesToRemove = $Null[array]$RemovedLicenses = $Null[array]$CurrentLicensesStage2 = $Null$GroupName = $Null# See if any licenses are assigned by group-based licensing[array]$GroupAssignments = $Account.licenseAssignmentStates `| Where-Object {$Null -ne $_.AssignedByGroup -and $_.State -eq "Active"} | Select-Object SkuId, AssignedByGroup$GroupsHash = @{}ForEach ($G in $GroupAssignments) { $GroupsHash.Add([string]$G.SkuId,[string]$G.AssignedByGroup) }ForEach ($License in $CurrentLicensesStage1) {# If a group-based license is found, flag it. Otherwise add to the licenses to check in the next stageIf ($License.SkuId -in $GroupAssignments.SkuId ) {$GroupName = (Get-MgGroup -GroupId $GroupsHash[$License.SkuId]).DisplayNameWrite-Host ("License {0} assigned by group-based licensing (group {1}): can't remove it" -f $License.SkuPartNumber, $GroupName ) -ForegroundColor Red} Else {$CurrentLicensesStage2 += $License}}ForEach ($License in $CurrentLIcensesStage2) {If ($License.SkuPartNumber -in $Exclusions) {Write-Host ("Excluded license {0} found and will not be removed" -f $License.SkuPartNumber) -ForegroundColor Red} Else {$LicensesToRemove += $License.SkuId$RemovedLicenses += $License.SkuPartNumber}}# Remove the set of licenses that are safe to deduct, but only if there are some licenses to remove!If ($LicensesToRemove) {$LicenseNamesRemoved = $RemovedLicenses -join ", "Write-Host ("Removing {0} licenses from account {1}" -f $LicenseNamesRemoved, $Account.DisplayName)$Status = Set-MgUserLicense -UserId $Account.Id -AddLicenses @() -RemoveLicenses $LicensesToRemoveIf ($Status) {Write-Host ("{0} licenses removed: {1} from {2}" -f $LicensesToRemove.count, $LicenseNamesRemoved, $Account.DisplayName)$LicenseRemovalCount = $LicenseRemovalCount + $LicensesToRemove.Count$ReportLine = [PSCustomObject]@{User = $Account.UserPrincipalNameName = $Account.DisplayNameId = $Account.ID'Licenses Removed' = $LicenseNamesRemovedTimestamp = (Get-Date -format 'dd-MMM-yyyy hh:mm')}$Report.Add($ReportLine)} Else {Write-Host ("Problem removing licenses from account {0} - please investigate!" -f $Account.DisplayName)}} Else {Write-Host ("No removable licenses found for account {0}" -f $Account.DisplayName)}}Write-Host ""Write-Host ("Script finished. A total of {0} licenses were removed from {1} accounts" -f $LicenseRemovalCount, $Accounts.count)Write-Host ""$Report | Format-Table Name, 'Licenses Removed', TimeStamp -AutoSize
Attribution
Author
Office365itpros