Примеры лицензирования на основе групп Microsoft Graph PowerShell

Групповое лицензирование в идентификаторе Microsoft Entra, который является частью Microsoft Entra, доступно через портал Azure. Существуют полезные задачи, которые можно выполнять с помощью командлетов PowerShell Microsoft Graph. В этой статье мы рассмотрим некоторые примеры использования Microsoft Graph PowerShell.

Примечание

Перед запуском командлетов убедитесь сначала, что подключение к вашей организации установлено, выполнив командлет Connect-MgGraph.

Предупреждение

Эти примеры предоставляются только в демонстрационных целях. Мы рекомендуем тестировать их в меньшем масштабе или в отдельной тестовой среде, прежде чем полагаться на них в рабочей среде. Вам также может потребоваться изменить примеры в соответствии с требованиями конкретной среды.

Назначение лицензий группе

Лицензирование на основе групп предоставляет удобный способ управления назначением лицензий. Вы можете назначить одну или несколько лицензий на продукты группе, и эти лицензии назначаются всем участникам группы.

# Import the Microsoft.Graph.Groups module
Import-Module Microsoft.Graph.Groups
$groupId = "911f05cf-f635-440c-b888-e54c73e0ef1a"

# Create a hashtable to store the parameters for the Set-MgGroupLicense cmdlet
$params = @{
    AddLicenses = @(
        @{
            # Remove the DisabledPlans key as we don't need to disable any service plans
            # Specify the SkuId of the license you want to assign
            SkuId = "c42b9cae-ea4f-4ab7-9717-81576235ccac"
        }
    )
    # Keep the RemoveLicenses key empty as we don't need to remove any licenses
    RemoveLicenses = @(
    )
}

# Call the Set-MgGroupLicense cmdlet to update the licenses for the specified group
# Replace $groupId with the actual group ID
Set-MgGroupLicense -GroupId $groupId -BodyParameter $params

Просмотр лицензий продуктов, назначенных группе

(Get-MgGroup -GroupId 99c4216a-56de-42c4-a4ac-1111cd8c7c41 -Property "AssignedLicenses" | Select-Object -ExpandProperty AssignedLicenses).SkuId

Получение всех групп с лицензиями

# Import the Microsoft.Graph.Groups module
Import-Module Microsoft.Graph.Groups
# Get all groups and licenses
$groups = Get-MgGroup -All
$groupsWithLicenses = @()
# Loop through each group and check if it has any licenses assigned
foreach ($group in $groups) {
    $licenses = Get-MgGroup -GroupId $group.Id -Property "AssignedLicenses, Id, DisplayName" | Select-Object AssignedLicenses, DisplayName, Id
    if ($licenses.AssignedLicenses) {
        $groupData = [PSCustomObject]@{
            ObjectId = $group.Id
            DisplayName = $group.DisplayName
            Licenses = $licenses.AssignedLicenses
        }
        $groupsWithLicenses += $groupData
    }
}

Получение статистики для групп с лицензиями

# Import User Graph Module
Import-Module Microsoft.Graph.Users
# Authenticate to MS Graph
Connect-MgGraph -Scopes "User.Read.All", "Directory.Read.All", "Group.ReadWrite.All"
#get all groups with licenses
$groups = Get-MgGroup -All -Property LicenseProcessingState, DisplayName, Id, AssignedLicenses | Select-Object  displayname, Id, LicenseProcessingState, AssignedLicenses | Select-Object DisplayName, Id, AssignedLicenses -ExpandProperty LicenseProcessingState | Select-Object DisplayName, State, Id, AssignedLicenses | Where-Object {$_.State -eq "ProcessingComplete"}
$groupInfoArray = @()
# Filter the groups to only include those that have licenses assigned
$groups = $groups | Where-Object {$_.AssignedLicenses -ne $null}
# For each group, get the group name, license types, total user count, licensed user count, and license error count
foreach ($group in $groups) {
    $groupInfo = New-Object PSObject
    $groupInfo | Add-Member -MemberType NoteProperty -Name "Group Name" -Value $group.DisplayName
    $groupInfo | Add-Member -MemberType NoteProperty -Name "Group ID" -Value $group.Id
    $groupInfo | Add-Member -MemberType NoteProperty -Name "License Types" -Value ($group.AssignedLicenses | Select-Object -ExpandProperty SkuId)
    $groupInfo | Add-Member -MemberType NoteProperty -Name "Total User Count" -Value (Get-MgGroupMember -GroupId $group.Id -All | Measure-Object).Count
    $groupInfo | Add-Member -MemberType NoteProperty -Name "Licensed User Count" -Value (Get-MgGroupMember -GroupId $group.Id -All | Where-Object {$_.      LicenseProcessingState -eq "ProcessingComplete"} | Measure-Object).Count
    $groupInfo | Add-Member -MemberType NoteProperty -Name "License Error Count" -Value (Get-MgGroupMember -GroupId $group.Id -All | Where-Object {$_.LicenseProcessingState -eq "ProcessingFailed"} | Measure-Object).Count
    $groupInfoArray += $groupInfo
}

# Format the output and print it to the console
$groupInfoArray | Format-Table -AutoSize

