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"
…