Licencjonowanie grupowe w Microsoft 365: diagnostyka błędów i automatyzacja przez PowerShell
Group-based licensing w M365 to jedyna skalowalna metoda zarządzania licencjami, ale błędy jak MutuallyExclusiveViolation czy brak UsageLocation pojawiają się cicho. Dowiedz się jak je diagnozować przez PowerShell Graph SDK i jak budować automatyzację, która zapobiega problemom zanim użytkownicy zadzwonią.

Licencjonowanie grupowe w Microsoft 365: diagnostyka błędów i automatyzacja przez PowerShell
TL;DR: Group-based licensing to jedyna skalowalna metoda zarządzania licencjami M365 w większych środowiskach, ale potrafi zaskoczyć — błędy takie jak
MutuallyExclusiveViolation, brakUsageLocationczy zagnieżdżone grupy powodują ciche niepowodzenia. Ten artykuł pokazuje jak diagnozować błędy przez PowerShell Graph SDK, jak je naprawiać, oraz jak uniknąć pułapek które nie są opisane wprost w dokumentacji Microsoftu.
Problem
Wyobraź sobie poniedziałkowy ranek: nowy pracownik czeka na dostęp do Teams i Exchange, a ty siedzisz nad zgłoszeniem, dlaczego jego konto nie ma licencji mimo że grup licencyjnych na pewno jest przypisana. W portalu admin.microsoft.com wszystko wygląda poprawnie. Licencje są. Grupa jest. Użytkownik jest w grupie. A mimo to — brak dostępu.
Tak właśnie objawia się cichy błąd group-based licensing (GBL). Mechanizm działa asynchronicznie w tle, błędy zapisuje na obiekcie użytkownika, ale nie wysyła żadnego powiadomienia. Jeśli nie wiesz gdzie patrzeć, możesz tracić godziny na debugowanie czegoś, co Microsoft skrzętnie ukrył za czterema warstwami menu.
Group-based licensing to funkcja Entra ID (wymaga licencji P1 lub wyżej — ale wystarczy, że masz Microsoft 365 Business Premium, E3, E5 lub EMS). Zamiast ręcznie przypisywać licencje każdemu użytkownikowi, dodajesz użytkownika do grupy bezpieczeństwa i system automatycznie przypisuje licencje zdefiniowane dla tej grupy. Idealne przy onboardingu — ale tylko gdy działa.
Problem w tym, że przetwarzanie licencji odbywa się w kolejce. Dla dużych tenantów zmiana licencji na grupie z 10 000 użytkowników może się propagować przez kilkanaście godzin. Dla małych — zwykle kilka minut. I tu zaczyna się pierwsze źródło problemów: gdy coś idzie nie tak, administrator nie wie tego momentalnie.
Dlaczego tak się dzieje
Microsoft Entra ID przetwarza licencje grupowe asynchronicznie przez wewnętrzny system kolejkowania. Kiedy dodajesz użytkownika do grupy z przypisaną licencją, system rejestruje zdarzenie i planuje przetwarzanie. Jeśli podczas przypisania licencji natrafi na problem — zapisuje błąd na obiekcie użytkownika zamiast rzucić wyjątek, który mógłby przerwać całą operację.
Skąd się biorą błędy? Główne przyczyny:
1. Konflikty planów serwisowych — niektóre plany M365 są wzajemnie wykluczające. Na przykład Microsoft 365 Phone System z pakietu E5 i oddzielnie kupiony Microsoft Teams Calling Plan mogą się kłócić z licencjami VoIP z innego pakietu. System nie przypisuje żadnej licencji z grupy, gdy natrafi na taki konflikt.
2. Brak Usage Location — Microsoft nie może przypisać licencji użytkownikowi, który nie ma ustawionego kraju/regionu, bo niektóre usługi są niedostępne w określonych lokalizacjach ze względów prawnych. W nowym koncie stworzonym bezpośrednio w Entra ID pole UsageLocation domyślnie jest puste. Podczas synchronizacji z on-premises Azure AD Connect przepisuje tę wartość — jeśli na koncie AD nie ma atrybutu msExchUsageLocation lub c (country), użytkownik trafia do chmury bez lokalizacji.
3. Zagnieżdżone grupy (nested groups) — GBL działa tylko na bezpośrednich członkach grupy. Jeśli masz grupę Licencje-E3 i dodasz do niej grupę Dział-IT (zamiast bezpośrednich użytkowników), licencje NIE zostaną przypisane użytkownikom z Dział-IT. System cicho to ignoruje.
4. Zduplikowane adresy proxy w Exchange Online — jeśli dwóch użytkowników ma przypisany ten sam alias email (co zdarza się przy migracjach), próba przypisania licencji Exchange Online do jednego z nich kończy się błędem.
Rozwiązanie krok po kroku
Krok 1: Znajdź użytkowników w stanie błędu
Zaloguj się do admin.microsoft.com → Billing → Licenses → wybierz produkt → w kolumnie Groups kliknij na grupę. Czerwony status przy użytkowniku = błąd do naprawienia.
Szybciej przez PowerShell. Najpierw połącz się z Microsoft Graph:
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All", "Organization.Read.All"
# Pobierz SKU ID dla konkretnej licencji (np. E3)
Get-MgSubscribedSku -All | Select-Object SkuPartNumber, SkuId | Sort-Object SkuPartNumber
# Znajdź użytkowników z błędami licencjonowania
$allUsers = Get-MgUser -All -Property "displayName,userPrincipalName,licenseAssignmentStates"
$usersWithErrors = $allUsers | Where-Object {
$_.LicenseAssignmentStates | Where-Object { $_.State -eq "Error" }
}
foreach ($user in $usersWithErrors) {
$errors = $user.LicenseAssignmentStates | Where-Object { $_.State -eq "Error" }
foreach ($err in $errors) {
[PSCustomObject]@{
User = $user.UserPrincipalName
Error = $err.Error
SKU = $err.SkuId
Group = $err.AssignedByGroup
}
}
} | Export-Csv -Path "licensing_errors.csv" -NoTypeInformation -Encoding UTF8
Krok 2: Rozwiąż błąd ProhibitedInUsageLocationViolation (brak UsageLocation)
To najczęstszy błąd przy nowych kontach. Naprawka jest prosta, ale żeby nie robić tego ręcznie dla 50 użytkowników:
# Ustaw UsageLocation dla wszystkich użytkowników bez tego atrybutu
$usersWithoutLocation = Get-MgUser -All -Filter "usageLocation eq null" `
-Property "id,displayName,userPrincipalName"
foreach ($user in $usersWithoutLocation) {
Update-MgUser -UserId $user.Id -UsageLocation "PL"
Write-Host "Ustawiono PL dla: $($user.UserPrincipalName)"
}
# Po ustawieniu lokalizacji — wymuś ponowne przetworzenie licencji
foreach ($user in $usersWithoutLocation) {
Invoke-MgLicenseUser -UserId $user.UserPrincipalName
}
Ważne: wartość PL to kod ISO 3166-1 alpha-2. Dla polskich firm to standardowo PL, ale jeśli masz użytkowników za granicą (np. oddział w Niemczech), musisz ustawić DE. Błędne ustawienie lokalizacji może sprawić, że niektóre usługi (np. Teams Audio Conferencing) będą niedostępne, bo Microsoft blokuje je geograficznie.
Krok 3: Zdiagnozuj i napraw MutuallyExclusiveViolation
Ten błąd pojawia się gdy dwie grupy przypisują do użytkownika plany serwisowe, które nie mogą współistnieć. Typowy scenariusz: masz grupę E3-Base z Microsoft 365 E3 i grupę VoIP-Teams z Teams Phone Standard. Jeśli E3 ma już włączony MCOSTANDARD (Skype for Business Online), a Teams Phone Standard wymaga wyłączonego MCOSTANDARD, dostajesz konflikt.
Rozwiązanie: wyłącz confliktujący plan w jednej z grup:
# Sprawdź dostępne plany serwisowe dla danej licencji
$sku = Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq "SPE_E3" }
$sku.ServicePlans | Select-Object ServicePlanName, ServicePlanId | Sort-Object ServicePlanName
# Przypisz licencję grupie z wyłączonym planem MCOSTANDARD
$skuId = (Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq "SPE_E3" }).SkuId
$mcoStandardId = "0feaeb32-d00e-4d66-bd5a-43b5b83db82c" # MCOSTANDARD GUID
Set-MgGroupLicense -GroupId "<GUID_grupy>" `
-AddLicenses @{
SkuId = $skuId
DisabledPlans = @($mcoStandardId)
} `
-RemoveLicenses @()
Krok 4: Wymuś ponowne przetwarzanie po naprawce
Po usunięciu przyczyny błędu system nie zawsze automatycznie przypisuje licencję. Możesz poczekać (zwykle do 24h przy małych tenantach) lub wymusić ręcznie:
# Wymuś reprocessing dla konkretnego użytkownika
Invoke-MgLicenseUser -UserId "jan.kowalski@firma.pl"
# Dla wielu użytkowników naraz
$errorUsers = @("jan.kowalski@firma.pl", "anna.nowak@firma.pl")
foreach ($upn in $errorUsers) {
Invoke-MgLicenseUser -UserId $upn
Write-Host "Reprocess wywołany dla: $upn"
}
Krok 5: Sprawdź zduplikowane adresy proxy (Exchange Online)
# Wymagany moduł ExchangeOnlineManagement
Connect-ExchangeOnline
# Znajdź zduplikowany alias
Get-Recipient -Filter "EmailAddresses -eq duplikat@firma.onmicrosoft.com" |
Select-Object DisplayName, RecipientType, EmailAddresses
Jeśli Get-Recipient zwróci dwa obiekty z tym samym adresem — musisz usunąć duplikat z jednego z nich zanim licencja Exchange Online zostanie przypisana.
Krok 6: Zbuduj raport stanu licencji grupowych
Dla regularnego monitoringu — skrypt generujący CSV ze statusem wszystkich przypisań grupowych:
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All"
$allUsers = Get-MgUser -All -Property "displayName,userPrincipalName,licenseAssignmentStates,usageLocation"
$report = @()
foreach ($user in $allUsers) {
$groupAssignments = $user.LicenseAssignmentStates | Where-Object { $_.AssignedByGroup -ne $null }
foreach ($assignment in $groupAssignments) {
$groupName = (Get-MgGroup -GroupId $assignment.AssignedByGroup -ErrorAction SilentlyContinue).DisplayName
$report += [PSCustomObject]@{
User = $user.UserPrincipalName
UsageLocation = $user.UsageLocation
Group = $groupName
SkuId = $assignment.SkuId
State = $assignment.State
Error = $assignment.Error
}
}
}
$report | Export-Csv -Path "gbl_report_$(Get-Date -Format yyyyMMdd).csv" -NoTypeInformation -Encoding UTF8
Typowe pułapki i edge cases
Pułapka 1: Kasowanie grupy z przypisaną licencją
Nie można usunąć grupy, która ma aktywne przypisanie licencji — portal pokaże błąd, PowerShell też rzuci wyjątek. To celowe zabezpieczenie Microsoftu. Sekwencja: najpierw Set-MgGroupLicense -RemoveLicenses @(skuId), poczekaj na propagację (kilka minut do godziny), dopiero potem usuń grupę.
Dodatkowa pułapka: jeśli po usunięciu licencji z grupy użytkownik miał DependencyViolation — czyli inna jego licencja (przypisana inną drogą) zależała od tej — po usunięciu grupy ta zależna licencja wchodzi w stan błędu. Musisz ją osobno naprawić przez Invoke-MgLicenseUser.
Pułapka 2: Nowe usługi Microsoft dodawane do istniejących pakietów
Microsoft regularnie dodaje nowe plany serwisowe do istniejących SKU. Np. gdy Microsoft dodał Microsoft Loop do pakietu E3, wszystkie grupy z E3 automatycznie dostały nową usługę włączoną dla wszystkich użytkowników. Jeśli masz grupę E3-Exchange-Only skonfigurowaną tak, żeby włączyć wyłącznie Exchange Online — po takim update Microsoft Loop i inne nowe usługi zostaną automatycznie włączone dla członków tej grupy.
Rozwiązanie: skonfiguruj subskrypcję na powiadomienia o zmianach produktów w centrum administracyjnym M365 (Settings → Org settings → Services → Message center) i po każdej zmianie zweryfikuj DisabledPlans na grupach.
Pułapka 3: Dynamiczne grupy i zmiana reguł członkostwa
Zmiana reguły dynamicznej grupy licencyjnej to operacja o poważnych konsekwencjach. Gdy zmienisz regułę, Entra ID usuwa WSZYSTKICH obecnych członków, a następnie przetwarza nową regułę. W czasie tej operacji użytkownicy tracą licencje — a tym samym dostęp do Exchange, Teams, SharePoint.
W środowisku produkcyjnym schemat postępowania: stwórz nową grupę z nową regułą, przypisz licencje, poczekaj aż propagacja zakończy się w 100%, dopiero wtedy usuń starą grupę. Nigdy nie modyfikuj reguły na grupie, która jest jedynym źródłem licencji dla użytkowników produkcyjnych.
Pułapka 4: Zagnieżdżone grupy — cisza jako odpowiedź
To jeden z tych błędów, po których nie ma żadnego komunikatu. Jeśli dodasz grupę jako członka grupy licencyjnej, Microsoft Entra ID po prostu zignoruje zagnieżdżoną grupę — bez błędu, bez ostrzeżenia w logach. Użytkownicy z zagnieżdżonej grupy po prostu nie otrzymają licencji.
Diagnoza: w Microsoft Graph możesz sprawdzić bezpośrednich vs. transitive members:
# Tylko bezpośredni członkowie (ci, którzy dostają licencje)
Get-MgGroupMember -GroupId "<GUID>" | Select-Object Id, "@odata.type"
# Wszyscy transitive (włącznie z zagnieżdżonymi) - CI NIE DOSTAJĄ LICENCJI
Get-MgGroupTransitiveMember -GroupId "<GUID>" | Select-Object Id, "@odata.type"
Jeśli różnica między tymi dwoma zestawami jest duża — masz zagnieżdżone grupy i użytkowników bez licencji, którzy nie wiedzą dlaczego.
Pułapka 5: LicenseAssignmentAttributeConcurrencyException
Błąd pojawia się gdy użytkownik jest członkiem dwóch grup, które przypisują tę samą licencję — i obie grupy przetwarzają zmiany jednocześnie. Microsoft automatycznie ponawia próbę, więc zwykle rozwiązuje się sam. Jeśli błąd utrzymuje się ponad 24h — usuń użytkownika z jednej z duplikujących grup.
Jak my to rozwiązujemy w Evolit
W Evolit obsługujemy onboarding pracowników przez Nexmę, która automatyzuje cały flow — od złożenia wniosku przez przełożonego, przez zatwierdzenie HR, aż po dodanie użytkownika do odpowiednich grup w Entra ID. Oznacza to, że konto trafia do grupy licencyjnej dopiero po kompletnym wypełnieniu profilu użytkownika — w tym pola UsageLocation — co eliminuje najczęstszy błąd ProhibitedInUsageLocationViolation.
Gdybyś chciał zrobić to bez Nexmy, możesz wymusić sprawdzenie przed dodaniem do grupy skryptem PowerShell sprawdzającym UsageLocation przed wywołaniem New-MgGroupMember. Ważne jest żeby tę weryfikację wbudować w pipeline onboardingowy, a nie zostawić jako krok manualny.
Więcej o tym jak Nexma obsługuje licencjonowanie M365 możesz przeczytać na nexma.app.
Podsumowanie
- Błędy GBL są ciche — nie dostajesz powiadomień, musisz aktywnie monitorować przez PowerShell lub portal (Billing → Licenses → Group)
ProhibitedInUsageLocationViolationto najczęstszy błąd — ustawUsageLocationna koncie użytkownika zanim dodasz go do grupy licencyjnej- Zagnieżdżone grupy nie działają — GBL przetwarza tylko bezpośrednich członków, bez żadnego błędu w logach
- Zmiana reguły dynamicznej grupy licencyjnej usuwa wszystkich członków — zawsze testuj na nowej grupie
- Po naprawie błędu często trzeba ręcznie wywołać
Invoke-MgLicenseUser— system nie zawsze ponawia automatycznie - Azure AD PowerShell i MSOnline są deprecated od marca 2024 — używaj wyłącznie Microsoft Graph PowerShell SDK (
Connect-MgGraph)