Update: Because of the deprecation of the Azure AD and Microsoft Online Services PowerShell modules, Practical365 recommends that you use the Microsoft Graph PowerShell SDK to manage licenses for Microsoft 365 accounts. See this article for details about how to create a licensing report with SDK cmdlets.

In Office 365 we have three methods of managing license assignments for individual or multiple user accounts.

The Office 365 admin portal provides a simple web interface for managing license assignments. It’s easy to add a license for a user, or for multiple users, enable or disable sub-SKU features (the individual services that are included in a license), or remove a license. Licenses for multiple users can be managed at the same time. This method is useful for ad-hoc license management, or for bulk assigning licenses when you first provision an Office 365 tenant. The web interface is friendly for even a non-technical user, so license management tasks can be delegated to people outside of IT support if necessary.

Azure Active Directory group-based licensing, which I wrote about here, simplifies license management by mapping license assignments to groups. License management for end users is then a simple task of adding or removing users in groups, and doesn’t require the ongoing use of Office 365 management tools.

In this article I’m going to demonstrate how to manage Office 365 licenses using PowerShell. This is more complex than either of the previous methods, but is useful for automating license assignments as part of a user provisioning process. PowerShell is also a good method for querying license usage and generating your own custom reports for license consumption, which can help you to manage your costs over time.

We’ll look at:

The Azure AD V2 PowerShell Module

License management in Office 365 is performed using the Azure Active Directory PowerShell module. The first version of this PowerShell module is also known as the MS Online module, and uses cmdlets with “Msol” in the name, for example Connect-MsolService and Get-MsolUser.

While the MS Online module is still available today, it will be deprecated in the near future. A new Azure Active Directory PowerShell V2 module has been developed to replace it. This module is also known as the Azure AD module. The Azure AD module uses the Office 365 Graph API to interact with Office 365. Microsoft aims to migrate the functionality of the MS Online module to the Azure AD module, and recommends that you use Azure AD for any script development. In this blog post I will demonstrate license management using the Azure AD module.

The Azure AD module is installed using PowerShellGet, which is included with Windows Management Framework (WPF) 5.0 (PowerShell 5.0). Windows 10 and Windows Server 2012 R2 or later ship with PowerShell 5.0 installed by default, which means that PowerShellGet is already available. For earlier operating systems, you’ll either need to upgrade to WMF 5.0, or install PowerShellGet for PowerShell 3.0 or 4.0. Some applications such as Exchange Server are sensitive to changes in the version of WMF that is installed on the system, so you should not upgrade WMF until you’ve verified that all your installed software will continue to work

PS C:\> Install-Module AzureAD

After the Azure AD module is installed you can connect to your Office 365 tenant by running the Connect-AzureAD cmdlet, and then entering your admin credentials when prompted. The Azure AD module supports the use of multi-factor authentication (MFA).

PS C:\> Connect-AzureAD

To explore the available cmdlets in the Azure AD module, run the following command.

PS C:\> Get-Command -Module AzureAD

As a side note, Connect-AzureAD will work with stored credentials function as long as your account does not require MFA or you’re connecting from a network that allows MFA to be bypassed.

PS C:\> Connect-AzureAD -Credential (Get-StoredCredential -UserName admin@exchangeserverpro.onmicrosoft.com)

Listing Available Licenses

The Get-AzureADSubscribedSku cmdlet is used to query the licenses that your organization has subscribed to in Office 365.

PS C:\> Get-AzureADSubscribedSku | Select Sku*,*Units

