-
-
Save mrik23/2ed37ce0c7c4a79605bdcf052e29b391 to your computer and use it in GitHub Desktop.
<# | |
Modified version of the script from Microsoft Documentation. | |
Removed the part that checks if the users is assigned more products than the group assigned license. | |
Added connection part and help to find Sku and Group Object ID. | |
This script requires Azure AD (aks MSOL) PowerShell v1. It doesn't seem possible to do so with v2. | |
Ref: https://docs.microsoft.com/en-us/azure/active-directory/active-directory-licensing-ps-examples | |
#> | |
Import-Module MSOnline | |
$UserCredential = Get-Credential | |
Connect-MsolService -Credential $UserCredential | |
#Get License Sku for the tenant | |
Get-MsolAccountSku | |
#license to be removed | |
$skuId = "Contoso:STANDARDPACK" | |
#add here the group with license assignment to be processed | |
$LicensedGroup = "Licensed_Group" | |
#Get the group Object ID | |
$groupId = (Get-MsolGroup -SearchString $LicensedGroup).ObjectId | |
#Helper functions used by the script | |
#Returns TRUE if the user has the license assigned directly | |
function UserHasLicenseAssignedDirectly | |
{ | |
Param([Microsoft.Online.Administration.User]$user, [string]$skuId) | |
$license = GetUserLicense $user $skuId | |
if ($license -ne $null) | |
{ | |
#GroupsAssigningLicense contains a collection of IDs of objects assigning the license | |
#This could be a group object or a user object (contrary to what the name suggests) | |
#If the collection is empty, this means the license is assigned directly - this is the case for users who have never been licensed via groups in the past | |
if ($license.GroupsAssigningLicense.Count -eq 0) | |
{ | |
return $true | |
} | |
#If the collection contains the ID of the user object, this means the license is assigned directly | |
#Note: the license may also be assigned through one or more groups in addition to being assigned directly | |
foreach ($assignmentSource in $license.GroupsAssigningLicense) | |
{ | |
if ($assignmentSource -ieq $user.ObjectId) | |
{ | |
return $true | |
} | |
} | |
return $false | |
} | |
return $false | |
} | |
#Returns TRUE if the user is inheriting the license from a specific group | |
function UserHasLicenseAssignedFromThisGroup | |
{ | |
Param([Microsoft.Online.Administration.User]$user, [string]$skuId, [Guid]$groupId) | |
$license = GetUserLicense $user $skuId | |
if ($license -ne $null) | |
{ | |
#GroupsAssigningLicense contains a collection of IDs of objects assigning the license | |
#This could be a group object or a user object (contrary to what the name suggests) | |
foreach ($assignmentSource in $license.GroupsAssigningLicense) | |
{ | |
#If the collection contains at least one ID not matching the user ID this means that the license is inherited from a group. | |
#Note: the license may also be assigned directly in addition to being inherited | |
if ($assignmentSource -ieq $groupId) | |
{ | |
return $true | |
} | |
} | |
return $false | |
} | |
return $false | |
} | |
#Returns the license object corresponding to the skuId. Returns NULL if not found | |
function GetUserLicense | |
{ | |
Param([Microsoft.Online.Administration.User]$user, [string]$skuId, [Guid]$groupId) | |
#we look for the specific license SKU in all licenses assigned to the user | |
foreach($license in $user.Licenses) | |
{ | |
if ($license.AccountSkuId -ieq $skuId) | |
{ | |
return $license | |
} | |
} | |
return $null | |
} | |
#process staging removal for only 20 members in the group first | |
Get-MsolGroupMember -MaxResults 20 -GroupObjectId $groupId | | |
#get full info about each user in the group | |
Get-MsolUser -ObjectId {$_.ObjectId} | | |
Foreach { | |
$user = $_; | |
$operationResult = ""; | |
#check if Direct license exists on the user | |
if (UserHasLicenseAssignedDirectly $user $skuId) | |
{ | |
#check if the license is assigned from this group, as expected | |
if (UserHasLicenseAssignedFromThisGroup $user $skuId $groupId) | |
{ | |
#remove the direct license from user | |
Set-MsolUserLicense -ObjectId $user.ObjectId -RemoveLicenses $skuId | |
$operationResult = "Removed direct license from user." | |
} | |
else | |
{ | |
$operationResult = "User does not inherit this license from this group. License removal was skipped." | |
} | |
} | |
else | |
{ | |
$operationResult = "User has no direct license to remove. Skipping." | |
} | |
#format output | |
New-Object Object | | |
Add-Member -NotePropertyName UserId -NotePropertyValue $user.ObjectId -PassThru | | |
Add-Member -NotePropertyName OperationResult -NotePropertyValue $operationResult -PassThru | |
} | Format-Table | |
<#You can then process all members in the group if the result of staging is OK | |
Get-MsolGroupMember -All -GroupObjectId $groupId | | |
#get full info about each user in the group | |
Get-MsolUser -ObjectId {$_.ObjectId} | | |
Foreach { | |
$user = $_; | |
$operationResult = ""; | |
#check if Direct license exists on the user | |
if (UserHasLicenseAssignedDirectly $user $skuId) | |
{ | |
#check if the license is assigned from this group, as expected | |
if (UserHasLicenseAssignedFromThisGroup $user $skuId $groupId) | |
{ | |
#remove the direct license from user | |
Set-MsolUserLicense -ObjectId $user.ObjectId -RemoveLicenses $skuId | |
$operationResult = "Removed direct license from user." | |
} | |
else | |
{ | |
$operationResult = "User does not inherit this license from this group. License removal was skipped." | |
} | |
} | |
else | |
{ | |
$operationResult = "User has no direct license to remove. Skipping." | |
} | |
#format output | |
New-Object Object | | |
Add-Member -NotePropertyName UserId -NotePropertyValue $user.ObjectId -PassThru | | |
Add-Member -NotePropertyName OperationResult -NotePropertyValue $operationResult -PassThru | |
} | Format-Table | |
#> | |
Hello Kumarn26 - good catch. This script should be ported to MS Graph for longevity. See Assign Microsoft 365 licenses to user accounts with PowerShell. The recommended replacement for Set-MsolUserLicense
is Set-MgUserLicense
.
But in the interim, I suspect the "...You have exceeded the maximum number of allowable transactions..." message is a throttling issue.
You may be able to work around it by adding something like Start-Sleep -Seconds 2
after the Set-MsolUserLicense
cmdlet. A one second pause may be sufficient, but both are admittedly untested. Regardless, script execution will be slowed down.
...
#remove the direct license from user
Set-MsolUserLicense -ObjectId $user.ObjectId -RemoveLicenses $skuId
$operationResult = "Removed direct license from user."
Start-Sleep -Seconds 2
...
Hope it helps.
Hello Nitin - yeah, understand the pain. Even a one second sleep adds A LOT of delay when processing thousands of users. If you are brave, feel free to give this a whirl:
https://gist.github.com/neil-sabol/250d43047c6c061067fca274588a4ce8
That is my first pass at porting this script to use the MgGraph (Microsoft Graph) PowerShell module instead of MSOnline. My very cursory testing suggests it works, but I cannot say 100%. Additional testing is needed. If you are able to do that, it would be appreciated.
Pending the outcome, @mrik23, would you mind incorporating the updates into your Gist since you clearly have the SEO advantage here? ;)
Thank you all,
-N
if someone test the above out and it works please report back
I think MS is going to depreciate it soon. It only processes 27 accounts now and then throws error:
Set-MsolUserLicense : You have exceeded the maximum number of allowable transactions. Please try again later.
At line:145 char:17