Jak znaleźć nieaktywne konta Guest w Entra ID
Konta gościnne to rzecz, która najłatwiej w tenancie narasta i najtrudniej się ją sprząta. Ktoś zaprasza agencję do jednej kampanii, konsultanta do jednej migracji, audytora na tydzień w marcu. Projekt się kończy, zaproszenie nie. Dwa lata później w katalogu siedzi kilkaset zewnętrznych tożsamości i nikt nie potrafi powiedzieć, które z nich nadal potrzebują dostępu.
Część mechaniczna — wylistowanie gości i ich ostatnich logowań — zajmuje jakieś pięć minut. Problemy zaczynają się przy ocenie, więc zanim cokolwiek wyłączysz, warto zrozumieć, co te dane właściwie mówią.
Co Entra ID naprawdę Ci pokazuje
Każdy obiekt użytkownika w Microsoft Graph ma właściwość signInActivity, a w niej między innymi lastSignInDateTime i lastNonInteractiveSignInDateTime. Do jej odczytania potrzebne jest uprawnienie AuditLog.Read.All obok User.Read.All — szczegół, który wykłada większość pierwszych podejść, bo bez niego właściwość po prostu wraca pusta, zamiast rzucić czytelnym błędem.
Dwa zastrzeżenia ważą więcej niż sama składnia:
- Dane o logowaniach zależą od licencji. Pełne raportowanie logowań to funkcja płatna — na tenancie bez Entra ID P1 lub P2 możesz nie dostać nic. Puste
signInActivitynie jest więc dowodem na to, że konto jest nieaktywne. Jest dowodem na brak danych. - Logowanie interaktywne to nie cała historia. Konto może wyglądać na uśpione w
lastSignInDateTime, podczas gdylastNonInteractiveSignInDateTimepokazuje regularne odświeżanie tokenów przez klienta, który nikogo o nic nie pyta. Patrz na oba pola.
Pobranie listy
Poniższy fragment czyta tenanta i zapisuje CSV. Niczego nie zmienia:
Connect-MgGraph -Scopes 'User.Read.All','AuditLog.Read.All','Group.Read.All'
$guests = Get-MgUser -All -Filter "userType eq 'Guest'" -Property @(
'id','displayName','userPrincipalName','createdDateTime',
'accountEnabled','externalUserState','signInActivity'
)
$guests | Select-Object DisplayName, UserPrincipalName, AccountEnabled, ExternalUserState,
@{ n = 'LastSignIn'; e = { $_.SignInActivity.LastSignInDateTime } },
@{ n = 'LastNonInter'; e = { $_.SignInActivity.LastNonInteractiveSignInDateTime } },
@{ n = 'DaysInactive'; e = {
if ($_.SignInActivity.LastSignInDateTime) {
[int](New-TimeSpan -Start $_.SignInActivity.LastSignInDateTime -End (Get-Date)).TotalDays
}
} } |
Sort-Object DaysInactive -Descending |
Export-Csv .\guests.csv -NoTypeInformation -Encoding UTF8
Jeśli Twój tenant odrzuci to połączenie -Filter z signInActivity, pomiń filtr i wybierz konta po stronie PowerShella przez $_.UserType -eq 'Guest'. Na dużym katalogu jest wolniej, ale działa zawsze.
Dlaczego „rok bez logowania" to nie wyrok
Liczba dni jest przesłanką do decyzji, a nie decyzją. Zanim uznasz konto gościnne za zbędne, trzy grupy zasługują na drugie spojrzenie:
- Rzadko, ale celowo. Audytorzy zewnętrzni, członkowie rady, awaryjne konta partnerskie — używane raz w roku, świadomie, i boleśnie brakujące dokładnie w najgorszym momencie.
- Nieprzyjęte zaproszenia. Konto z
externalUserStaterównymPendingAcceptancenigdy się nie zalogowało z definicji. Ono nie jest nieaktywne — ono nigdy nie zostało aktywowane. To inny problem i inne rozwiązanie. - Goście z rolami. Zewnętrznej tożsamości z przypisaną rolą administracyjną nie usuwa się przy okazji rutynowego sprzątania. Takie konta znajdź najpierw i potraktuj indywidualnie.
Drugim sygnałem, który warto pobrać, jest członkostwo w grupach. Gość należący wyłącznie do domyślnej grupy All Guest Users to zupełnie inna sytuacja niż taki, który siedzi w grupie dającej dostęp do działającej aplikacji.
Kolejność, która wytrzymuje zderzenie z rzeczywistością
Bezpieczny wzorzec to nie „skanuj i usuwaj", tylko:
- Raport. Przygotuj listę i pokaż ją osobom, które odpowiadają za relację — kierownicy projektów wiedzą rzeczy, których katalog nie wie.
- Blokada. Wyłącz konto, zamiast je usuwać. Wyłączenie jest natychmiast odwracalne i samo wyławia konta, które komuś jednak były potrzebne: reklamacja przychodzi w ciągu tygodnia.
- Czekanie. Daj okres karencji. Trzydzieści dni łapie procesy miesięczne, kwartał łapie kwartalne.
- Usunięcie. Dopiero wtedy i tylko dla kont, które przez całe okno milczały.
Jednej rzeczy Entra ID za Ciebie nie zrobi: nie zapisuje, kiedy konto zostało wyłączone. Katalog wie, że konto jest zablokowane, a nie od kiedy. Jeśli Twój okres karencji ma cokolwiek znaczyć, tę datę musisz śledzić sam — w pliku, w arkuszu albo w atrybucie rozszerzenia na koncie.
Zabezpiecz wyjątki systemowo
W każdym tenancie są konta, których nie wolno tknąć, a pamiętanie o nich nie jest strategią. Zapisz wyjątek zamiast go pamiętać: dedykowana grupa wykluczeń albo atrybut rozszerzenia w rodzaju extensionAttribute15 = DoNotDelete ustawiony na koncie. Reguła staje się wtedy mechaniczna, przeżywa osobę, która ją wymyśliła, i działa tak samo przy dziesiątym sprzątaniu jak przy pierwszym.
Usunięcie jest mniej ostateczne, niż się wydaje
Usunięcie użytkownika w Entra ID to usunięcie miękkie. Konto trafia do kosza i pozostaje odzyskiwalne przez 30 dni, z portalu albo poleceniem Restore-MgDirectoryDeletedItem. To realna siatka bezpieczeństwa — ale trzydziestodniowa i nie przywracająca wszystkiego idealnie, więc powinna być zabezpieczeniem, a nie planem.
Kiedy robisz to cyklicznie
Powyższy skrypt wystarcza na jeden raz. Kłopot zaczyna się przy drugim przebiegu, kiedy trzeba pamiętać, które konta zablokowałeś w zeszłym kwartale, które są w okresie karencji, które są wyłączone z reguł, a które wróciły do życia. Ten stan nie mieszka w katalogu, a arkusz kalkulacyjny starzeje się źle.
Tę lukę wypełnia Guest Account Cleanup: ten sam skan tylko do odczytu, plus daty blokad, reguły wykluczeń i arytmetyka okresu karencji trzymane w jednym miejscu — z trybem próbnym włączonym domyślnie, więc nic się nie zmienia, dopóki sam nie zdecydujesz.
Sprzątasz katalog, który odziedziczyłeś?
Napisz, z iloma kontami gościnnymi mniej więcej masz do czynienia, a przeprowadzimy Cię przez to. Sam skan jest tylko do odczytu.
Skontaktuj się