Получение всех групп с ошибками лицензий

# Import User Graph Module
Import-Module Microsoft.Graph.Users
# Authenticate to MS Graph
Connect-MgGraph -Scopes "Group.Read.All"
# Get all groups in the tenant with license assigned and with errors
$groups = Get-MgGroup -All -Property LicenseProcessingState, DisplayName, Id, AssignedLicenses | Select-Object  displayname, Id, LicenseProcessingState, AssignedLicenses | Select-Object DisplayName, Id, AssignedLicenses -ExpandProperty LicenseProcessingState | Select-Object DisplayName, State, Id, AssignedLicenses | Where-Object {$_.State -eq "ProcessingFailed" -and $_.AssignedLicenses -ne $null }
# Display the results and format output
$groups | Format-Table -AutoSize

Получение всех пользователей с ошибками лицензий в группе

Зная группу, в которой есть ошибки, связанные с лицензиями, вы можете просмотреть список всех пользователей, у которых возникли эти ошибки. Пользователю также могут отображаться ошибки из других групп. Но в этом примере результаты ограничены только ошибками, относящимися к данной группы. Для этого мы проверяем свойство ReferencedObjectId каждой записи IndirectLicenseError пользователя.

# Import User Graph Module
Import-Module Microsoft.Graph.Users
# Authenticate to MS Graph
Connect-MgGraph -Scopes "Group.Read.All", "User.Read.All"
# Get all groups in the tenant with license assigned
$groups = Get-MgGroup -All -Property LicenseProcessingState, DisplayName, Id, AssignedLicenses | Select-Object  displayname, Id, LicenseProcessingState, AssignedLicenses | Select-Object DisplayName, Id, AssignedLicenses | Where-Object {$_.AssignedLicenses -ne $null }
#output array
$groupInfoArray = @()
# Get All Members from the groups and check their license status
foreach($group in $groups) {
    $groupMembers = Get-MgGroupMember -GroupId $group.Id -All -Property LicenseProcessingState, DisplayName, Id, AssignedLicenses | Select-Object  displayname, Id, LicenseProcessingState, AssignedLicenses | Select-Object DisplayName, Id, AssignedLicenses -ExpandProperty LicenseProcessingState | Select-Object DisplayName, Id, AssignedLicenses | Where-Object {$_.AssignedLicenses -ne $null }
    foreach($member in $groupMembers) {
        Write-Host "Member $($member.DisplayName)"
        if($member.LicenseProcessingState -eq "ProcessingFailed") {
            $group | Add-Member -MemberType NoteProperty -Name "License Error" -Value $member.DisplayName
            $groupInfoArray += $group
        }
    }
}

# Format the output and print it to the console

if ($groupInfoArray.Length -gt 0) {
    $groupInfoArray | Format-Table -AutoSize
}
else {
    Write-Host "No License Errors"
}

Получение всех пользователей с ошибками лицензий в организации в целом

Чтобы просмотреть список всех пользователей с ошибками лицензий из одной или нескольких групп, используйте следующий скрипт. Этот сценарий отобразит по одной строке для каждого пользователя, указывая ошибку лицензии, что позволит четко определить источник каждой ошибки.

# Import User Graph Module
Import-Module Microsoft.Graph.Users
# Authenticate to MS Graph
Connect-MgGraph -Scopes "User.Read.All"
# Get All Users From the Tenant with licenses assigned
$users = Get-MgUser -All -Property AssignedLicenses, LicenseAssignmentStates, DisplayName | Select-Object DisplayName, AssignedLicenses -ExpandProperty LicenseAssignmentStates | Select-Object DisplayName, AssignedByGroup, State, Error, SkuId
#count the number of users found with errors
$count = 0
# Loop through each user and check the Error property for None value
foreach($user in $users) {
    if($user.Error -ne "None") {
        $count += 1
        Write-Host "User $($user.DisplayName) has a license error"
    }
}
if ($count -le 0) {
 write-host "No user found with license errors"
}

Как проверить, назначена ли лицензия пользователю напрямую или унаследована от группы?

# Connect to Microsoft Graph using Connect-MgGraph
Connect-MgGraph -Scopes "User.Read.All"

# Get all users using Get-MgUser with a filter
$users = Get-MgUser -All -Property AssignedLicenses, LicenseAssignmentStates, DisplayName | Select-Object DisplayName, AssignedLicenses -ExpandProperty LicenseAssignmentStates | Select-Object DisplayName, AssignedByGroup, State, Error, SkuId

$output = @()


# Loop through all users and get the AssignedByGroup Details which will list the groupId
foreach ($user in $users) {
    # Get the group ID if AssignedByGroup is not empty
    if ($user.AssignedByGroup -ne $null)
    {
        $groupId = $user.AssignedByGroup
        $groupName = Get-MgGroup -GroupId $groupId | Select-Object -ExpandProperty DisplayName  
        Write-Host "$($user.DisplayName) is assigned by group - $($groupName)" -ErrorAction SilentlyContinue -ForegroundColor Yellow
        $result = [pscustomobject]@{
            User=$user.DisplayName
            AssignedByGroup=$true
            GroupName=$groupName
            GroupId=$groupId
        }
        $output += $result
    }

    else {
    $result = [pscustomobject]@{
            User=$user.DisplayName
            AssignedByGroup=$false
            GroupName="NA"
            GroupId="NA"
        }
        $output += $result
        Write-Host "$($user.DisplayName) is Not assigned by group" -ErrorAction SilentlyContinue -ForegroundColor Cyan
    }
        
    
}

