Entra / Microsoft 365 · Licensing
Remove service plan2
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.
# Review required modules and connection steps before running.# Connect to Microsoft Graph or Exchange Online as needed for this script.
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Function Get-Response ([string]$Prompt,[int]$NumberPossibleAnswers) {# Help 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}# Check loaded modules$ModulesLoaded = Get-Module | Select-Object NameIf (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Please connect to the Exchange Online Management module and then restart the script"; break}If (!($ModulesLoaded -like "*AzureAD*")) {Write-Host "Please connect to the Azure Active Directory module and then restart the script"; break}# We seem to be fully connected to the necessary modules so we can proceed$CSVOutputFile = "c:\temp\ServicePlanRemovals.csv"# Find the set of SKUs used in the tenant[array]$Skus = (Get-AzureADSubscribedSku)Write-Host " "Write-Host "Which Office 365 product 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 product 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 product is" $SelectedSku$ServicePlans = $Skus[$i].ServicePlans | Select-Object ServicePlanName, ServicePlanId | Sort-Object ServicePlanName} Elseif ($Answer -eq 0) { #AbortWrite-Host "Script stopping..." ; break}# 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)} #end IfElseif ($Answer -eq 0) { #AbortWrite-Host "Script stopping..." ; break }# We need to know what target accounts to remove the service plan from. In this case, we use Get-ExoMailbox to find a bunch of user mailboxes, mostly because we can use a server-side# filter. You can use whatever other technique to find target accounts (like Get-AzureADUser). The important thing is to feed the object identifier for the account to Get-MsolUser to# retrieve license information[array]$Mbx = Get-ExoMailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited | `Select-Object DisplayName, UserPrincipalName, Alias, ExternalDirectoryObjectId[int]$LicensesRemoved = 0Write-Host ("Total of {0} matching mailboxes found" -f $mbx.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 ($M in $Mbx) {Write-Host "Checking licenses for" $M.DisplayName$User = (Get-AzureADUser -ObjectId $M.ExternalDirectoryObjectId)$i = 0$UserLicenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicensesForeach ($License in $User.AssignedLicenses) {If ($License.SkuId -eq $SelectedSkuId){ # We match the service plan to remove# Set up license options to remove the selected service plan ($ServicePlanName) from the SKU ($SelectedSkuId)$License.DisabledPlans = ($License.DisabledPlans + $ServicePlanId | Sort-Object -Unique)$UserLicenses.AddLicenses += $LicenseWrite-Host ("Removing service plan {0} from SKU {1} for account {2}" -f $ServicePlanName, $SelectedSKUId, $M.DisplayName) -foregroundcolor RedSet-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $userLicenses$LicenseUpdateMsg = $ServicePlanName + " service plan removed from account " + $M.UserPrincipalName + " on " + (Get-Date) + " from " + $FullLicenseNameSet-Mailbox -Identity $M.Alias -ExtensionCustomAttribute2 $LicenseUpdateMsgWrite-Host ("Service plan {0} removed from SKU {1} for {2}" -f $ServicePlanName, $SelectedSku, $M.DisplayName)$LicensesRemoved++$ReportLine = [PSCustomObject][Ordered]@{DisplayName = $M.DisplayNameUPN = $M.UserPrincipalNameInfo = $LicenseUpdateMsgSKU = $SelectedSKUId"Service Plan" = $ServicePlanName"ServicePlanId" = $ServicePlanId }$Report.Add($ReportLine)} # End if} # End ForEach license} #End Foreach mailboxWrite-Host ("Total Licenses Removed: {0}. Output CSV file available in {1}" -f $LicensesRemoved, $CSVOutputFile)# Output the report$Report | Out-GridView$Report | Export-CSV -NoTypeInformation $CSVOutputFile
Attribution
Author
Office365itpros