Entra / Microsoft 365 · Licensing
Remove service plan3
Remove an individual service plan from a SKU assigned to Microsoft 365 accounts.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scopes Directory.ReadWrite.All -NoWelcome
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Function Get-Response ([string]$Prompt,[int]$NumberPossibleAnswers) {# Helper function to prompt a question and get a response$OKtoProceed = $FalseWhile ($OKToProceed -eq $False) {[int]$Answer = Read-Host $PromptIf ($Answer -gt 0 -and $Answer -le $NumberPossibleAnswers) {$OKtoProceed = $TrueReturn ($Answer) }ElseIf ($Answer -eq 0) { #break out of loop$OKtoProceed = $TrueReturn ($Answer)}} #End while}# Directory.ReadWrite.All is used to fetch subscription information for the tenant, read user details, and update user licensesConnect-MgGraph -Scopes Directory.ReadWrite.All -NoWelcome$CSVOutputFile = "c:\temp\ServicePlanRemovals.csv"# Find the set of SKUs used in the tenantWrite-Host "Checking subscriptions known to the tenant" -Foregroundcolor Yellow[array]$Skus = (Get-MgSubscribedSku)Clear-HostWrite-Host " "Write-Host "Which subscription do you want to remove a service plan from?"; [int]$i=0ForEach ($Sku in $Skus) {$i++Write-Host $i ":" $Sku.SkuPartNumber}[Int]$Answer = Get-Response -Prompt "Enter the number of the subscription to edit" -NumberPossibleAnswers $iIf (($Answer -gt 0) -and ($Answer -le $i)) {$i = ($Answer-1)[string]$SelectedSku = $Skus[$i].SkuPartNumber[string]$SelectedSkuId = $Skus[$i].SkuIdWrite-Host "OK. Selected subscription is" $SelectedSku# Find the set of service plans in the selected SKU, excluding those that we can't remove because they are assigned by the tenant[array]$ServicePlans = $Skus[$i].ServicePlans | Where-Object {$_.AppliesTo -eq 'User'} | Select-Object ServicePlanName, ServicePlanId | Sort-Object ServicePlanName} Elseif ($Answer -eq 0) { #AbortWrite-Host "Script stopping..." ; break}# Check if there are any service plans that can be removed from the SKUIf ($ServicePlans.Count -eq 0) {Write-Host "No service plans available to remove from" $SelectedSkuBreak}# Select Service plan to removeWrite-Host " "Write-Host "Which Service plan do you want to remove from" $SelectedSku; [int]$i=0ForEach ($ServicePlan in $ServicePlans) {$i++Write-Host $i ":" $ServicePlan.ServicePlanName}[Int]$Answer = Get-Response -Prompt "Enter the number of the service plan to remove" -NumberPossibleAnswers $iIf (($Answer -gt 0) -and ($Answer -le $i)) {[int]$i = ($Answer-1)[string]$ServicePlanId = $ServicePlans[$i].ServicePlanId[string]$ServicePlanName = $ServicePlans[$i].ServicePlanNameWrite-Host " "Write-Host ("Proceeding to remove service plan {0} from the {1} license for target users." -f $ServicePlanName, $SelectedSku)} Elseif ($Answer -eq 0) { #AbortWrite-Host "Script stopping..."break}# Find the set of users assigned the selected SKU[guid]$TargetSku = $SelectedSkuIdWrite-Host ("Searching for accounts assigned the {0} license" -f $SelectedSku) -foregroundcolor yellow[array]$Users = Get-MgUser -ConsistencyLevel Eventual -CountVariable Licenses -All `-Sort 'displayName' -Property Id, displayName, userPrincipalName, assignedLicenses `-Filter "assignedLicenses/any(s:s/skuId eq $TargetSku)" | Sort-Object DisplayNameIf ($Users.Count -eq 0) {Write-Host ("No user accounts found with the {0} license" -f $SelectedSku) -foregroundcolor redBreak} Else {Write-Host ("Total of {0} licensed user accounts found" -f $Users.count) -Foregroundcolor red}# Main loop through mailboxes to remove selected service plan from a SKU if the SKU is assigned to the account.$Report = [System.Collections.Generic.List[Object]]::new()ForEach ($User in $Users) {$DisabledSPs = $null$OKtoProceed = $trueWrite-Host ("Checking service plans for {0}" -f $User.DisplayName)# Fetch the set of service plans for the selected SKU[array]$AllLicenses = Get-MgUserLicenseDetail -UserId $User.Id | Where-Object {$_.SkuId -eq $SelectedSkuId} | `Select-Object -ExpandProperty ServicePlans | Sort-Object ServicePlanId -Unique# Find if any service plans are already disabled[array]$DisabledLicenses = $AllLicenses | Where-Object {$_.ProvisioningStatus -eq 'Disabled'}# Figure out if any service plans are already disabled and add to the set to updateIf ($ServicePlanId -in $DisabledLicenses.ServicePlanId) {Write-Host ("Service plan {0} is already disabled for account {1}" -f $ServicePlanName, $User.DisplayName) -foregroundcolor Yellow$OKtoProceed = $false}[array]$DisabledSPs = $ServicePlanIdIf ($DisabledLicenses) {If ($DisabledLicenses.Count -eq 1) {$DisabledSPs += $DisabledLicenses.ServicePlanId} Else {ForEach ($SP in $DisabledLicenses) {$DisabledSPs += $SP.ServicePlanId }}} # End if# Go ahead and remove the service plan from the account if it hasn't already been removedIf ($ServicePlanId -in $AllLicenses.ServicePlanId -and $OKtoProceed -eq $true) {Write-Host ("Removing service plan {0} from SKU {1} for account {2}" -f $ServicePlanName, $SelectedSKUId, $User.DisplayName) -foregroundcolor Red$LicenseOptions = @{SkuId = $SelectedSkuId ; DisabledPlans = $DisabledSPs }Try {$Status = Set-MgUserLicense -UserId $User.Id -AddLicenses $LicenseOptions -RemoveLicenses @() -ErrorAction Stop$SPRemoved = $true} Catch {$SPRemoved = $falseWrite-Output "Something bad happened:" $Status}If ($SPRemoved) {$LicenseUpdateMsg = ("Service plan {0} removed from account {1} on {2} from {3}" -f $ServicePlanName, $User.DisplayName, (Get-Date), $SelectedSKU)Write-Host ("Service plan {0} removed from SKU {1} for {2}" -f $ServicePlanName, $SelectedSku, $User.DisplayName)$ReportLine = [PSCustomObject][Ordered]@{DisplayName = $User.DisplayNameUPN = $User.UserPrincipalNameInfo = $LicenseUpdateMsgSKU = $SelectedSKUId"Service Plan" = $ServicePlanName"ServicePlanId" = $ServicePlanId }$Report.Add($ReportLine)}}} #End Foreach UserWrite-Host ("Total service plans removed from user accounts: {0}. Output CSV file available in {1}" -f $Report.Count, $CSVOutputFile)# Output the report$Report | Out-GridView -Title "Service Plan Removals from User Accounts"$Report | Export-CSV -NoTypeInformation $CSVOutputFile
Attribution
Author
Office365itpros