nieaktywni użytkownicy EntraID
w AD sprawa była prosta… no może nie tak bardzo prosta, bo historia atrybutów lastLogon i lastLogonTimeStamp też ma swoje drugie dno, ale od wielu lat wiadomo jak aktywność użytkownika zbadać… i pojawiła się Chmura i hybryda, która całość skomplikowała…
przez wiele lat nie było prostego sposobu na zapytanie 'wyszukaj nieużywane konta’, ponieważ nie było w EntraID odpowiednika lastLogonTimeStamp i trzeba było każdy system badać na dziwne sposoby. wedle Microsoft, należało odpytać logu audytu, który miał standardowo 3o dni.. wielce przydatne. nie drążąc dalej historii, od kwietnia 2o2o GraphAPI produkcyjnie obsługuje ’signInActivity’ – w końcu atrybut/zasób, który jest automatycznie aktualizowany ostatnią datą uwierzytelnienia.
uwaga. atrybut może mieć do 6h opóźnienia w aktualizacji
jak odpytać
kilka przykładów jak przygotować sobie raport, korzystając z commandletów Microsoft.Graph
wyświetlenie wszystkich użytkowników, wraz z ich mailem oraz datą ostatniej aktywności:
Get-MgUser -Property displayname,mail,signInActivity -all| select displayname,mail,@{L='signin';E={$_.SignInActivity.LastSignInDateTime}}
korzystanie z commandletów graphowych nie jest przyjemne i wymaga cierpliwości. większość operacji, ze względu na optymalizację, nie wyświetla prawie żadnych atrybutów – dla tego trzeba wyraźnie zaznaczyć w 'property’ czego będzie się potrzebowało.
trochę bardziej złożony raport, wyrzucany na csv, zawierający dodatkowo informacje o licencjach:
Get-MgUser -Property id,displayname,givenname,surname,accountenabled,userprincipalname,mail,signInActivity,userType -all| select id,displayname,givenname,surname,accountenabled,userprincipalname,userType,mail,` @{L='signin';E={$_.SignInActivity.LastSignInDateTime}},` @{L='licenses';E={(Get-MgUserLicenseDetail -UserId $_.id).SkuPartNumber -join ','}}| export-csv -nti c:\temp\EntraUsers.csv -Encoding utf8
co mnie denerwuje w tych commandletach, to że nie są przyjmują w prosty sposób obiektów, np to nie zadziała:
get-mgUser -all|get-mgUserLicenseDetail
to było abecadło PS, ale wzięli się za to developerzy webowi i taki efekt… trzeba też zawsze pamiętać o dodaniu 'all’ bo standardowo jest paging na 1oo obiektów.
i jeszcze drobny przykład na użycie filtra. nie jest co prawda widoczne w helpie od get-mgUser ale to jest filtr OData – z którego ogólnie korzysta cały Graph. tutaj prosty filtr, żeby wyświetlić tylko konta typu guest. nie podjąłem się filtrowania po dacie w OData bo to by kosztowało mnie za dużo czasu na rozkminkę – operacje na datach nie są oczywiste, a PowerShell sobie świetnie z tym radzi:
Get-MgUser -Filter "userType eq 'guest'" -Property displayname,usertype,signinactivity| ?{$_.SignInActivity.LastSignInDateTime -lt (get-date).AddMonths(-2)}| select displayname,usertype,@{L='activity';E={($_|select -ExpandProperty SignInActivity).LastSignInDateTime}}
connect
oczywiście żeby korzystać z commandletów graph trzeba się połaczyć z odpowiednim zakresem (scope). jeśli to jest dla ciebie nadal problemem to koniecznie wrzuć do bookmarków ten link, który opisuje jak użyć Find-MgGraphCommand żeby sprawdzić niezbędne zakresy.
jak Microsoft potrafi strzelić klientom w kolano
ciekawostką jest, że żeby być w stanie odczytać ten atrybut, trzeba mieć minimum EntraID P1 – inaczej wyskoczy błąd:
Get-MgUser_List: Neither tenant is B2C or tenant doesn't have premium license Status: 403 (Forbidden) ErrorCode: Authentication_RequestFromNonPremiumTenantOrB2CTenant
…dlaczego? nie chodzi mi o techniczne wyjaśnienie, ale to krzyk w stronę MS – dla czego tak podstawowa funkcjonalność, jak wyszukiwanie nieaktywnych kont, wymaga dodatkowej licencji?
eN.
Classic Administrator – porządki i źle wyświetlane przypisanie
intro
kolejny brzegowy przypadek. tym razem mógł mnie kosztować usunięcie istniejących subskrypcji – a więc 'niuans, który może zabić’.
scenariusz to clean up subskrypcji. jest ich kilkaset, więc trzeba podejść do tematu systemowo. najpierw zajmę się omówieniem scenariusza i moim podejściem do sprzątania, a jak kogoś interesuje sam bug w Azure – to będzie na końcu.
clean up process
w pierwszej kolejności trzeba wyizolować osierocone i puste subskrypcje. o tym jak znaleźć te puste szybko, czyli KQLem – za niedługo, wraz z małym qrsem KQL. dziś o tych 'osieroconych’.
duża część subskrypcji zakładana jest na chwilę, głównie przez developerów, którzy przychodzą i odchodzą…. a subskrypcje zostają. warto więc znaleźć te, których właściciele już nie istnieją w AAD.
zadanie wygląda więc tak – zrobić listing wszystkich subskrypcji wraz z osobami które są Ownerami w ARM IAM oraz Classic Administrators – czyli role Service Administrator oraz Co-Administrator. posłużą zarówno jako lista kontaktowa, oraz pozwolą na kolejne odpytania, żeby wyłuskać te konta, które już nie istnieją. subskrypcje bez właścicieli to najlepsze kandydatki do podróży do śmietnika. posłuży do tego taki prosty skrypcik, napisany na kolanie:
load-CSV to kolejna funkcja z eNlib – jedna z najczęściej przeze mnie używanych. ładuje CSV z automatyczną detekcją znaq oddzielającego, co jest mega istotne jak się pracuje w różnych regionalsach. ma też kilka innych bajerów… ale nie o tym tu. dalej już z górki – wyłusqję SignInNames unikalne i odpytuję AAD o użytkownika. ponieważ brak użytkownika nie jest błędem, a nullem, więc nie można użyć try/catch a trzeba wykorzystać IFa.
i wydawałoby się że mam już to, co potrzeba – wrzucam wynik do excela, robię XLOOKUP i pięknie widać które subskrypcje należą do kont, których już wśród nas nie ma… zabrzmiało złowieszczo khekhe…
Błąd w Azure
…chyba, żeby nie. całe szczęście natchnęło mnie, żeby jednak kilka posprawdzać ręcznie. i nagle okazało się, że konta których nie ma… czasem jednak są. a wszystko rozbija się o 'Classic Administrators’, który musi wewnętrznie źle komunikować się z ARMem. wiele staje się jaśniejsze, jeśli przyjrzeć się jak takie obiekty są listowane przy 'get-AzRoleAssigment -includeClassic’:
na pierwszy rzut okiem może wydawać się, że jest ok… ale gdzie jest 'ObjectId’? to jednak tylko pierwsza część problemu. porządki mają to do siebie, że robi się je rzadko. a to oznacza bagaż historii. a w tym bagażu np. projekt standaryzacji nazw użytkowników i zmiana UPNów z gsurname@domain.name na givenname.surname@domain.name. okazuje się, że ten staruszek nie ma mechanizmu odświeżania i pomimo zmiany UPN, czyli de facto SignInName, tutaj pokazuje starą nazwę. a więc przy odpytaniu AAD – nie znajduje usera, pomimo, że ten istnieje.
co ciekawe, jeśli wejdzie się do uprawnień IAM w portalu Azure, każdy user jest aktywnym linkiem, pozwalającym na kliknięcie i przeniesienie do AAD, na szczegóły konta. tak z ciekawości spróbujcie to zrobić dla Classic Administrators…
całe szczęście projekt był zrobiony z głową i stare UPNy zostały jako aliasy mailowe, a więc doprecyzowałem zapytanie:
ale co mnie to czasu i nerwów kosztowało…
eN.