SkuId                                SkuPartNumber  ConsumedUnits PrepaidUnits
-----                                -------------  ------------- ------------
6fd2c87f-b296-42f0-b197-1e91e994b900 ENTERPRISEPACK            14 class LicenseUnitsDetail {...
efccb6f7-5641-4e0e-bd10-b4976e1bf68e EMS                        2 class LicenseUnitsDetail {...

A more detailed view of the licenses that are enabled and consumed is available by expanding the PrepaidUnits property.

PS C:\> Get-AzureADSubscribedSku | Select -Property Sku*,ConsumedUnits -ExpandProperty PrepaidUnits


SkuId         : 6fd2c87f-b296-42f0-b197-1e91e994b900
SkuPartNumber : ENTERPRISEPACK
ConsumedUnits : 17
Enabled       : 25
Suspended     : 0
Warning       : 0

SkuId         : efccb6f7-5641-4e0e-bd10-b4976e1bf68e
SkuPartNumber : EMS
ConsumedUnits : 2
Enabled       : 5
Suspended     : 0
Warning       : 0

In the output above we can see that my tenant has 25 “ENTERPRISEPACK” licenses, and 5 “EMS” licenses. The SkuPartNumber for each does not precisely match the name of the license that you’ll see in Office 365 documentation or in the license management sections of the Office 365 admin portal. For example, ENTERPRISEPACK is the SkuPartNumber for the Enterprise E3 license, while EMS is the SkuPartNumber for the Enterprise Mobility and Security E3 license. A complete list of part numbers and friendly names isn’t available on Microsoft online documentation sites, although with a little searching and common sense you can usually work out what they mean. If there’s any confusion, opening a support ticket with Microsoft will get you the answers you need.

The individual license features and services, also referred to as sub-SKU features, can also be inspected. As with the SkuPartNumber values, the ServicePlanName values are not a match for the friendly names that you see in the Office 365 or Azure admin portals, but names like SWAY, POWERAPPS_O365_P2, and EXCHANGE_S_ENTERPRISE are obvious. Others are not so obvious, such as MCOSTANDARD (Skype for Business Online), but again some searching online will usually clear up any confusion.

PS C:\> $licenses = Get-AzureADSubscribedSku

PS C:\> $licenses[0].SkuPartNumber
ENTERPRISEPACK

PS C:\> $licenses[0].ServicePlans

AppliesTo ProvisioningStatus ServicePlanId                        ServicePlanName
--------- ------------------ -------------                        ---------------
User      Success            8c7d2df8-86f0-4902-b2ed-a0458298f3b3 Deskless
User      Success            76846ad7-7776-4c40-a281-a386362dd1b9 FLOW_O365_P2
User      Success            c68f8d98-5534-41c8-bf36-22fa496fa792 POWERAPPS_O365_P2
User      Success            57ff2da0-773e-42df-b2af-ffb7a2317929 TEAMS1
User      Success            b737dad2-2f6c-4c65-90e3-ca563267e8b9 PROJECTWORKMANAGEMENT
User      Success            a23b959c-7ce8-4e57-9140-b90eb88a9e97 SWAY
Company   Success            882e1d05-acd1-4ccb-8708-6ee03664b117 INTUNE_O365
User      Success            7547a3fe-08ee-4ccb-b430-5077c5041653 YAMMER_ENTERPRISE
User      Success            bea4c11e-220a-4e6d-8eb8-8ea15d019f90 RMS_S_ENTERPRISE
User      Success            43de0ff5-c92c-492b-9116-175376d08c38 OFFICESUBSCRIPTION
User      Success            0feaeb32-d00e-4d66-bd5a-43b5b83db82c MCOSTANDARD
User      Success            e95bec33-7c88-4a70-8e19-b10bd9d0c014 SHAREPOINTWAC
User      Success            5dbe027f-2339-4123-9542-606e4d348a72 SHAREPOINTENTERPRISE
User      Success            efb87545-963c-4e0d-99df-69c6916d9eb0 EXCHANGE_S_ENTERPRISE


PS C:\> $licenses[1].SkuPartNumber
EMS

PS C:\> $licenses[1].ServicePlans

AppliesTo ProvisioningStatus ServicePlanId                        ServicePlanName
--------- ------------------ -------------                        ---------------
User      Success            6c57d4b6-3b23-47a5-9bc9-69f17b4947b3 RMS_S_PREMIUM
User      Success            c1ec4a95-1f05-45b3-a911-aa3fa01094f5 INTUNE_A
User      Success            bea4c11e-220a-4e6d-8eb8-8ea15d019f90 RMS_S_ENTERPRISE
User      Success            41781fb2-bc02-4b7c-bd55-b576c07bb09d AAD_PREMIUM
User      Success            8a256a2b-b617-496d-b51b-e76466e88db0 MFA_PREMIUM

Querying License Assignments for User Accounts

There are two user properties that reveal the license assignments for a user. The first is the AssignedLicenses property, which can be retrieved using Get-AzureADUser.

PS C:\> Get-AzureADUser -SearchString jane.tulley@exchangeserverpro.net | Select -ExpandProperty AssignedLicenses

DisabledPlans                          SkuId
-------------                          -----
{7547a3fe-08ee-4ccb-b430-5077c5041653} 6fd2c87f-b296-42f0-b197-1e91e994b900

In the output above we can see the SkuId of the license that is assigned to the user. The matching license can be found by running Get-AzureADSubscribedSku, which in the ouput below we can see is the ENTERPRISEPACK license (Enterprise E3).

PS C:\> Get-AzureADSubscribedSku | Where {$_.SkuId -eq "6fd2c87f-b296-42f0-b197-1e91e994b900"}

ObjectId                                                                  SkuPartNumber
--------                                                                  -------------
2b9bca49-687e-4e5f-8a52-21350b719b06_6fd2c87f-b296-42f0-b197-1e91e994b900 ENTERPRISEPACK

The Get-AzureADUser output shown above also reveals the DisabledPlans property. This property contains the ServicePlanId values of the sub-SKU features that have been disabled for the user. There are two ways to match those ServicePlanId values to the actual names of the sub-SKU features. The first is to use the output of Get-AzureADSubscribedSku to view the ServicePlanId values for the individual services, as demonstrated earlier. For the example of Jane Tulley, the ID of the disabled plan is “7547a3fe-08ee-4ccb-b430-5077c5041653”, which is YAMMER_ENTERPRISE in the list of service plans for the ENTERPRISEPACK license.

The other approach is to look at the AssignedPlans property of the user.

PS C:\> Get-AzureADUser -SearchString jane.tulley@exchangeserverpro.net | Select -ExpandProperty AssignedPlans

AssignedTimestamp     CapabilityStatus Service                       ServicePlanId
-----------------     ---------------- -------                       -------------
1/05/2017 11:01:50 AM Enabled          PowerAppsService              c68f8d98-5534-41c8-bf36-22fa496fa792
1/05/2017 11:01:50 AM Enabled          ProcessSimple                 76846ad7-7776-4c40-a281-a386362dd1b9
1/05/2017 11:01:50 AM Enabled          RMSOnline                     bea4c11e-220a-4e6d-8eb8-8ea15d019f90
1/05/2017 11:01:50 AM Enabled          Deskless                      8c7d2df8-86f0-4902-b2ed-a0458298f3b3
1/05/2017 11:01:50 AM Enabled          Sway                          a23b959c-7ce8-4e57-9140-b90eb88a9e97
27/02/2017 5:43:09 AM Enabled          TeamspaceAPI                  57ff2da0-773e-42df-b2af-ffb7a2317929
27/02/2017 5:40:56 AM Suspended        YammerEnterprise              7547a3fe-08ee-4ccb-b430-5077c5041653
19/01/2017 6:24:33 AM Enabled          exchange                      efb87545-963c-4e0d-99df-69c6916d9eb0
19/01/2017 6:24:33 AM Enabled          SharePoint                    5dbe027f-2339-4123-9542-606e4d348a72
19/01/2017 6:24:33 AM Enabled          SharePoint                    e95bec33-7c88-4a70-8e19-b10bd9d0c014
19/01/2017 6:24:33 AM Enabled          MicrosoftCommunicationsOnline 0feaeb32-d00e-4d66-bd5a-43b5b83db82c
19/01/2017 6:24:33 AM Enabled          MicrosoftOffice               43de0ff5-c92c-492b-9116-175376d08c38
19/01/2017 6:24:33 AM Enabled          ProjectWorkManagement         b737dad2-2f6c-4c65-90e3-ca563267e8b9

In the output above we can see that the “YammerEnterprise” service is suspended, and has a ServicePlanId matching the ID in the list of DisabledPlans we saw earlier.

You will also notice that the service names returned in the Get-AzureADUser output do always not match the service plan names returned in the Get-AzureADSubscribedSku output. For example, Get-AzureADUser shows a service name of “TeamspaceAPI” whereas Get-AzureADSubscribedSku shows the same service as “TEAMS1”. These differences are mildly irritating but do reinforce the idea that you should match two difference pieces of data by the ServicePlanId, not by the friendly name, whenever you are running PowerShell cmdlets or writing scripts to manage your licenses.

For administrators who are familiar with using the MS Online PowerShell module to manage licenses, there is one minor difference to be aware of. The Get-AzureADUser and Get-MsolUser cmdlets return slightly different information for the same user object. Get-AzureADUser will only return sub-SKU features that are Enabled, Deleted or Suspended, whereas Get-MsolUser will return the status of all sub-SKU features. Here’s an example, using an account where I’ve disabled several sub-SKU features to demonstrate the differences in cmdlet output.

PS C:\> Get-AzureADUser -SearchString aisha.bhari@exchangeserverpro.net | Select -ExpandProperty AssignedPlans

AssignedTimestamp     CapabilityStatus Service               ServicePlanId
-----------------     ---------------- -------               -------------
1/05/2017 11:12:19 AM Enabled          SharePoint            5dbe027f-2339-4123-9542-606e4d348a72
1/05/2017 11:12:19 AM Enabled          SharePoint            e95bec33-7c88-4a70-8e19-b10bd9d0c014
1/05/2017 11:12:19 AM Enabled          MicrosoftOffice       43de0ff5-c92c-492b-9116-175376d08c38
1/05/2017 11:12:19 AM Enabled          ProjectWorkManagement b737dad2-2f6c-4c65-90e3-ca563267e8b9
1/05/2017 11:12:19 AM Enabled          TeamspaceAPI          57ff2da0-773e-42df-b2af-ffb7a2317929
1/05/2017 11:12:19 AM Enabled          PowerAppsService      c68f8d98-5534-41c8-bf36-22fa496fa792
1/05/2017 11:12:19 AM Enabled          ProcessSimple         76846ad7-7776-4c40-a281-a386362dd1b9

PS C:\> (Get-MsolUser -UserPrincipalName aisha.bhari@exchangeserverpro.net).Licenses[0].ServiceStatus

ServicePlan           ProvisioningStatus
-----------           ------------------
Deskless              Disabled
FLOW_O365_P2          Success
POWERAPPS_O365_P2     Success
TEAMS1                Success
PROJECTWORKMANAGEMENT Success
SWAY                  Disabled
INTUNE_O365           Success
YAMMER_ENTERPRISE     Disabled
RMS_S_ENTERPRISE      Disabled
OFFICESUBSCRIPTION    Success
MCOSTANDARD           Disabled
SHAREPOINTWAC         Success
SHAREPOINTENTERPRISE  Success
EXCHANGE_S_ENTERPRISE Disabled

Assigning a Single License Using PowerShell

The Set-AzureADUserLicense cmdlet assigns and removes Office 365 licenses to user accounts. A simple example is assigning a license to a new user account. Before assigning the license, the usage location of the account also needs to be configure. The license assignment process involves a series of steps to:

  1. Create an assigned license (singular) object
  2. Add the assigned license object to another object representing the assigned licenses (plural)
  3. Run Set-AzureADUserLicense and provide the value for the assigned licenses (plural)

To perform those steps we need to know the ObjectId of the user account, and the SkuId of the license.

PS C:\> Get-AzureADSubscribedSku | Select Sku*

SkuId                                SkuPartNumber
-----                                -------------
6fd2c87f-b296-42f0-b197-1e91e994b900 ENTERPRISEPACK
efccb6f7-5641-4e0e-bd10-b4976e1bf68e EMS


PS C:\> $User = Get-AzureADUser -SearchString sharon.butler@exchangeserverpro.net
PS C:\> $user

ObjectId                             DisplayName   UserPrincipalName                   UserType
--------                             -----------   -----------------                   --------
9930a287-640e-4670-a2ae-2ba3eb5fca33 Sharon Butler Sharon.Butler@exchangeserverpro.net Member

Now we can go ahead and create the license assignment.

PS C:\> Set-AzureADUser -ObjectId $User.ObjectId -UsageLocation AU

PS C:\> $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense

PS C:\> $License.SkuId = "6fd2c87f-b296-42f0-b197-1e91e994b900"

PS C:\> $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses

PS C:\> $LicensesToAssign.AddLicenses = $License

PS C:\> Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $LicensesToAssign

The user now has a single license SKU assigned, and the plans for that license are enabled.

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedLicenses

DisabledPlans SkuId
------------- -----
{}            6fd2c87f-b296-42f0-b197-1e91e994b900

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedPlans

AssignedTimestamp    CapabilityStatus Service                       ServicePlanId
-----------------    ---------------- -------                       -------------
2/05/2017 2:41:26 AM Enabled          TeamspaceAPI                  57ff2da0-773e-42df-b2af-ffb7a2317929
2/05/2017 2:41:26 AM Enabled          MicrosoftCommunicationsOnline 0feaeb32-d00e-4d66-bd5a-43b5b83db82c
2/05/2017 2:41:26 AM Enabled          PowerAppsService              c68f8d98-5534-41c8-bf36-22fa496fa792
2/05/2017 2:41:26 AM Enabled          ProcessSimple                 76846ad7-7776-4c40-a281-a386362dd1b9
2/05/2017 2:41:26 AM Enabled          SharePoint                    e95bec33-7c88-4a70-8e19-b10bd9d0c014
2/05/2017 2:41:26 AM Enabled          ProjectWorkManagement         b737dad2-2f6c-4c65-90e3-ca563267e8b9
2/05/2017 2:41:26 AM Enabled          RMSOnline                     bea4c11e-220a-4e6d-8eb8-8ea15d019f90
2/05/2017 2:41:26 AM Enabled          SharePoint                    5dbe027f-2339-4123-9542-606e4d348a72
2/05/2017 2:41:26 AM Enabled          YammerEnterprise              7547a3fe-08ee-4ccb-b430-5077c5041653
2/05/2017 2:41:26 AM Enabled          Deskless                      8c7d2df8-86f0-4902-b2ed-a0458298f3b3
2/05/2017 2:41:26 AM Enabled          MicrosoftOffice               43de0ff5-c92c-492b-9116-175376d08c38
2/05/2017 2:41:26 AM Enabled          Sway                          a23b959c-7ce8-4e57-9140-b90eb88a9e97
2/05/2017 2:41:26 AM Enabled          exchange                      efb87545-963c-4e0d-99df-69c6916d9eb0

Assigning Multiple Licenses Using PowerShell

Using the same steps as above you can assign an additional license to a user. For example, if we wanted to add the EMS license to the user who is already licensed for Enterprise E3, we could simply re-run the steps above using the SkuId for the EMS license.

If you would like to assign multiple licenses at the same time you can do so with just a small modification to the process. Remember, we’re creating an assigned license (singular) object, and adding it to an assigned licenses (plural) object. We can add multiple assigned license (singular) objects by repeating those commands. Here’s an example of licensing a user for Enterprise E3 and EMS at the same time.

PS C:\> $User = Get-AzureAdUser -SearchString vik.kirby@exchangeserverpro.net

PS C:\> Set-AzureADUser -ObjectId $User.ObjectId -UsageLocation AU

PS C:\> $E3License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
PS C:\> $EMSLicense = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense

PS C:\> $E3License.SkuId = "6fd2c87f-b296-42f0-b197-1e91e994b900"
PS C:\> $EMSLicense.SkuId = "efccb6f7-5641-4e0e-bd10-b4976e1bf68e"

PS C:\> $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses

PS C:\> $LicensesToAssign.AddLicenses = $E3License,$EMSLicense

PS C:\> Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $LicensesToAssign

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedPlans

The user now has multiple license SKUs assigned, and the services for both of those SKUs are enabled.

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedLicenses

DisabledPlans SkuId
------------- -----
{}            efccb6f7-5641-4e0e-bd10-b4976e1bf68e
{}            6fd2c87f-b296-42f0-b197-1e91e994b900

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedPlans

AssignedTimestamp    CapabilityStatus Service                       ServicePlanId
-----------------    ---------------- -------                       -------------
2/05/2017 2:39:08 AM Enabled          TeamspaceAPI                  57ff2da0-773e-42df-b2af-ffb7a2317929
2/05/2017 2:39:08 AM Enabled          MicrosoftCommunicationsOnline 0feaeb32-d00e-4d66-bd5a-43b5b83db82c
2/05/2017 2:39:08 AM Enabled          PowerAppsService              c68f8d98-5534-41c8-bf36-22fa496fa792
2/05/2017 2:39:08 AM Enabled          AADPremiumService             41781fb2-bc02-4b7c-bd55-b576c07bb09d
2/05/2017 2:39:08 AM Enabled          ProcessSimple                 76846ad7-7776-4c40-a281-a386362dd1b9
2/05/2017 2:39:08 AM Enabled          SharePoint                    e95bec33-7c88-4a70-8e19-b10bd9d0c014
2/05/2017 2:39:08 AM Enabled          ProjectWorkManagement         b737dad2-2f6c-4c65-90e3-ca563267e8b9
2/05/2017 2:39:08 AM Enabled          RMSOnline                     bea4c11e-220a-4e6d-8eb8-8ea15d019f90
2/05/2017 2:39:08 AM Enabled          RMSOnline                     6c57d4b6-3b23-47a5-9bc9-69f17b4947b3
2/05/2017 2:39:08 AM Enabled          SharePoint                    5dbe027f-2339-4123-9542-606e4d348a72
2/05/2017 2:39:08 AM Enabled          YammerEnterprise              7547a3fe-08ee-4ccb-b430-5077c5041653
2/05/2017 2:39:08 AM Enabled          Deskless                      8c7d2df8-86f0-4902-b2ed-a0458298f3b3
2/05/2017 2:39:08 AM Enabled          MultiFactorService            8a256a2b-b617-496d-b51b-e76466e88db0
2/05/2017 2:39:08 AM Enabled          MicrosoftOffice               43de0ff5-c92c-492b-9116-175376d08c38
2/05/2017 2:39:08 AM Enabled          Sway                          a23b959c-7ce8-4e57-9140-b90eb88a9e97
2/05/2017 2:39:08 AM Enabled          SCO                           c1ec4a95-1f05-45b3-a911-aa3fa01094f5
2/05/2017 2:39:08 AM Enabled          exchange                      efb87545-963c-4e0d-99df-69c6916d9eb0

In the output above you might notice that Intune, which is included with the EMS license, is not listed as a service. This is another example of how Get-AzureADUser doesn’t show services that are not in an enabled, suspended, or deleted state. Intune requires activation for the user, and is in a “PendingInput” state when the license is initially assigned to the user as you can see in the Get-MsolUser output below.

PS C:\> Get-MsolUser -UserPrincipalName vik.kirby@exchangeserverpro.net | Select -ExpandProperty Licenses | Select -Expa
ndProperty ServiceStatus

ServicePlan           ProvisioningStatus
-----------           ------------------
RMS_S_PREMIUM         Success
INTUNE_A              PendingInput
RMS_S_ENTERPRISE      Success
AAD_PREMIUM           Success
MFA_PREMIUM           Success
...

Assigning Licenses with Sub-SKU Features Disabled

Office 365 licenses such as Enterprise E3 and E5 allow access to multiple services and applications. For some organizations it is required to disable some of the features of a license, either because the feature should not be used in that environment, or simply to manage a staged roll out and adoption of Office 365 features.

To assign a license with sub-SKU features disabled we use the same process demonstrated earlier, but this time we need to configure the license object with enable and disabled plans before applying the license to the user. For this example I’ll assign an E3 license but only enabled the Exchange Online mailbox and the Office 365 ProPlus applications.

PS C:\> $User = Get-AzureADUser -SearchString blake.johnson@exchangeserverpro.net

PS C:\> Set-AzureADUser -ObjectId $User.ObjectId -UsageLocation AU

PS C:\> $SkuFeaturesToEnable = @("EXCHANGE_S_ENTERPRISE","OFFICESUBSCRIPTION")

PS C:\> $StandardLicense = Get-AzureADSubscribedSku | Where {$_.SkuId -eq "6fd2c87f-b296-42f0-b197-1e91e994b900"}

PS C:\> $SkuFeaturesToDisable = $StandardLicense.ServicePlans | ForEach-Object { $_ | Where {$_.ServicePlanName -notin $SkuFeaturesToEnable }}

PS C:\> $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense

PS C:\> $License.SkuId = $StandardLicense.SkuId

PS C:\> $License.DisabledPlans = $SkuFeaturesToDisable.ServicePlanId

PS C:\> $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses

PS C:\> $LicensesToAssign.AddLicenses = $License

PS C:\> Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $LicensesToAssign

The user now has the license SKU assigned, but with multiple disabled plans. Only the two enabled features are showing as enabled services for the user as well.

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedLicenses | fl


DisabledPlans : {8c7d2df8-86f0-4902-b2ed-a0458298f3b3, 76846ad7-7776-4c40-a281-a386362dd1b9,
                c68f8d98-5534-41c8-bf36-22fa496fa792, 57ff2da0-773e-42df-b2af-ffb7a2317929...}
SkuId         : 6fd2c87f-b296-42f0-b197-1e91e994b900

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedPlans

AssignedTimestamp    CapabilityStatus Service         ServicePlanId
-----------------    ---------------- -------         -------------
2/05/2017 2:58:15 AM Enabled          MicrosoftOffice 43de0ff5-c92c-492b-9116-175376d08c38
2/05/2017 2:58:15 AM Enabled          exchange        efb87545-963c-4e0d-99df-69c6916d9eb0

Removing Licenses Using PowerShell

For the final demonstration in this article let’s look at how to remove an assigned license using PowerShell. The Set-AzureADUserLicense cmdlet is used for this task, and the process is similar to adding a license. The difference is that when creating the assigned licenses (plural) object we use RemoveLicenses instead of AddLicenses, and provide only the SkuId instead of the full license object.

PS C:\> $User = Get-AzureAdUser -SearchString sue.cooper@exchangeserverpro.net 

PS C:\> $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense

PS C:\> $License.SkuId = "6fd2c87f-b296-42f0-b197-1e91e994b900"

PS C:\> $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses

PS C:\> $LicensesToAssign.AddLicenses = @()
PS C:\> $LicensesToAssign.RemoveLicenses = $License.SkuId

PS C:\> Set-AzureADUserLicense -ObjectId $User.ObjectId -AssignedLicenses $LicensesToAssign

The user has now had each of the previously licensed features marked as deleted due to the license being removed.

PS C:\> Get-AzureADUser -ObjectId $User.ObjectId | Select -ExpandProperty AssignedPlans

AssignedTimestamp    CapabilityStatus Service                       ServicePlanId
-----------------    ---------------- -------                       -------------
2/05/2017 3:08:16 AM Deleted          TeamspaceAPI                  57ff2da0-773e-42df-b2af-ffb7a2317929
2/05/2017 3:08:16 AM Deleted          MicrosoftCommunicationsOnline 0feaeb32-d00e-4d66-bd5a-43b5b83db82c
2/05/2017 3:08:16 AM Deleted          PowerAppsService              c68f8d98-5534-41c8-bf36-22fa496fa792
2/05/2017 3:08:16 AM Deleted          ProcessSimple                 76846ad7-7776-4c40-a281-a386362dd1b9
2/05/2017 3:08:16 AM Deleted          SharePoint                    e95bec33-7c88-4a70-8e19-b10bd9d0c014
2/05/2017 3:08:16 AM Deleted          ProjectWorkManagement         b737dad2-2f6c-4c65-90e3-ca563267e8b9
2/05/2017 3:08:16 AM Deleted          RMSOnline                     bea4c11e-220a-4e6d-8eb8-8ea15d019f90
2/05/2017 3:08:16 AM Deleted          SharePoint                    5dbe027f-2339-4123-9542-606e4d348a72
2/05/2017 3:08:16 AM Deleted          YammerEnterprise              7547a3fe-08ee-4ccb-b430-5077c5041653
2/05/2017 3:08:16 AM Deleted          Deskless                      8c7d2df8-86f0-4902-b2ed-a0458298f3b3
2/05/2017 3:08:16 AM Deleted          MicrosoftOffice               43de0ff5-c92c-492b-9116-175376d08c38
2/05/2017 3:08:16 AM Deleted          Sway                          a23b959c-7ce8-4e57-9140-b90eb88a9e97
2/05/2017 3:08:16 AM Deleted          exchange                      efb87545-963c-4e0d-99df-69c6916d9eb0

Summary

As you can see, managing Office 365 licenses with the Azure AD V2 PowerShell module is a complex task at first, but once you’ve performed the steps a few times it should become much more comfortable. Azure AD group-based license management is simpler, but won’t fit everyone’s needs. Using PowerShell to manage licenses like this will suit organizations who want to automated license assignments into other processes. The use of the Graph API also means you can ignore the Azure AD module itself and write custom code to interact with the REST API to perform the same tasks. That is out of scope of this blog post, but it’s something you can explore if custom development and integration into third party systems is a requirement for you.

About the Author

Paul Cunningham

Paul is a former Microsoft MVP for Office Apps and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul no longer writes for Practical365.com.

Comments

  1. Rita Wilson

    Nice post. For Azure consulting services visit TechTriad

  2. DanW

    For years I have been running a detailed report of User licensing built from 3 commands:
    Get-MsolSubscription
    Get-MsolAccountSku
    Get-MsolUser -ALL
    The report splices out which part of the business owns which subscription and what users are using what licensing.

    Now I need to use AzureAD or mgGraph modules and they each hae a command to view the licenses and sku’s
    but not to see the individual subscriptions.

    Do you know a replacement for the Get-MsolSubscription command??
    Thanks

    1. Thomas Schittli

      This Microsoft “Docs” URL *would* be nice, but in fact, it’s once more just a shame. Microsoft writes:

      “These tables are for reference purposes and are accurate only as of the date when this article was last updated. Microsoft does not plan to update them for newly added services periodically.”

      Obviously, Microsoft doesn’t value their customers that much to keep “Docs” up to date… or at least provide a service that always offers the latest license information with all this mess: String_IDs, SKUs, GUIDs, etc. etc.

      But it’s actually much worse:
      Microsoft cheats the many millions of customers by charging licenses multiple times.

      Of course I can prove this very easily 🙂

  3. Aruna Chinnamuthu

    Hi ,

    Can you pls brief on the warning key in the Prepaid Units.

  4. Muhamed

    Hi Paul,

    Great article. How can I get the report for all the o365 licenses assigned to a list of users ?

    For eg: My userlist file is C:\UserNamesInput.txt

  5. Aleksej Kusnir

    Hello Paul, thank you very much for th clear exmaples.

    Very usefull.

    Best regards.
    Aleksej

  6. Saurabh Jain

    It covers all most everything in Azure AD user Licensing, Very helpful article.

    Thanks for Sharing.

  7. Rich

    Thank you for this article and write-up. Removing a license(s) from users individually or in bulk seems straightforward.

    Say we have users leave the org. Accounts and licenses are removed from the user, but the licenses remain in O365 as unassigned and costing money. What I am looking for is the powershell method of deprovisioning unassigned licenses in O365 down to the assigned number. Why pay for licenses we’re not using?

  8. Jason

    How do we distinguish between a CSP License and an EA License?

  9. Andre Schoeman

    I’m trying to construct a report that will show me all my Azure users and what licenses they have assigned.

    For Example:
    Each User is assigned “Office 365 Enterprise E3” but then inside of “Office 365 Enterprise E3” you have 18 services that can be enable/disabled.

    The Ideal would be to create a report on each Product (Office 365 Enterprise E3) then cycle through all the users showing what each user has enabled.

    Has anyone got a PowerShell script that could help with this?

  10. Riccardo

    VERY HELPFUL
    In my ignorance, I opened a ticket to Microsoft support and they are not able to give me such detailed answers…
    By chance I happend to get to this mistery of licenses assignements dates I was looking for…
    Thanks to you and Internet
    Riccardo

  11. Barrie Sharp

    I’m trying to remove Yammer from all accounts with a mixture of licenses. I have tried to digest your samples but I’m still unsure how to disable specific Sub Features on an existing license. Is this possible or does the script have to reapply the license with Yammer switched off?

  12. Juliano Cercal

    First of all thank you for article.

    I would like to get information about users + licensing, I did this code:
    Get-AzureADUser | Select DisplayName,mail,Department,CompanyName,UsageLocation,AssignedLicenses -ExpandProperty AssignedLicenses | Where {$_.SkuId -eq “6fd2c87f-b296-42f0-b197-1e91e994b900” -or $_.SkuId -eq “4b585984-651b-448a-9e53-3b10f069cf7f”} | ft -a

    Could I show the SkuId name in result?
    Like:
    DisplayName | Mail | Department | CompanyName | UsageLocation | AssignedLicenses

  13. Oğulcan Özügenç

    Hey hello and thanks for your article.

    How can I get all the users with specific service like “MicrosoftOffice”? Thanks for your help!

    I mean this “Get-AzureADUser -SearchString test@test.com | Select -ExpandProperty AssignedPlans”

  14. Anup P.

    Hi Team,

    Can anybody know how to check the date when license is assigned, who assign license and to whom with Office 365 Powershell. is it possible ?

    Thanks in advance.

  15. sai

    thanks for the article. i am looking for a way to Enabled individual ServicePlan for existing users. we use E3 license with only have a few service plans enabled, now we want to enable more plans, such as “Team”. How can we do that without removing the whole license and re-add.

    1. Radek

      I am also wondering about it. How can we do it in a more friendly way?

  16. Vishal Saini

    Very helpful article

  17. Mike Campbell

    Hi Paul, have you noticed, or is it just me, that the Get-AzureADSubscribedSku now no longer returns the ServicePlans objects with each Sku object but instead returns a string that looks like the following:

    class ServicePlanInfo {
    AppliesTo: User
    ProvisioningStatus: Success
    ServicePlanId: efb87545-963c-4e0d-99df-69c6916d9eb0
    ServicePlanName: EXCHANGE_S_ENTERPRISE
    }

    Just wanting to do a sanity check before I start building some code to parse those strings for the ServicePlanId and ServicePlanName. Looks like this is YAML output, essentially, except for the class declaration.

    1. Mike Campbell

      You can ignore the above – the AzureAD commands were being used in my case imported from another PSSession. The serialization must be causing the effect.

  18. James

    Hello Thank you very much for your article. I was wondering. If i have many users, and want to add a licence for all this users? How can i do it? Do i need a csv file or muss write my code in a for each loop? Please can you help me or give me more informations?
    Thank you

    1. Avatar photo
      Paul Cunningham

      Yes, some PowerShell code to loop through a list of users would do the trick.

      Or switch to using group-based license management.

  19. RP

    “AdHoc License Administrator” role has been deprecated and “User administrator” role is required at the minimum for managing the licenses.

    “User administrator” role gives more access than what I would like to give to the account running the provisioning scripts. Have you found a method to delegate only “License management” permissions to the account running the provisioning scripts.

  20. Chris S

    Using the get-azureaduser and get-azureaduserlicensedetail I can easily see what license are assigned. Is there a way to determine if the license is directly applied, or applied using Azure AD group based licenses? I found a way using the MSOL module, but not the AzureAD module.

    1. TC

      I have a similar question about cmdlets and methods for viewing licenses assigned using security groups. I have the methods that exist today in the MSOnline module will have counterparts in AzureAd module. This is hoping that that v2 supports more features — not less.

  21. maxcoder88

    I want to remove multiple licenses using PowerShell. Actually I able to remove single licence like below. But How do I remove multiple licenses?

    $AzureUser = Get-AzureAdUser -ObjectId $ADuser.UserPrincipalName

    $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense

    $License.SkuId = “6fd2c87f-b296-42f0-b197-1e91e994b900” #6fd2c87f-b296-42f0-b197-1e91e994b900 Office 365 E3

    $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses

    $LicensesToAssign.AddLicenses = @()
    $LicensesToAssign.RemoveLicenses = $License.SkuId

    Set-AzureADUserLicense -ObjectId $AzureUser.ObjectId -AssignedLicenses $LicensesToAssign

    1. Thomas

      Had this question too, and I figured it out. The logic for this seems pretty silly, and the logic is beyond me, but you need to:
      – Create an object for each license SkuId
      – Add the correct license to the objects made in previous step
      – Create the object with the licenses (plural)
      – Add the previously created variables to another object (I think)
      – Create the array
      – Create the variable/property to remove the licenses
      – Create the user variable
      – Then remove the licenses

      So the code would look like this:
      #Hard code the username or prompt for username
      $ADName = myuserprincipalname / $ADName = Read-host

      $E3License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
      $EMSLicense = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
      $E3License.SkuId = “6fd2c87f-b296-42f0-b197-1e91e994b900”
      $EMSLicense.SkuId = “efccb6f7-5641-4e0e-bd10-b4976e1bf68e”
      $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
      $LicensesToAssign.AddLicenses = $E3License, $EMSLicense
      $LicensesToAssign.AddLicenses = @()
      $LicensesToAssign.RemoveLicenses = $E3License.SkuId, $EMSLicense.SkuId
      $user = Get-AzureADUser -SearchString “$adname”
      Set-AzureADUserLicense -ObjectId $user.ObjectId -AssignedLicenses $LicensesToAssign

  22. Steve Rackham

    What if you want to enable previously disabled plans?
    Would this cause disruption? Will it pick up the add or error as the license is already assigned?

    1. Avatar photo
      Paul Cunningham

      I don’t understand your question. What is the scenario you’re looking at?

      1. Mark

        I’m in the same boat Paul.

        Two Scenarios I’d like to try and work through:

        1) All users were globally disabled Teams (when that was a thing, which it isn’t now), and we want to add that SubSKU (Teams) back in for all enabled / licensed Users.

        2) When we piloted Teams, a group of 20 users were given the Teams SubSKU to allow them to evaluate Teams. If 1), above, works, what will happen to those 20 users. Would the script need to iterate through and skip them if they were already enabled for Teams, or would it just enable it again and continue.

        TIA

      2. John

        Hi Paul

        I also have the same question as everyone else. Can we re-apply a license with additional features enabled or disabled. It seems like that is not the case.

        Is the only way to add and remove features to remove the full license then re apply with desired features?

        Thank you

  23. Matthew Silcox

    This probably won’t help you, but maybe it will help someone else who reads your blog. Your bit on removing licenses was helpful; however, we have a ton of SKUs. There’s no way my Junior Admins or I will remember the SkuIDs for these…so I needed a way to easily remove licenses from a user account without having that information. I wrote this module:

    function global:Remove-CompanyUserLicense ($delicenseUID)

    {

    Try

    {

    $delicenseEmail = $delicenseUID + ‘@company.com’
    $Current = Get-AzureADUser -ObjectId $delicenseEmail | Select -ExpandProperty AssignedLicenses
    $License = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicense
    $License.SkuId = $Current.SkuId
    $LicensesToAssign = New-Object -TypeName Microsoft.Open.AzureAD.Model.AssignedLicenses
    $LicensesToAssign.AddLicenses = @()
    $LicensesToAssign.RemoveLicenses = $License.SkuId

    Set-AzureADUserLicense -ObjectId $delicenseEmail -AssignedLicenses $LicensesToAssign

    Write-Host -ForegroundColor Cyan “License has been removed on $delicenseEmail.”

    }

    Catch

    {

    Write-Output “Error clearing license.”

    }

    }

    I’m sure there is a more efficient way to do this, as my Powershell is a bit green. At any rate, I thought it might help save someone some time. Great blog, by the way!

  24. Steve Rackham

    Hi Paul
    Great article…
    Trying to make reporting on a user readable for humans without memorising each SkuId. Has anyone managed a script that gives
    The License SkuPartNumber assigned
    The assigned License ServicePlanName
    Preferably parsable as an object for reporting…
    This is my output and its terrible…

    Status :
    ObjectID : 0e2afe5b-22d9-442f-9ee6-b66c79b5071f
    UserPrincipalName : tilda.backslash@domain.com
    UsageLocation : NZ
    AssignedLicenses : {class AssignedLicense {
    DisabledPlans: System.Collections.Generic.List`1[System.String]
    SkuId: efccb6f7-5641-4e0e-bd10-b4976e1bf68e
    }
    , class AssignedLicense {
    DisabledPlans: System.Collections.Generic.List`1[System.String]
    SkuId: 6fd2c87f-b296-42f0-b197-1e91e994b900
    }
    }
    AssignedPlans : {@{Service=ProjectWorkManagement}, @{Service=MicrosoftOffice}, @{Service=YammerEnterprise}, @{Service=Sway}…}

  25. turbomcp

    very nice article
    thanks

Leave a Reply