# Display the result
$output | ft

Удаление прямых лицензий для пользователей с лицензиями группы

Целью этого сценария является удаление ненужных прямых лицензий для пользователей, которые уже унаследовали такую же лицензию от группы (например, при переходе на групповое лицензирование).

Примечание

Чтобы пользователи не потеряли доступ к службам и данным, важно убедиться, что лицензии, назначаемые напрямую, не предоставляют больше функциональных возможностей службы, чем унаследованные лицензии. В настоящее время с помощью PowerShell невозможно определить, какие службы включены с помощью унаследованных лицензий или прямых лицензий. Таким образом, скрипт использует минимальный уровень служб, которые, как известно, наследуются от групп проверка и гарантирует, что пользователи не испытывают непредвиденной потери служб.

# Import the Microsoft.Graph.Users and Microsoft.Graph.Groups modules
Import-Module Microsoft.Graph.Users -Force
Import-Module Microsoft.Graph.Authentication -Force
Import-Module Microsoft.Graph.Users.Actions -Force
Import-Module Microsoft.Graph.Groups -Force

Clear-Host

if ($null -eq (Get-MgContext)) {
    Connect-MgGraph -Scopes "Directory.Read.All, User.Read.All, Group.Read.All, Organization.Read.All" -NoWelcome
}

# Get all groups with licenses assigned
$groupsWithLicenses = Get-MgGroup -All -Property AssignedLicenses, DisplayName, Id | Where-Object { $_.assignedlicenses } | Select-Object DisplayName, Id -ExpandProperty AssignedLicenses | Select-Object DisplayName, Id, SkuId

$output = @()

# Check if there is any group that has licenses assigned or not
if ($null -ne $groupsWithLicenses) {
    # Loop through each group
    foreach ($group in $groupsWithLicenses) {
        # Get the group's licenses
        $groupLicenses = $group.SkuId
    
        # Get the group's members
        $groupMembers = Get-MgGroupMember -GroupId $group.Id -All

        # Check if the group member list is empty or not
        if ($groupMembers) {
            # Loop through each member
            foreach ($member in $groupMembers) {
                # Check if the member is a user
                if ($member.AdditionalProperties.'@odata.type' -eq '#microsoft.graph.user') {
                    # Get the user's direct licenses
                    Write-Host "Fetching license details for $($member.AdditionalProperties.displayName)" -ForegroundColor Yellow
                    
                    # Get User With Directly Assigned Licenses Only
                    $user = Get-MgUser -UserId $member.Id -Property AssignedLicenses, LicenseAssignmentStates, DisplayName | Select-Object DisplayName, AssignedLicenses -ExpandProperty LicenseAssignmentStates | Select-Object DisplayName, AssignedByGroup, State, Error, SkuId | Where-Object { $_.AssignedByGroup -eq $null }

                    $licensesToRemove = @()
                    if($user)
                    {
                        if ($user.count -ge 2) {
                            foreach ($u in $user) {
                                $userLicenses = $u.SkuId
                                $licensesToRemove += $userLicenses | Where-Object { $_ -in $groupLicenses }
                            }
                        }
                        else {
                            $userLicenses = $user.SkuId
                            $licensesToRemove = $userLicenses | Where-Object { $_ -in $groupLicenses }
                        }  
                    } else {
                        Write-Host "No conflicting licenses found for the user $($member.AdditionalProperties.displayName)" -ForegroundColor Green
                    }
                    
                                       
        
                    # Remove the licenses from the user
                    if ($licensesToRemove) {
                        Write-Host "Removing the license $($licensesToRemove) from user $($member.AdditionalProperties.displayName) as inherited from group $($group.DisplayName)" -ForegroundColor Green
                        $result = Set-MgUserLicense -UserId $member.Id -AddLicenses @() -RemoveLicenses $licensesToRemove
                        $obj = [PSCustomObject]@{
                            User                      = $result.DisplayName
                            Id                        = $result.Id
                            LicensesRemoved           = $licensesToRemove
                            LicenseInheritedFromGroup = $group.DisplayName
                            GroupId                   = $group.Id
                        }

                        $output += $obj

                    } 
                    else {
                        Write-Host "No action required for $($member.AdditionalProperties.displayName)" -ForegroundColor Green
                        }
        
                }
            }
        }
        else {
            Write-Host "The licensed group $($group.DisplayName) has no members, exiting now!!" -ForegroundColor Yellow
        }   
        
    }
    
    $output | Format-Table -AutoSize
}
else {
    Write-Host "No groups found with licenses assigned." -ForegroundColor Cyan
}

Дальнейшие действия

Дополнительные сведения о наборе функций для управления лицензиями с помощью групп см. в следующих статьях: