Stronicowane przeglądanie AD za pomocą PS

Ostatnio opisałem jak uzyskać dostęp poszczególnych obiektów bądź grup obiektów w AD za pomocą PowerShella. Dziś pokażę prawie to samo, a jednak zupełnie inaczej. Wyobraźmy sobie sytuację, iż mamy kilkadziesiąt czy nawet kilkaset obiektów w domenie i chcemy pobrać informacje o nich. Niby sprawa wygląda prosto, niemniej jednak przy takiej ilości obiektów możemy natrafić na efekt uboczny w postaci komunikatu „Sizelimit exceeded”. Aby tego uniknąć trzeba już skorzystać z obiektów opartych o klasy .NET wraz z użyciem paginacji podczas pobierania danych.

Aby móc w ogóle skorzystać z naszych klas konieczne jest załadowanie odpowiedniej biblioteki:

[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols") | Out-Null

A następnie tworzymy obiekt Connectora do AD, gdzie podczas wywoływania konstruktora wskarzemy adres jednego z naszych kontrolerów domeny:

$DC = ([ADSI]"LDAP://rootDSE").dnsHostName.value
 $Connection = New-Object System.DirectoryServices.Protocols.LdapConnection($DC)

Następnie tworzymy obiekt wyszukiwania I podajemy kilka podstawowych informacji o metodzie wyszukiwania:

$SearchRequest = New-Object System.DirectoryServices.Protocols.SearchRequest
 $LDAP = ([ADSI]'LDAP://rootDSE').defaultNamingContext.value
 $Filter = "(&(objectCategory=person)(objectClass=user))"
 $SearchRequest.DistinguishedName = $LDAP
 $SearchRequest.Filter = $Filter
 $SearchRequest.SizeLimit = [int]::MaxValue
 $SearchRequest.Scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree

Ważne jest aby na tym etapie zdefiniować jakiego typu dane chcemy otrzymać jako wynik, tzn. jakie własności obiektów AD chcemy zobaczyć.

$Attribute = @()
 $Attribute += "displayName"
 $Attribute += "whenCreated"
 $Attribute | %{$SearchRequest.Attributes.Add($_) | Out-Null }

Następnie Tworzymy dwa kolejne obiekty odpowiadające właśnie za paginację oraz zakres przeszykiwania.

$PageLimit = 5 # nasz limit na stronę
$PageResultControl = New-Object System.DirectoryServices.Protocols.PageResultRequestControl($PageLimit)
$SearchRequest.Controls.Add($pageResultControl) | Out-Null
$SearchOptionsControl = New-Object System.DirectoryServices.Protocols.SearchOptionsControl([System.DirectoryServices.Protocols.SearchOption]::DomainScope)
$searchRequest.Controls.Add($SearchOptionsControl) | Out-Null

Ostatecznie uruchamiamy w pętli wysyłanie I pobieranie poszczególnych partii danych naszego zapytania.

$ADObjects = @()
 Do
 {
 $SearchResponse = $Connection.SendRequest($SearchRequest)
 $SearchResponse.Controls | ForEach {$PageResultControl.Cookie = $_.Cookie}
 $ADObjects += $SearchResponse.Entries
 #W produkcji powinniśmy usunąć poniższą linię. Wstawiłęm ją tylko w celu zaprezentowania, iż faktycznie dane są odbierane z kontrolera domeny partiami
 $ADObjects += ""
}
While ($PageResultControl.Cookie.length -gt 0)

Niestety drobnym utrudnieniem tej metody jest iż atrybuty obiektów z domeny są zwracane w formie binarnej i wymagane jest przekonwertowanie ich (Choć większość PowerShell potrafi zrobić dynamicznie).

$ADObjects | select @{l="displayname";e={$_.Attributes.item('displayname')[0]}}, @{l="whencreated";e={([DateTime]::ParseExact($_.Attributes.item('whencreated')[0],"yyyyMMddHHmmss.f'Z'",[System.Globalization.CultureInfo]::InvariantCulture)).ToLocalTime()}} -first 1

Reklamy

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s