Skip to the main content.

Licensing with Microsoft Graph PowerShell

Table of contents

 

 

Post Featured Image

The Microsoft Graph SDK PowerShell module is replacing two other modules. Learn more about connecting to Graph, finding licenses, making changes and removing licenses.

For the longest time, the AzureAD and MSOnline PowerShell modules have been around and available for managing users, groups, licenses and more in a Microsoft 365 tenant. All of that is changing. We're past Microsoft's a deadline of June 2023 for ending these two PowerShell modules and deprecating them completely. Their replacement? Microsoft Graph SDK PowerShell module. Read about upgrading from AzureAD to Microsoft Graph here. This article is all about licenses and Graph.

 

Connecting to Graph

Permissions

First, we need to make a connection to the Microsoft Graph API and in order to do so, we need to get the correct permissions to make these changes. Graph has an intricate set of permissions that allow for a granular approach to security and security assets in a tenant. We can get a list of permissions using this PowerShell cmdlet without connecting to Graph first: 


Find-MgGraphPermission -PermissionType Any

01_truncated list of permissions

Truncated list of permissions

While this provides a good list of permissions, it is not sufficient to reveal what permission is needed to manage licenses for a tenant. Where can we look to find these? With a bit of researching, we can find this documentation on Graph API Permissions required for certain actions: Microsoft | Learn.

Searching the page, we find this reference to licensing:

02_permissions required to manage licensing in MS Graph PowerShell

Permissions required to manage licensing in Microsoft Graph PowerShell

This is important, because when we connect to Graph, we need a scope of permissions that allows PowerShell to manipulate objects with the correct rights.

Connection string

The cmdlet used to connect to Microsoft Graph is either Connect-Graph or Connect-MgGraph. Why either? One is an Alias and the other an actual cmdlet:

03_connection PowerShell cmdlets

Choice of connection PowerShell cmdlets

Let us use Connect-Graph. Beyond permissions, we have a couple of other options for the connection.

  • TenandID: Specify the Tenant's GUID so that PowerShell connects to the intended tenant's Graph connection point.
  • UseDeviceAuthentication: 'Use device code authentication instead of a browser control' (optional).
  • Scopes: Specify permissions to use during the connect, in our case 'Directory.ReadWRite.All'.
Connect-Graph -Scopes 'Directory.ReadWRite.All' -TenantID <your tenant's GUID>

You may be prompted for a login account and/or need to grant permissions if this is your first connection attempt.

Reminder: Remember to close your connection to Graph so that you do not run out of active connections (usually three) for resources in Microsoft 365.
 

04_disconnect PowerShell cmdlets

Two Disconnect PowerShell cmdlets as well

 

Finding licenses

We can get a list of available licenses by first running this cmdlet to give us a list of available plans:

Get-MgSubscribedSku | fl

05_available SKU in a tenant

Available SKU in a tenant

Then running this cmdlet, we get a list of individual licensed features available to enable / disable for users:

(Get-MgSubscribedSku -SubscribedSkuId 5d0cc54e-0082-4eb8-a300-ce17a036f3f4_6fd2c87f-b296-42f0-b197-1e91e994b900).ServicePlans

06_truncated feature list

Truncated feature list

From here we can proceed. Now we can look up the ServicePlanId’s for license management.

 

 

Making changes

Licensing concepts

Before we assign a license to users or groups, we need to cover a few concepts. Microsoft has a lot of license types and Groupings (E3 / E5 for example) and we can use these as the basis for our assignments. However, there are times when an organization may not want to assign all of these licenses as they have not trained users, had other solutions or had no general interest in the feature included in their licensing plans. 

  • Group Based Licensing: Assigning licenses to users who are members of a certain group by setting licensing assignments on that group.
  • Overall SKU: This SKU covers a higher-level license group like E3 or E5 and is made up of a group of features available in Microsoft 365.
  • Single SKU: A SKU is just for one feature, known as the 'ServicePlanId' property.
  • Disabled SKUs: List of features that are not available to a user either assigned directly or via group-based licensing.

 

Service Plans

These are the Office 365 / Microsoft 365 E1, E3 and E5 license groups. Below are their SKU Ids which we will need later.

  • Office 365 E1: 18181a46-0d4e-45cd-891e-60aabd171b4e
  • Office 365 E3: 6fd2c87f-b296-42f0-b197-1e91e994b900
  • Office 365 E5: c7df2760-2c81-4ef7-b578-5b5392b571df
  • Microsoft 365 E3: 05e9a617-0261-4cee-bb44-138d3ef5d965
  • Microsoft 365 E5: 06ebc4ee-1bb5-47dd-8120-11324bc54e06

