Removing Office 365 licenses via the AzureAD PowerShell module

The AzureAD PowerShell module has been available for a while now, so it makes sense for Office 365 admins to start adapting their scripts and skills to take advantage of the new module instead of the MSOL cmdlets. However, a little progress has been made in the actual usability, or user-friendliness of the module. Most of the cmdlets still rely on using ObjectIDs and convoluted syntaxes, and what’s even worse, documentation is still lacking and misleading at times.

One example of improperly documented cmdlet is the Set-AzureADUserLicense cmdlet, used to manage the Office 365 licenses assigned to a user. Before using the cmdlet to remove a license, lets first check the current assignments for the user:

Get-AzureADUserLicenseDetail -ObjectId 421117a2-1be8-4262-b847-927b04839c9b

ObjectId ServicePlans
-------- ------------
uhI3kio12k6-zgnQaE0M-_e2zO9BVg5OvRC0l24b9o4 {class ServicePlanInfo {...
uhI3kio12k6-zgnQaE0M-3_I0m-WsvBCsZcekemUuQA {class ServicePlanInfo {...

Crappy syntax and output, no? Here’s a slightly more convenient method:

Get-AzureADUser -SearchString pesho | Get-AzureADUserLicenseDetail | select @{n="License";e={$_.SkuPartNumber}}, @{n="DisabledPlans";e={($_.ServicePlans | ? {$_.ProvisioningStatus -eq "Disabled"}).ServicePlanName -join ","}}

License DisabledPlans
------- -------------
EMS AAD_PREMIUM,MFA_PREMIUM
ENTERPRISEPACK RMS_S_ENTERPRISE

We are taking advantage of the fact that the Get-AzureADUser cmdlet accepts free form input via the –SearchString parameter, then piping it to Get-AzureADUserLicenseDetail and using calculated properties to display the important license information.

The syntax we have to use in order to manage licenses is even more convoluted. First, we need to prepare the AssignedLicenseS (note the ‘s’ at the end!) object, which describes each individual AssignedLicense (singular this time) changes we are to make. For example, if we follow the steps in the documentation to remove the EMS SKU, we would use:

$License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
$License.SkuId = "efccb6f7-5641-4e0e-bd10-b4976e1bf68e"

$Licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
$Licenses.RemoveLicenses = $License

Set-AzureADUserLicense -ObjectId 421117a2-1be8-4262-b847-927b04839c9b -AssignedLicenses $Licenses

Aaaand… we will be greeted by an error!

041217 1926 RemovingOff1

The reason for the error is that the RemoveLicenses property of the AssignedLicenses object accepts a string value (the GUID) instead of the full AssignedLicense object like the above example suggests. So not the same as the AddLicenses property! Here’s what a working example should look like:

$Licenses.RemoveLicenses = "efccb6f7-5641-4e0e-bd10-b4976e1bf68e"

$Licenses

AddLicenses RemoveLicenses
----------- --------------
{efccb6f7-5641-4e0e-bd10-b4976e1bf68e}

Get-AzureADUser -SearchString pesho | Get-AzureADUserLicenseDetail| select @{n="License";e={$_.SkuPartNumber}}, @{n="DisabledPlans";e={($_.ServicePlans | ? {$_.ProvisioningStatus -eq "Disabled"}).ServicePlanName -join ","}}

License DisabledPlans
------- -------------
ENTERPRISEPACK RMS_S_ENTERPRISE

In case you want to both add and remove licenses in a single cmdlet, make sure that the appropriate properties of the AssignedLicenses object are set accordingly. That is, for any licenses you want to add, pass the full AssignedLicense object and set its SkuId property to populate the AddLicenses property, while for any licenses you want to remove only set the SkuId string value on the RemoveLicenses property. Here’s an example:

$License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
$License.SkuId = "efccb6f7-5641-4e0e-bd10-b4976e1bf68e"

$Licenses = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
$Licenses.AddLicenses = $License

$Licenses.RemoveLicenses = "6fd2c87f-b296-42f0-b197-1e91e994b900"

Set-AzureADUserLicense -ObjectId 421117a2-1be8-4262-b847-927b04839c9b -AssignedLicenses $Licenses

We can verify that the E3 license was removed and the EMS one was added:

Get-AzureADUser -SearchString pesho | Get-AzureADUserLicenseDetail| select @{n="License";e={$_.SkuPartNumber}}, @{n="DisabledPlans";e={($_.ServicePlans | ? {$_.ProvisioningStatus -eq "Disabled"}).ServicePlanName -join ","}}

License DisabledPlans
------- -------------
EMS

There you have it, a proper example on how to Remove licenses via the AzureAD module, that should hopefully help people struggling with this task. In all fairness, the documentation does not explicitly cover the Remove scenario and if you look at the object properties you should find clues for the correct input.

We didn’t cover the topic of disabling individual services, which involves setting further properties of the AssignedLicense object and makes the examples even more hard to read than they are now, but rest assured, this is also possible!

7 thoughts on “Removing Office 365 licenses via the AzureAD PowerShell module

  1. ricardo says:

    can you provide an example on how to do it for multiple users and multiple licenses. I have tried several way with no success

    Reply
  2. Riccardo says:

    Hi all
    Could you shed some light on how to reach the most secret info about a license packege that is “when (date) the license has been assigned/removed to a particular user?”
    I am lost in ServicePlanInfo system object…
    I manage a tenant with lot of subsidiaries and I need to reconcile the single billing invoice to all of them…
    What a nightmare…
    Riccardo

    Reply
    1. Vasil Michev says:

      If you use the Graph or the Azure AD PowerShell module, you will see the AssignedTimestamp data as part of the AssignedPlans property. Here’s an example:

      (Get-AzureADUser -SearchString huku).AssignedPlans

      Or you can crawl the Azure AD logs/Unified Audit log for any “license changed” events

      Reply
  3. Andreas says:

    Thanks for the post. I would have never expected that Microsoft is offering such a crapy API.

    Reply
  4. Nam says:

    Was stuck for a bit until I found your blog! Thank you!!!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.