Entra / Microsoft 365 · Licensing
Remove service plan mg graph
Remove an individual service plan from a SKU assigned to Microsoft 365 accounts using cmdlets from the Microsoft Graph PowerShell SDK.
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 NameIf (!($ModulesLoaded -match "ExchangeOnlineManagement")) {Write-Host "Please connect to the Exchange Online Management module and then restart the script"; break}If (!($ModulesLoaded -like "*Microsoft.Graph*")) {Write-Host "Please connect to the Microsoft Graph PowerShell SDK and then restart the script"; break}# We seem to be fully connected to the necessary modules so we can proceed# Make sure we're using the beta endpoint to make sure we can get license information$Profile = (Get-MgProfile).NameIf ($Profile -ne "beta") { Select-MgProfile Beta }# Check Scopes$Scopes = Get-MgContext | Select-Object -Expandproperty ScopesIf ("User.ReadWrite.All" -and "Directory.ReadWrite.All" -notin $Scopes) { Connect-MgGraph -Scopes "User.ReadWrite.All","Directory.ReadWrite.All" }$CSVOutputFile = "c:\temp\ServicePlanRemovals.csv"# Find the set of SKUs used in the tenant[array]$Skus = (Get-MgSubscribedSku)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 ServicePlanName, ServicePlanId | Sort ServicePlanName} #end ifElseif ($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-MgUser). The important thing is to have an object identifier for each account to# retrieve license information[array]$Mbx = (Get-ExoMailbox -RecipientTypeDetails UserMailbox -Filter {Office -eq "Dublin"} -ResultSize Unlimited | Select 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-MgUser -UserId $M.ExternalDirectoryObjectId)$i = 0Foreach ($License in $User.AssignedLicenses) {If ($License.SkuId -eq $SelectedSkuId){ # We match the service plan to removeWrite-Host ("Removing service plan {0} from SKU {1} for account {2}" -f $ServicePlanName, $SelectedSKUId, $M.DisplayName) -foregroundcolor Red$ExistingDisabledPlans = $NullForEach ($S in $User.AssignedLicenses) { # Check for existing disabled licensesIf ($S.SkuId -eq $SelectedSkuId) {$ExistingDisabledPlans = $S.DisabledPlans }}$ExistingDisabledPlans += $ServicePlanId # Add the plan we want to remove to the set of disabled plans$LicenseToRemove = New-Object -TypeName Microsoft.Graph.PowerShell.Models.MicrosoftGraphAssignedLicense$LicenseToRemove.SkuId = $SelectedSkuId$LicenseToRemove.DisabledPlans = $ExistingDisabledPlans$Status = Set-MgUserLicense -UserId $User.id -AddLicenses $LicenseToRemove -RemoveLicenses @()$LicenseUpdateMsg = $ServicePlanName + " service plan removed from account " + $M.UserPrincipalName + " on " + (Get-Date) + " from " + $SelectedSkuSet-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