Source: https://learn.microsoft.com/en-us/azure/active-directory/enterprise-users/licensing-service-plan-reference
 

Individual Licenses

First place we can start is individual licenses as these are usually one-offs that an organization may need to perform for an onboarding process or a new role being assigned to a user. Assignments like this do not need as much planning as they affect a singular user and can be done quite easily with PowerShell. In the following two examples we apply licenses to users individually and from a CSV file list containing user identification information. 

Example 1

We have some users that only need access to most features, but we need to disable Flow, PowerApps, Viva Engage (formerly Yammer) and Bookings. Lester and Nancy are interns who do not need those features and we can change their licensing like so:
 


$YammerID = '7547a3fe-08ee-4ccb-b430-5077c5041653'
$FlowID = '07699545-9485-468e-95b6-2fca3738be01'
$PowerAppsID = '9c0dab89-a30c-4117-86e7-97bda240acd2'
$BookingsID = '199a5c09-e0ca-4e37-8f7c-b05d533e1ea2'
$DisabledPlans = @( $YammerID, $FlowID, $PowerAppsID, $BookingsID )
$SKU = 'c7df2760-2c81-4ef7-b578-5b5392b571df'
Set-MgUserLicense -UserID Lester@mmcug.com -AddLicenses @{SkuId = $SKU ; DisabledPlans = $DisabledPlans} -RemoveLicenses @()
Set-MgUserLicense -UserID Nancy@mmcug.com -AddLicenses @{SkuId = $SKU ; DisabledPlans = $DisabledPlans} -RemoveLicenses @()
 

Now Lester and Nancy have three disabled licenses in their E5 plan online.

Example 2

A set of users, that we have listed in a CSV, need to have the full E5 license assigned as they need the extra features it provides. Similar to the previous example of using Set-MgUserLicense, we will use a Foreach loop to process each user and assign the SKU for Office 365 E5.


$SKU = 'c7df2760-2c81-4ef7-b578-5b5392b571df'
$CSV = Import-CSV 'E5-USerList.csv'
Foreach ($Line in $CSV)
Set-MgUserLicense -UserID $_.UPN -AddLicenses @{SkuId = $SKU} -RemoveLicenses @()
}
 

Depending on the number of users, this may take some time to process. A better method for a large change like this would be to use Group Based Licensing, which we will cover next:

 

Group Licenses

Beyond assigning licenses to each user on an individual basis, we have the option to mass assign licenses via Entra ID (formerly Azure AD) groups, also known as Group Based Licensing. Simply put, using groups to assign licenses we can transition a user from one license to another in a simpler fashion by adding them to a Group that has a license assignment associated with it. When a user no longer needs the license, they can be removed from the same group and the license is then removed.
 

Example

In this scenario, IT is slowly rolling out various features that were included with the Office E3 licenses that was purchased. An IT project is underway where Teams, Viva Engage (formerly Yammer) and Sway are being deployed in waves to departments / sections of staff as they are trained. As such, they are looking to block these three features initially for all but then assign them as the groups are trained and internal IT communicates access to these products / rollout clients (Teams for example). Using the previous ServicePlanId's for the product we can create a group to block using a concept of Disabled Plans. In the below PowerShell we put together the three products we wish to block and assemble them in an array: 


$TeamsID = '57ff2da0-773e-42df-b2af-ffb7a2317929'
$YammerID =  '7547a3fe-08ee-4ccb-b430-5077c5041653'
$SwayID = 'a23b959c-7ce8-4e57-9140-b90eb88a9e97'
$DisabledPlans = @( $TeamsID, $YammerID, $SwayID )
 

Next, we've created two groups which we will assign licenses to and we can gather information on these groups via PowerShell:

Get-MgGroup | Where DisplayName -like 'M365*'

 

Which returns this:


Id
                                                DisplayName
--                                                  -----------
06170e2a-8017-45f9-a0a5-27cb4a8781ce                M365E3-PostTraining
64a84c9f-eb10-422c-9333-c58bd5b45419                M365E3-PreTraining

 

The Id property is what we will need to assign licenses to the two groups. For the Pre-training group, we need the $DisabledPlans, the Group's ID value and the Office 365 E3 SKU from above and supply them in the Set-MgGroupLicense cmdlet as shown here: 


$GroupID = (Get-MgGroup | Where DisplayName -eq 'M365E3-PreTraining').Id
$SKU = 'c7df2760-2c81-4ef7-b578-5b5392b571df'
Set-MgGroupLicense -GroupID $GroupID  -AddLicenses @{SkuId = $SKU ; DisabledPlans = $DisabledPlans} -RemoveLicenses @()

 

Now, for the Post-training group, we only need the Office 365 E3 SKU and the Group's ID for the same cmdlet as shown here:

$GroupID = (Get-MgGroup | Where DisplayName -eq 'M365E3-PostTraining').Id
$SKU = 'c7df2760-2c81-4ef7-b578-5b5392b571df'
Set-MgGroupLicense -GroupID $GroupID  -AddLicenses @{SkuId = $SKU} -RemoveLicenses @()
 

 

If we look in the Microsoft 365 Admin Center, we can see the difference:

 07_license changes with or without PowerShell script

License changes pre / post PowerShell script

Also, we can confirm the differences in PowerShell: 

Get-MgUserLicenseDetail -UserId Frank@domain.com | select -ExpandProperty ServicePlans | where ProvisioningStatus -eq Disabled

08_disabled licensed features

Disabled licensed features on Frank’s account

 

Get-MgUserLicenseDetail -UserId Brian@domain.com | select -ExpandProperty ServicePlans | where ProvisioningStatus -eq Disabled

This returns an empty result which means that none are disabled. To get a total of all active services, we can run these two cmdlets:


(Get-MgUserLicenseDetail -UserId Frank@domain.com | select -ExpandProperty ServicePlans | where ProvisioningStatus -ne Disabled).Count
(Get-MgUserLicenseDetail -UserId Brian@domain.com | select -ExpandProperty ServicePlans | where ProvisioningStatus -ne Disabled).Count

09_difference of three as expected

Difference of three, as expected

 

Offboarding or License Removal

Depending on how a license is applied, removing a license could be as simple as removing the user from a particular group or it could be that we need to remove all assigned licensing. Both of these tasks can be accomplished with PowerShell and is a reversal of the individual assignment.


$SKU = 'c7df2760-2c81-4ef7-b578-5b5392b571df'
Set-MgUserLicense -UserID Lester@mmcug.com -AddLicenses @() -RemoveLicenses @($SKU) 

10_E5 license unchecked

E5 license is now unchecked for Lester

 

If the license is assigned by a group, we simply remove that user from the group membership.

 

Conclusion

The new method of managing licenses is similar to the older Entra ID (Azure AD) methodology. The only extra layer is knowing what permissions to assign to the user as they are performing tasks in Graph PowerShell. Otherwise, the overall process is the same. Make sure that you start learning Graph PowerShell as it will be the replacement for many other PowerShell modules over time.
 

Quick Note: If reports are needed on who has what licenses, this can also be done with PowerShell as shown here:


Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $SKU)" -All

11_list of users with assigned license

A list of users with the assigned E5 license

 

Good2know

ScriptRunner and Graph 

Microsoft Graph as a service for M365 targets

Over a year ago (ScriptRunner Portal Edition R4), the target type for M365 has been extended with the service for the Microsoft Graph module. The use of Microsoft Graph with PowerShell enables the automation of complex relationships in the context of infrastructure, users, their content and various services and properties. It is strategic for Microsoft in that it should allow access to very diverse data as well as remove the limitations of PowerShell connections to a tenant because it works stateless with REST. 

An overview of Microsoft Graph with PowerShell can be found here. Below, a screenshot from ScriptRunner: 

18_my O365 tenant

Creating an MS Graph target – options for certificate & client secret 

The implementation in ScriptRunner follows the user guidance for creating and configuring targets. The Microsoft Graph service can be added to an existing M365 target or one that is to be created. The current version of Microsoft Graph supports the two authentication methods with certificate or with client secret. If a certificate is used, the certificate must be stored in the local certificate store. If, on the other hand, a client secret is used, the name and secret must have been configured under Credentials. In both cases, a service principal must be set up in the tenant or the permissions must have been assigned to the registered ScriptRunner service.

 

 

Related links

Related posts

9 min read

Top ten tasks for Microsoft Purview PowerShell

4 min read

PowerShell benefits for SharePoint

10 min read

My top 10 ways to use Teams PowerShell

About the author: