12 min read
Licensing with Microsoft Graph PowerShell
The Microsoft Graph SDK PowerShell module is replacing two other modules. Learn more about connecting to Graph, finding...
ScriptRunner Blog
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.
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
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:
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.
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:
Choice of connection PowerShell cmdlets
Let us use Connect-Graph. Beyond permissions, we have a couple of other options for the connection.
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.
Two Disconnect PowerShell cmdlets as well
We can get a list of available licenses by first running this cmdlet to give us a list of available plans:
Get-MgSubscribedSku | fl
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
Truncated feature list
From here we can proceed. Now we can look up the ServicePlanId’s for license management.
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.
These are the Office 365 / Microsoft 365 E1, E3 and E5 license groups. Below are their SKU Ids which we will need later.
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.
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.
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:
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.
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:
DisplayName
Id
-- -----------
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:
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
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
Difference of three, as expected
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)
E5 license is now unchecked for Lester
If the license is assigned by a group, we simply remove that user from the group membership.
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
A list of users with the assigned E5 license
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:
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.
Sep 14, 2023 by Damian Scoles
The Microsoft Graph SDK PowerShell module is replacing two other modules. Learn more about connecting to Graph, finding...
Mar 6, 2024 by Damian Scoles
With MS Online and Azure AD module deprecation, it's time to map previous tasks to new methods. We hope the following...
Sep 27, 2022 by Michael Gall
In recent years, hacker attacks have increased, but only the most spectacular ones (such as the Uber hack) receive...
Damian Scoles is a ten-time Microsoft MVP specializing in Exchange, Office 365 and PowerShell who has 25 years of IT industry experience. He is based in the Chicago area and started out managing Exchange 5.5 and Windows NT. Over the years he has worked with Office 365 since BPOS and his experience has grown to include Azure AD, Security and Compliance Admin Centers, and Exchange Online. His community outreach includes contributing to TechNet forums, creating PowerShell scripts that can be found on his blogs, writing in-depth PowerShell / Office365 / Exchange blog articles, tweeting, and creating PowerShell videos on YouTube. He has written five PowerShell books and is also actively working on the book "Microsoft 365 Security for IT Pros".