Dec 29 2022 02:27 AM
Hi all,
Merry Xmas and have a happy new year to start.
Now to my question, I am looking into the script here https://learn.microsoft.com/en-us/azure/active-directory/enterprise-users/licensing-ps-examples which removes direct licenses for users with group licenses - the script from the article found below and I have set these sections to have the group ID and SKUId of the license I am working with just to test how the script works and for any logic issues.
#BEGIN: executing the script #the group to be processed $groupId = "48ca647b-7e4d-41e5-aa66-40cab1e19101" #license to be removed - Office 365 E3 $skuId = "contoso:ENTERPRISEPACK" #minimum set of service plans we know are inherited from groups - we want to make sure that there aren't any users who have more services enabled #which could mean that they may lose access after we remove direct licenses $servicePlansFromGroups = ("EXCHANGE_S_ENTERPRISE", "SHAREPOINTENTERPRISE", "OFFICESUBSCRIPTION")
The issue I am having is that when it runs I am not seeing the license being remove that were directly assigned even though I know they are.
UserId OperationResult ------ --------------- 6294ea82-64e0-4773-8006-744de05721e6 User has no direct license to remove. Skipping.
The other issue is that I have one group that currently provides 3 licenses so I need to make this work on the basis of that one group, I assume I can use this logic.
#BEGIN: executing the script #the group to be processed $groupId = "48ca647b-7e4d-41e5-aa66-40cab1e19101,48ca647b-7e4d-41e5-aa66-40cab1e19101. etc." #license to be removed - Office 365 E3 $skuId = "contoso:ENTERPRISEPACK,contoso:WINDOWS_STORE" #minimum set of service plans we know are inherited from groups - we want to make sure that there aren't any users who have more services enabled #which could mean that they may lose access after we remove direct licenses $servicePlansFromGroups = ("EXCHANGE_S_ENTERPRISE", "SHAREPOINTENTERPRISE", "OFFICESUBSCRIPTION")
Full Script
#BEGIN: 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 } #produces a list of disabled service plan names for a set of plans we want to leave enabled function GetDisabledPlansForSKU { Param([string]$skuId, [string[]]$enabledPlans) $allPlans = Get-MsolAccountSku | where {$_.AccountSkuId -ieq $skuId} | Select -ExpandProperty ServiceStatus | Where {$_.ProvisioningStatus -ine "PendingActivation" -and $_.ServicePlan.TargetClass -ieq "User"} | Select -ExpandProperty ServicePlan | Select -ExpandProperty ServiceName $disabledPlans = $allPlans | Where {$enabledPlans -inotcontains $_} return $disabledPlans } function GetUnexpectedEnabledPlansForUser { Param([Microsoft.Online.Administration.User]$user, [string]$skuId, [string[]]$expectedDisabledPlans) $license = GetUserLicense $user $skuId $extraPlans = @(); if($license -ne $null) { $userDisabledPlans = $license.ServiceStatus | where {$_.ProvisioningStatus -ieq "Disabled"} | Select -ExpandProperty ServicePlan | Select -ExpandProperty ServiceName $extraPlans = $expectedDisabledPlans | where {$userDisabledPlans -notcontains $_} } return $extraPlans } #END: helper functions #BEGIN: executing the script #the group to be processed $groupId = "48ca647b-7e4d-41e5-aa66-40cab1e19101" #license to be removed - Office 365 E3 $skuId = "contoso:ENTERPRISEPACK" #minimum set of service plans we know are inherited from groups - we want to make sure that there aren't any users who have more services enabled #which could mean that they may lose access after we remove direct licenses $servicePlansFromGroups = ("EXCHANGE_S_ENTERPRISE", "SHAREPOINTENTERPRISE", "OFFICESUBSCRIPTION") $expectedDisabledPlans = GetDisabledPlansForSKU $skuId $servicePlansFromGroups #process all members in the group and get full info about each user in the group looping through group members. Get-MsolGroupMember -All -GroupObjectId $groupId | 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) { #check if there are any extra plans we didn't expect - we are being extra careful not to remove unexpected services $extraPlans = GetUnexpectedEnabledPlansForUser $user $skuId $expectedDisabledPlans if ($extraPlans.Count -gt 0) { $operationResult = "User has extra plans that may be lost - license removal was skipped. Extra plans: $extraPlans" } else { #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 #END: executing the script
Anyone else used this before?
Mar 08 2023 08:27 AM
It seems that the script is not properly detecting users who have direct licenses assigned. One possible reason for this is that the script is not properly checking for licenses with an empty GroupsAssigningLicense collection, which is the case for users who have never been licensed via groups in the past. To address this, you can modify the UserHasLicenseAssignedDirectly function as follows:
#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 or contains only the user's own ID, 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) -or ($license.GroupsAssigningLicense.Count -eq 1 -and $license.GroupsAssigningLicense[0] -ieq $user.ObjectId))
{
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
}
This modified version of the function checks whether the GroupsAssigningLicense collection is empty or contains only the user's own ID, in addition to checking whether the collection contains the ID of the user object, to determine whether the license is assigned directly.
Regarding your second question, if you want to process multiple groups and/or multiple SKUs, you can modify the script as follows:
#BEGIN: executing the script
#the groups to be processed
$groupIds = @("48ca647b-7e4d-41e5-aa66-40cab1e19101", "48ca647b-7e4d-41e5-aa66-40cab1e19102", ...)
#licenses to be removed - Office 365 E3 and Windows 10 Enterprise E3
$skuIds = @("contoso:ENTERPRISEPACK", "contoso:WINDOWS_STORE")
#minimum set of service plans we know are inherited from groups - we want to make sure that there aren't any users who have more services enabled
#which could mean that they may lose access after we remove direct licenses
$servicePlansFromGroups = ("EXCHANGE_S_ENTERPRISE", "SHAREPOINTENTERPRISE", "OFFICESUBSCRIPTION")
#process each group and SKU
foreach ($groupId in $groupIds)
{
foreach ($skuId in $skuIds)
{
Write-Host "Processing group $groupId and SKU $skuId"
#get all users in the group
$users = Get-MsolUser -All -UserPrincipalName *@contoso.com | where {$_.ProxyAddresses -like "*$groupId*"}
#remove direct licenses for users with group licenses
foreach ($user in $users)
{
if (UserHasLicenseAssignedDirectly $user $skuId)
{
if ($user.Licenses.ServiceStatus | where {$_.ServicePlan.ServiceName -in