Microsoft Entra의 일부인 Microsoft Entra ID의 그룹 기반 라이선스는 Azure Portal을 통해 사용할 수 있습니다. Microsoft Graph PowerShell Cmdlet을 사용하여 수행할 수 있는 유용한 작업이 있습니다.
이 문서에서는 Microsoft Graph PowerShell을 사용하는 몇 가지 예제를 살펴보겠습니다.
경고
이 샘플은 데모용으로만 제공됩니다. 프로덕션 환경에서 사용하기 전에 더 작은 규모 또는 별도의 테스트 환경에서 테스트하는 것이 좋습니다. 특정 환경의 요구 사항을 충족하도록 샘플을 수정할 수 있습니다.
cmdlet 실행을 시작하기 전에 먼저 Connect-MgGraph cmdlet을 실행하여 조직에 연결해야 합니다.
그룹에 라이선스 할당
그룹 기반 라이선스를 통해 라이선스 할당을 편리하게 관리할 수 있습니다. 그룹에 하나 이상의 제품 라이선스를 할당할 수 있으며 해당 라이선스는 그룹의 모든 구성원에게 할당됩니다.
# Import the Microsoft.Graph.Groups module
Import-Module Microsoft.Graph.Groups
# Define the group ID - replace with your actual group ID
$groupId = "11111111-1111-1111-1111-111111111111"
# 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 = "11111111-1111-1111-1111-111111111111" }
)
# 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
그룹에 할당된 제품 라이선스 보기
The [Get-MgGroup](/powershell/module/microsoft.graph.groups/get-mggroup) cmdlet can be used to retrieve the group object and check the *AssignedLicenses* property: it lists all product licenses currently assigned to the group.
```powershell
# Define the group ID
$groupId = "99c4216a-56de-42c4-a4ac-e411cd8c7c41"
# Get the group with the specified ID and its assigned licenses
$group = Get-MgGroup -GroupId $groupId -Property "AssignedLicenses"
# Extract the assigned licenses
$assignedLicenses = $group | Select-Object -ExpandProperty AssignedLicenses
# Extract the SKU IDs from the assigned licenses
$skuIds = $assignedLicenses | Select-Object -ExpandProperty SkuId
# For each SKU ID, get the corresponding SKU part number
$skuPartNumbers = $skuIds | ForEach-Object {
$skuId = $_
$subscribedSku = Get-MgSubscribedSku | Where-Object { $_.SkuId -eq $skuId }
$skuPartNumber = $subscribedSku | Select-Object -ExpandProperty SkuPartNumber
$skuPartNumber
}
# Output the SKU part numbers
$skuPartNumbers
이 결과는 다음과 같습니다.
SkuPartNumber
-------------
ENTERPRISEPREMIUM
EMSPREMIUM
할당된 라이선스가 있는 모든 그룹 가져오기
다음 명령을 실행하면 라이선스가 할당되어 있는 모든 그룹을 찾을 수 있습니다.
Get-MgGroup -All -Property Id, MailNickname, DisplayName, GroupTypes, Description, AssignedLicenses | Where-Object {$_.AssignedLicenses -ne $null }
어떤 제품이 할당되어 있는지에 대해 자세한 정보를 표시할 수 있습니다.
# Get all groups with assigned licenses
$groups = Get-MgGroup -All -Property Id, MailNickname, DisplayName, GroupTypes, Description, AssignedLicenses | Where-Object {$_.AssignedLicenses -ne $null }
# Process each group
$groupInfo = foreach ($group in $groups) {
# For each group, get the SKU part numbers of the assigned licenses
$skuPartNumbers = foreach ($skuId in $group.AssignedLicenses.SkuId) {
$subscribedSku = Get-MgSubscribedSku | Where-Object { $_.SkuId -eq $skuId }
$subscribedSku.SkuPartNumber
}
# Create a custom object with the group's object ID, display name, and license SKU part numbers
[PSCustomObject]@{
ObjectId = $group.Id
DisplayName = $group.DisplayName
Licenses = $skuPartNumbers -join ', '
}
}
$groupInfo
이 결과는 다음과 같습니다.
Id DisplayName AssignedLicenses
-- ----------- ----------------
7023a314-6148-4d7b-b33f-6c775572879a EMS E5 – Licensed users EMSPREMIUM
cf41f428-3b45-490b-b69f-a349c8a4c38e PowerBi - Licensed users POWER_BI_STANDARD
962f7189-59d9-4a29-983f-556ae56f19a5 O365 E3 - Licensed users ENTERPRISEPACK
c2652d63-9161-439b-b74e-fcd8228a7074 EMSandOffice {ENTERPRISEPREMIUM,EMSPREMIUM}
그룹에 할당된 비활성화된 모든 서비스 계획 라이선스 보기
$groups = Get-MgGroup -All
$groupsWithLicenses = @()
foreach ($group in $groups) {
$licenses = Get-MgGroup -GroupId $group.Id -Property "AssignedLicenses, Id, DisplayName" |
Select-Object AssignedLicenses, DisplayName, Id
if ($licenses.AssignedLicenses) {
foreach ($license in $licenses.AssignedLicenses) {
$skuId = $license.SkuId
$disabledPlans = $license.DisabledPlans
$skuDetails = Get-MgSubscribedSku | Where-Object { $_.SkuId -eq $skuId }
$skuPartNumber = $skuDetails.SkuPartNumber
$disabledPlanDetails = @()
if ($disabledPlans.Count -gt 0) {
foreach ($planId in $disabledPlans) {
$planDetails = $skuDetails.ServicePlans | Where-Object { $_.ServicePlanId -eq $planId }
if ($planDetails) {
$disabledPlanDetails += "$($planDetails.ServicePlanName) ($planId)"
}
}
} else {
$disabledPlanDetails = "None"
}
$groupsWithLicenses += [PSCustomObject]@{
GroupObjectId = $group.Id
GroupName = $group.DisplayName
SkuId = $skuId
SkuPartNumber = $skuPartNumber
DisabledPlans = ($disabledPlanDetails -join ", ")
}
}
}
}
Export to CSV
$csvPath = "$env:USERPROFILE\Documents\GroupLicenses.csv"
$groupsWithLicenses | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
Write-Host "Export completed: $csvPath"
라이선스가 있는 그룹에 대한 통계 가져오기
# 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
라이선스 오류가 있는 모든 그룹 가져오기
# Get all groups that have assigned licenses
$groups = Get-MgGroup -All -Property DisplayName, Id, AssignedLicenses |
Where-Object { $_.AssignedLicenses -ne $null } |
Select-Object DisplayName, Id, AssignedLicenses
# Initialize an array to store group information
$groupInfo = @()
# Iterate over each group
foreach ($group in $groups) {
$groupId = $group.Id
$groupName = $group.DisplayName
# Initialize counters for total members and members with license errors
$totalCount = 0
$licenseErrorCount = 0
# Get all members of the group that have license errors
$members = Get-MgGroupMemberWithLicenseError -GroupId $groupId
# Process each member
foreach ($member in $members) {
$totalCount++
# If the member has a license error (indicated by a non-empty Id), increment the error count
if (![string]::IsNullOrEmpty($member.Id)) {
$licenseErrorCount++
}
}
# Create a custom object with the group's information and counts
$groupInfo += [PSCustomObject]@{
GroupName = $groupName
GroupId = $groupId
TotalUserCount = $totalCount
LicenseErrorCount = $licenseErrorCount
}
}
# Display the groups with licensing errors
$groupInfo | Where-Object { $_.LicenseErrorCount -gt 0 } | Format-Table -Property GroupName, GroupId, TotalUserCount, LicenseErrorCount
그룹에서 라이선스 오류가 있는 모든 사용자 가져오기
라이선스 관련 오류가 포함된 그룹이 있는 경우 오류의 영향을 받는 모든 사용자를 나열할 수 있습니다. 사용자는 다른 그룹에서 발생한 오류를 가질 수도 있습니다. 하지만 이 예제에서는 사용자에 대한 IndirectLicenseError 항목의 ReferencedObjectId 속성을 확인하여 요청된 그룹과 관련된 오류만으로 결과를 제한합니다.
# Import necessary modules
Import-Module Microsoft.Graph.Users
Import-Module Microsoft.Graph.Groups
# Specify the group ID you want to check
$groupId = "ENTER-YOUR-GROUP-ID-HERE"
# Authenticate to Microsoft Graph
Connect-MgGraph -Scopes "Group.Read.All", "User.Read.All"
# Get the specified group
$group = Get-MgGroup -GroupId $groupId -Property DisplayName, Id, AssignedLicenses
Write-Host "Checking license errors for group: $($group.DisplayName)" -ForegroundColor Cyan
# Initialize output array
$groupInfoArray = @()
# Get all members from the group and check their license status
$groupMembers = Get-MgGroupMember -GroupId $group.Id -All
$errorCount = 0
# Process each member
foreach ($memberId in $groupMembers.Id) {
# Get user details
$user = Get-MgUser -UserId $memberId -Property DisplayName, Id, LicenseAssignmentStates
# Check for license errors
$licenseErrors = $user.LicenseAssignmentStates | Where-Object {
$_.AssignedByGroup -eq $groupId -and $_.Error -ne "None"
}
if ($licenseErrors) {
$errorCount++
$userInfo = [PSCustomObject]@{
GroupName = $group.DisplayName
GroupId = $group.Id
UserName = $user.DisplayName
UserId = $user.Id
Error = ($licenseErrors.Error -join ", ")
ErrorSubcode = ($licenseErrors.ErrorSubcode -join ", ")
}
$groupInfoArray += $userInfo
}
}
# Summary
Write-Host "Found $errorCount users with license errors in group $($group.DisplayName)" -ForegroundColor Yellow
# Format the output and print it to the console
if ($groupInfoArray.Length -gt 0) {
$groupInfoArray | Format-Table -AutoSize
}
else {
Write-Host "No License Errors"
}
전체 조직에서 라이선스 오류가 있는 모든 사용자 가져오기
다음 스크립트는 하나 이상의 그룹에서 라이선스 오류가 있는 모든 사용자를 가져오는 데 사용할 수 있습니다. 이 스크립트는 사용자당, 라이선스 오류당 한 행을 출력하기 때문에 각 오류의 소스를 명확하게 식별할 수 있습니다.
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "User.Read.All", "Directory.Read.All", "Organization.Read.All"
# Retrieve all SKUs in the tenant
$skus = Get-MgSubscribedSku -All | Select-Object SkuId, SkuPartNumber
# Retrieve all users in the tenant with required properties
$users = Get-MgUser -All -Property AssignedLicenses, LicenseAssignmentStates, DisplayName, Id, UserPrincipalName
# Initialize an empty array to store the user license information
$allUserLicenses = @()
foreach ($user in $users) {
# Initialize a hash table to track all assignment methods for each license
$licenseAssignments = @{}
$licenseErrors = @()
# Loop through license assignment states
foreach ($assignment in $user.LicenseAssignmentStates) {
$skuId = $assignment.SkuId
$assignedByGroup = $assignment.AssignedByGroup
$assignmentMethod = if ($assignedByGroup -ne $null) {
# If the license was assigned by a group, get the group name
$group = Get-MgGroup -GroupId $assignedByGroup
if ($group) { $group.DisplayName } else { "Unknown Group" }
} else {
# If the license was assigned directly by the user
"User"
}
# Check for errors in the assignment state and capture them
if ($assignment.Error -ne $null -or $assignment.ErrorSubcode -ne $null) {
$errorDetails = @{
Error = $assignment.Error
ErrorSubcode = $assignment.ErrorSubcode
SkuId = $skuId
AssignedBy = $assignmentMethod
}
$licenseErrors += $errorDetails
}
# Ensure all assignment methods are captured
if (-not $licenseAssignments.ContainsKey($skuId)) {
$licenseAssignments[$skuId] = @($assignmentMethod)
} else {
$licenseAssignments[$skuId] += $assignmentMethod
}
}
# Process assigned licenses
foreach ($skuId in $licenseAssignments.Keys) {
# Get SKU details from the pre-fetched list
$sku = $skus | Where-Object { $_.SkuId -eq $skuId } | Select-Object -First 1
$skuPartNumber = if ($sku) { $sku.SkuPartNumber } else { "Unknown SKU" }
# Sort and join the assignment methods
$assignmentMethods = ($licenseAssignments[$skuId] | Sort-Object -Unique) -join ", "
# Clean up license errors to make them more legible
$errorDetails = if ($licenseErrors.Count -gt 0) {
$errorMessages = $licenseErrors | Where-Object { $_.SkuId -eq $skuId } | ForEach-Object {
# Check if error or subcode are empty, and filter them out
if ($_Error -ne "None" -and $_.ErrorSubcode) {
"$($_.AssignedBy): Error: $($_.Error) Subcode: $($_.ErrorSubcode)"
} elseif ($_Error -ne "None") {
"$($_.AssignedBy): Error: $($_.Error)"
} elseif ($_.ErrorSubcode) {
"$($_.AssignedBy): Subcode: $($_.ErrorSubcode)"
}
}
# Join filtered error messages into a clean output
$errorMessages -join "; "
} else {
"No Errors"
}
# Construct a custom object to store the user's license information
$userLicenseInfo = [PSCustomObject]@{
UserId = $user.Id
UserDisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
SkuId = $skuId
SkuPartNumber = $skuPartNumber
AssignedBy = $assignmentMethods
LicenseErrors = $errorDetails
}
# Add the user's license information to the array
$allUserLicenses += $userLicenseInfo
}
}
# Export the results to a CSV file
$path = Join-path $env:LOCALAPPDATA ("UserLicenseAssignments_" + [string](Get-Date -UFormat %Y%m%d) + ".csv")
$allUserLicenses | Export-Csv $path -Force -NoTypeInformation
# Display the location of the CSV file
Write-Host "CSV file generated at: $((Get-Item $path).FullName)"
참고
이 스크립트는 사용자 환경에서 라이선스가 부여된 모든 사용자 목록을 검색하여 할당된 라이선스와 할당 방법을 보여 줍니다. 결과에서 "AssignedBy"는 "User"를 표시하며 직접 라이선스 할당을 나타냅니다. "SkuPartNumber"에 "알 수 없는 SKU"가 표시되면 테넌트에서 특정 라이선스 SKU가 비활성화되었음을 나타냅니다. 스크립트는 추가 분석을 위해 전체 결과를 로컬 AppData 폴더의 CSV 파일로 내보냅니다.
사용자 라이선스가 직접 할당되었는지 또는 그룹에서 상속되었는지 확인
# Retrieve all SKUs in the tenant
$skus = Get-MgSubscribedSku -All | Select-Object SkuId, SkuPartNumber
# Retrieve all users in the tenant with required properties
$users = Get-MgUser -All -Property AssignedLicenses, LicenseAssignmentStates, DisplayName, Id, UserPrincipalName
# Initialize an empty array to store the user license information
$allUserLicenses = @()
foreach ($user in $users) {
# Initialize a hash table to track all assignment methods for each license
$licenseAssignments = @{}
# Loop through license assignment states
foreach ($assignment in $user.LicenseAssignmentStates) {
$skuId = $assignment.SkuId
$assignedByGroup = $assignment.AssignedByGroup
$assignmentMethod = if ($assignedByGroup -ne $null) {
# If the license was assigned by a group, get the group name
$group = Get-MgGroup -GroupId $assignedByGroup
if ($group) { $group.DisplayName } else { "Unknown Group" }
} else {
# If the license was assigned directly by the user
"User"
}
# Ensure all assignment methods are captured
if (-not $licenseAssignments.ContainsKey($skuId)) {
$licenseAssignments[$skuId] = @($assignmentMethod)
} else {
$licenseAssignments[$skuId] += $assignmentMethod
}
}
# Process assigned licenses
foreach ($skuId in $licenseAssignments.Keys) {
# Get SKU details from the pre-fetched list
$sku = $skus | Where-Object { $_.SkuId -eq $skuId } | Select-Object -First 1
$skuPartNumber = if ($sku) { $sku.SkuPartNumber } else { "Unknown SKU" }
# Sort and join the assignment methods
$assignmentMethods = ($licenseAssignments[$skuId] | Sort-Object -Unique) -join ", "
# Construct a custom object to store the user's license information
$userLicenseInfo = [PSCustomObject]@{
UserId = $user.Id
UserDisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
SkuId = $skuId
SkuPartNumber = $skuPartNumber
AssignedBy = $assignmentMethods
}
# Add the user's license information to the array
$allUserLicenses += $userLicenseInfo
}
}
# Export the results to a CSV file
$path = Join-path $env:LOCALAPPDATA ("UserLicenseAssignments_" + [string](Get-Date -UFormat %Y%m%d) + ".csv")
$allUserLicenses | Export-Csv $path -Force -NoTypeInformation
# Display the location of the CSV file
Write-Host "CSV file generated at: $((Get-Item $path).FullName)"
그룹 라이선스가 있는 사용자에 대한 직접 라이선스 제거
이 스크립트의 용도는 동일한 라이선스를 그룹에서 이미 상속한(예: 그룹 기반 라이선스로 전환의 일환으로) 사용자로부터 불필요한 직접 라이선스를 제거하는 것입니다.
참고
사용자가 서비스 및 데이터에 대한 액세스 권한을 잃지 않도록 하려면 직접 할당된 라이선스가 상속된 라이선스보다 더 많은 서비스 기능을 제공하지 않는지 확인하는 것이 중요합니다. 현재 PowerShell을 사용하여 상속된 라이선스와 직접 라이선스를 통해 사용하도록 설정된 서비스를 확인할 수 없습니다. 따라서 스크립트는 그룹에서 상속된 것으로 알려진 최소 수준의 서비스를 사용하여 사용자가 예기치 않은 서비스 손실을 경험하지 않는지 확인하고 확인합니다.
변수
- $GroupLicenses: 그룹에 할당된 라이선스를 나타냅니다.
- $GroupMembers: 그룹의 멤버를 포함합니다.
- $UserLicenses: 사용자에게 직접 할당된 라이선스를 보유합니다.
- $DirectLicensesToRemove: 사용자로부터 제거해야 하는 라이선스를 저장합니다.
# Define the group ID containing the assigned license
$GroupId = "objectID of Group"
# Force all errors to be terminating errors
$ErrorActionPreference = "Stop"
# Get the group's assigned licenses
$Group = Get-MgGroup -GroupId $GroupId -Property AssignedLicenses
$GroupLicenses = $Group.AssignedLicenses.SkuId
if (-not $GroupLicenses) {
Write-Host "No licenses assigned to the specified group. Exiting script."
return
}
# Get all members of the group
$GroupMembers = Get-MgGroupMember -GroupId $GroupId -All
foreach ($User in $GroupMembers) {
$UserId = $User.Id
# Get user's assigned licenses
$UserData = Get-MgUser -UserId $UserId -Property DisplayName,Mail,UserPrincipalName,AssignedLicenses
$UserLicenses = $UserData.AssignedLicenses.SkuId
# Identify direct licenses that match the group's assigned licenses
$DirectLicensesToRemove = @()
foreach ($License in $UserLicenses) {
if ($GroupLicenses -contains $License) {
$DirectLicensesToRemove += $License
}
}
# Print user info before taking action
Write-Host ("{0,-40} {1,-25} {2,-40} {3}" -f $UserData.Id, $UserData.DisplayName, $UserData.Mail, $UserData.UserPrincipalName)
# Skip users who have no direct licenses matching the group
if ($DirectLicensesToRemove.Count -eq 0) {
Write-Host "No direct licenses to remove. (Only inherited licenses detected)"
Write-Host "------------------------------------------------------"
continue
}
# Attempt to remove direct licenses
try {
Write-Host "Removing direct license(s)..."
Set-MgUserLicense -UserId $UserId -RemoveLicenses $DirectLicensesToRemove -AddLicenses @() -ErrorAction Stop
Write-Host "✅ License(s) removed successfully."
}
catch {
$ErrorMessage = $_.Exception.Message
if ($ErrorMessage -match "User license is inherited from a group membership") {
Write-Host "⚠️ Skipping removal - License is inherited from a group."
} else {
Write-Host "❌ Unexpected error: $ErrorMessage"
}
}
Write-Host "------------------------------------------------------"
}
Write-Host "Script execution complete."
다음 단계
그룹의 라이선스를 관리할 수 있는 기능 집합에 대한 자세한 내용은 다음 문서를 참조하세요.