Nagy csoporttagságú csoport tagjainak lekérdezése

Korábban, amíg csak laborkörnyezetben foglalkoztam Active Directory-val, soha nem találkoztam azzal a problémával, hogy mi az, hogy nagy csoport. Ha sikerült egy szkripttel 3-400 felhasználót felvennem, akkor az már kellően soknak tűnt. Mígnem a mostani munkahelyemen egyszer csak azt a feladatot kaptam, hogy listázzam ki egy csoport tagságát. Amikor megírtam az egyszerű kis szkriptemet (nem lehetett használnom semmilyen AD modult vagy snapint) meglepetten tapasztaltam, hogy a csoportom pont 1500 tagú. És gyanús volt, hogy nem jött elő az a tag, akit viszont mondtak, hogy biztos benne van. Kis utána olvasással kiderült számomra, hogy az AD önvédelemből a lekérdezésekre alaphelyzetben csak 1500 elemet ad vissza válaszként. Bár ezt átkonfigurálhatjuk magasabb értékre, de mekkorára emeljük? Bármekkorára is emeljük, tudnánk annál magasabb számot is mondani, amire nem működne megint a dolog. Így inkább oldjuk meg azt, hogy több darabban olvassuk ki a tagságot, de hogyan lehet ezt? Az alapszintű AD tanfolyamokon ez sajnos nem szerepel.

Nézzük tehát, hogyan lehet a nagy csoport tagságát felmérni! A kiindulás egy egyszerű AD kereső objektumon alapszik:

PS C:\> $group = "nagycsoport"

PS C:\> $objRoot = [ADSI] "LDAP://DC=R2,DC=dom"

PS C:\> $objSearcher = New-Object System.DirectoryServices.DirectorySearcher

PS C:\> $objSearcher.SearchRoot = $objRoot

PS C:\> $objSearcher.Filter = "(&(objectCategory=group)(|(SAMAccountName=$group

)(distinguishedName=$group)))"

PS C:\> $objSearcher.SearchScope = "Subtree"

PS C:\> $colProplist = "distinguishedName"

PS C:\> $objSearcher.PropertiesToLoad.Add($colPropList) > $null

PS C:\> $o = $objSearcher.FindAll()

PS C:\> $groupObj = $o[0].getdirectoryentry()

PS C:\> $groupObj.member.count

1500

Az utolsó sorokban látjuk, hogy 1500 tagot számlálunk, pedig ennél több van a nagy csoportomban. A további elemeket egy egész furcsa szintaxissal lehet lekérdezni:

PS C:\> $ds = New-Object DirectoryServices.DirectorySearcher($GroupObj,"(object

Class=*)", "member;range=1500-1510",'Base')

PS C:\> $ds.findall()[0]

 

Path                                    Properties

----                                    ----------

LDAP://CN=NagyCsoport,OU=Dél,DC=r2,D... {adspath, member;range=1500-1510}

 

 

PS C:\> $ds.findall()[0].properties."member;range=1500-1510"

CN=Máriássy Eszter,OU=Kelet,DC=r2,DC=dom

CN=Benedek Ábel,OU=Kelet,DC=r2,DC=dom

CN=Réti Dóri,OU=Dél,DC=r2,DC=dom

CN=Badár Károly,OU=Észak,DC=r2,DC=dom

CN=Kangiszer Orsolya,OU=Dél,DC=r2,DC=dom

CN=Jakus Piroska,OU=Dél,DC=r2,DC=dom

CN=Osváth Júlianna,OU=Észak,DC=r2,DC=dom

CN=Máriássy Cecília,OU=Észak,DC=r2,DC=dom

CN=Kun Virág,OU=Kelet,DC=r2,DC=dom

CN=Bernyák Ákos,OU=Észak,DC=r2,DC=dom

CN=Klinkó Rezső,OU=Dél,DC=r2,DC=dom

Definiáltam egy másik keresőt, aminek már célzottan a kifejtendő csoportot adom át paraméterként, nem szűkítve le a találatot semmilyen osztályra, és itt jön az érdekes, a tagság tulajdonság egészen furcsa módon szerepel, mint lekérdezendő attribútum: "member;range=1500-1510"! Azaz ott van az a tól-ig határ, ahányadik adatot kérjük. A visszakapott eredmény properties."member;range=1500-1510" tulajdonságában vannak a tényleges tagok.

Külön érdekesség, hogy vajon honnan tudjuk, hogy már nincs több tagunk? Ez is elég érdekesen néz ki:

PS C:\> $ds.findall()[0].properties."member;range=4300-5000"

PS C:\> $ds.findall()[0].properties

 

Name                           Value

----                           -----

adspath                        {LDAP://CN=NagyCsoport,OU=Dél,DC=r2,DC=dom}

member;range=4300-*            {CN=Minárovits Ágnes,OU=Kelet,DC=r2,DC=dom, ...

Láthatólag ilyenkor a visszakapott tulajdonságnévben a felső határnál nem az általunk megadott szám, hanem egy „*” áll. Innen tudhatjuk, hogy nem érdemes egy következő kört futni, mert már túllőttünk bohó célunkon.

Mindezek figyelembevételével készítettem egy szkriptet, amellyel fájlba lehet exportálni a csoporttagokat, ráadásul az egyes tagokat „objektumosítja”, azaz kiolvassa a tényleges felhasználói objektum megadott tulajdonságait is:

param (

    $group,

    $outputpath = "$env:TEMP\membership-$group.csv",

    $AttributeList=@("Name","canonicalName","dNSHostName", "distinguishedname"),

    [switch] $open

)

 

# Find the group in AD

$objRoot = [ADSI] "LDAP://DC=R2,DC=dom"

$objSearcher = New-Object System.DirectoryServices.DirectorySearcher

$objSearcher.SearchRoot = $objRoot

$objSearcher.Filter = "(&(objectCategory=group)(|(SAMAccountName=$group)(distinguishedName=$group)))"

$objSearcher.SearchScope = "Subtree"

$colProplist = "distinguishedName"

$objSearcher.PropertiesToLoad.Add($colPropList) > $null

$o = $objSearcher.FindAll()

 

$groupObj = $o[0].getdirectoryentry()

 

# Retrieve large group membership in batches

$from = 0

$all = $false

$members = @()

$step = 1500

while (! $all) { 

    $to = $from + $step-1

    Write-Host "Retrieving members between index $from and $to..."

    $DS =

    New-Object DirectoryServices.DirectorySearcher($GroupObj,"(objectClass=*)",

"member;range=$from-$to",'Base')

    $newmembersobj = $ds.findall()[0]

    $ending = $newmembersobj.Properties.PropertyNames -like "member;*" |

      %{if($_ -match "member;range=\d+-(.+)"){$matches[1]}

       }

      $members += $newmembersobj.Properties.item($Matches[0])

      $from += $step

      if($ending -eq "*"){$all = $true}

}

 

Write-Host "Retrieved $($members.count) group members."

 

$objSearcher.SearchScope = "base"

$objSearcher.Filter = "(objectClass=*)"

$objSearcher.PropertiesToLoad.clear()

foreach ($i in $AttributeList){$objSearcher.PropertiesToLoad.Add($i) > $null}

 

Write-Host "Populating attributes '$attributelist' and outputting into CSV file..."

$members | %{

    $objSearcher.searchroot = [ADSI]"LDAP://$_"

    $curr = $objSearcher.FindAll()

    $obj = New-Object -TypeName PSObject

    $curr.PropertiesLoaded | %{

        if($curr[0].properties.item($_).count -le 1){

            $obj | Add-Member -MemberType noteproperty -Name $_ -Value $curr[0].properties.item($_)[0]

        }

        else{

            $obj | Add-Member -MemberType noteproperty -Name $_ -Value $curr[0].properties.item($_)

        }

    }

    $obj

} | Select-Object -Property $AttributeList | Export-Csv -Path $outputpath -Encoding Unicode -UseCulture -NoTypeInformation

if($open){ Invoke-Item $outputpath}

Ezt, ha meghívjuk, a következő kimenetet kapjuk:

PS C:\Munka> .\get-largemembership.ps1 -group nagycsoport -outputpath C:\Munka\

nagycsoport.csv

Retrieving members between index 0 and 1499...

Retrieving members between index 1500 and 2999...

Retrieving members between index 3000 and 4499...

Retrieved 4304 group members.

Populating attributes 'Name canonicalName dNSHostName distinguishedname' and ou

tputting into CSV file...

Meg persze a CSV fájlt:

PS C:\Munka> Get-Content C:\Munka\nagycsoport.csv

"Name";"canonicalName";"dNSHostName";"distinguishedname"

"Szilágyi Flóra";"r2.dom/Észak/Szilágyi Flóra";;"CN=Szilágyi Flóra,OU=Észak,DC

=r2,DC=dom"

"Kun Gábor";"r2.dom/Észak/Kun Gábor";;"CN=Kun Gábor,OU=Észak,DC=r2,DC=dom"

"Jakus Előd";"r2.dom/Észak/Jakus Előd";;"CN=Jakus Előd,OU=Észak,DC=r2,DC=dom"

"Hutai Ábel";"r2.dom/Észak/Hutai Ábel";;"CN=Hutai Ábel,OU=Észak,DC=r2,DC=dom"



Word To HTML Converter