daabm 1.354 Geschrieben 14. Februar Melden Teilen Geschrieben 14. Februar (bearbeitet) Hallo zusammen. Keine Frage, nur ein Tip, wenn's mal schneller gehen soll Wenn man in Active Directory User/Gruppen/Mitgliedschaften auslesen oder bearbeiten will, findet man oft Schleifen, die über Mitglieder oder Mitgliedschaften iterieren und mit den Ergebnissen dann wieder etwas machen. Das läßt sich dramatisch beschleunigen, indem man nicht iteriert, sondern vorher einen passenden LDAP-Filter baut. Und LDAP-Filter dürfen lang sein - sehr lang. Ich hatte das Thema kürzlich, als es um Account-Dubletten im Rahmen von Domänen-Migrationen ging. Das "übliche" Vorgehen wäre wohl, auf der einen Seite alle Accounts zu holen und dann für jeden Account zu prüfen, ob es den auch auf der anderen Seite gibt. Holt man die Accounts der anderen Seite dagegen über einen LDAP-Filter auf die Accounts, die man auf der ersten Seite schon gefunden hat (nur die können ja doppelt vorhanden sein), wird das dramatisch schneller... Getestet mit bis zu 2.000 sAMAccountName im LDAP-Filter, geht problemlos. Die einzige Herausforderung dabei ist, den Input (also das Member- oder MemberOf-Array) passend zu zerlegen. Ich habe mich für die einfachste Variante entschieden, und ich setze auch voraus, daß der CN eindeutig ist. Aber egal wie aufwändig das Konstruieren des LDAP-Filter wird - es wird immer um Größenordnungen schneller gehen als die Objekte einzeln zu holen. Sample Code - $GroupName muss natürlich angepasst werden, wenn das jemand ausprobieren will: $GroupName = 'LDAPFilterGroup' $Group = Get-ADGroup $GroupName -Properties Member $NumRepetitions = 5 $ScriptBlocks = @( { $Members = @() Foreach ( $Member in $Group.member ) { $Members += Get-ADObject $Member } }, { $Members = [Collections.Arraylist]::new() Foreach ( $Member in $Group.member ) { [void] $Members.Add( ( Get-ADObject $Member ) ) } }, { $GroupMembers = $Group.Member | Foreach { ( $_ -Split ',(OU|CN)=' -Replace '^CN=', '' )[0] } $LdapFilter = '(|(sAMAccountName=' + ( $GroupMembers -join ')(sAMAccountName=' ) + '))' $Members = Get-ADObject -LDAPFilter $LdapFilter } ) Foreach ( $ScriptBlock in $ScriptBlocks ) { $Durations = For ( $i=1; $i -le $NumRepetitions; $i++ ) { Measure-Command -Expression $ScriptBlock } $Durations | Measure-Object TotalSeconds -Average | Select-Object Average } Ergebnis in einer Spielumgebung für eine Gruppe mit 1.000 Mitgliedern in Sekunden: Foreach mit @(): 6,5145044 Foreach mit ArrayList: 5,96655574 LDAP-Filter: 0,75933482 Muß man glaub nix mehr dazu sagen... [Collections.Arraylist] spart schon bei nur 1.000 Accounts eine halbe Sekunde, aber Variante 3 mit dem "extrem langen LDAP-Filter" ist um den Faktor 10 schneller (der Filter hat knapp 24 kB). Und das wird immer schlimmer für die anderen beiden Methoden, wenn es mehr Mitglieder sind und die Netzwerkverbindung langsamer. Edit: Warum ich auf so was achte? Weil manche unserer Domains 150.000 User, 30.000 OUs und 10.000 GPOs haben. Die Anzahl der Gruppen weiß ich nicht, aber auch irgendwo im 6-stelligen Bereich. Da ist Geschwindigkeit alles... bearbeitet 14. Februar von daabm 5 Zitieren Link zu diesem Kommentar
v-rtc 88 Geschrieben 14. Februar Melden Teilen Geschrieben 14. Februar Ich frag mich, wie man sowas überhaupt noch gemanaged bekommt 😵💫 krasse Beschleunigung Zitieren Link zu diesem Kommentar
daabm 1.354 Geschrieben 14. Februar Autor Melden Teilen Geschrieben 14. Februar Ganz viel skripten. Also alles eigentlich. Und alles, was mehr als ein Objekt bearbeitet, auf Performance optimieren... Zitieren Link zu diesem Kommentar
BOfH_666 577 Geschrieben 14. Februar Melden Teilen Geschrieben 14. Februar vor 4 Stunden schrieb daabm: Weil manche unserer Domains 150.000 User, 30.000 OUs und 10.000 GPOs haben. 1 Zitieren Link zu diesem Kommentar
cj_berlin 1.312 Geschrieben 15. Februar Melden Teilen Geschrieben 15. Februar So isses. Und als Merkprinzip dabei, wer es nicht täglich macht (gilt 99%, Ausnahmen bestätigen die Regel): Zitat Niemand verarbeitet AD-Daten so schnell und effizient wie ein Domain Controller Zitieren Link zu diesem Kommentar
Apex 15 Geschrieben 15. Februar Melden Teilen Geschrieben 15. Februar Danke sehr interessant zum Thema LDAP Filter für mich Zitieren Link zu diesem Kommentar
daabm 1.354 Geschrieben 16. Februar Autor Melden Teilen Geschrieben 16. Februar Kurzer Nachtrag dazu: Wenn der LDAP-Filter zu groß wird, geht das in die Hose. Nach ein paar Tests mit sAMAccountName - sind es mehr als ~40k Einträge (der Filter ist dann etwa 1 MB groß), verweigert der Server die Antwort. Belastbare Informationen über entsprechende Limits habe ich aber keine gefunden... Wenn jemand was dazu hat, gerne her damit $LDAPFilter = "(|" + ( ( 1..50000 | Foreach { "(sAMAccountName=User$_)" } ) -join '' ) + ")" $LDAPFilter.Length ( Measure-Command { $ADobjects = Get-ADObject -LDAPFilter $LDAPFilter } ).TotalSeconds Ergebnis: 1158897 Get-ADObject : Es kann keine Verbindung mit dem Server hergestellt werden. Dies liegt möglicherweise daran, dass der Server nicht vorhanden oder derzeit ausgefallen ist oder dass darauf nicht Active Directory-Webdienste ausgeführt wird. In Zeile:3 Zeichen:34 + ... e-Command { $ADobjects = Get-ADObject -LDAPFilter $LDAPFilter } ).Tot ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ResourceUnavailable: (:) [Get-ADObject], ADServerDownException + FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.GetADObject 0,319832 Zitieren Link zu diesem Kommentar
MrCocktail 192 Geschrieben 21. Februar Melden Teilen Geschrieben 21. Februar Am 16.2.2024 um 17:38 schrieb daabm: Kurzer Nachtrag dazu: Wenn der LDAP-Filter zu groß wird, geht das in die Hose. Nach ein paar Tests mit sAMAccountName - sind es mehr als ~40k Einträge (der Filter ist dann etwa 1 MB groß), verweigert der Server die Antwort. Belastbare Informationen über entsprechende Limits habe ich aber keine gefunden... Wenn jemand was dazu hat, gerne her damit $LDAPFilter = "(|" + ( ( 1..50000 | Foreach { "(sAMAccountName=User$_)" } ) -join '' ) + ")" $LDAPFilter.Length ( Measure-Command { $ADobjects = Get-ADObject -LDAPFilter $LDAPFilter } ).TotalSeconds Ergebnis: 1158897 Get-ADObject : Es kann keine Verbindung mit dem Server hergestellt werden. Dies liegt möglicherweise daran, dass der Server nicht vorhanden oder derzeit ausgefallen ist oder dass darauf nicht Active Directory-Webdienste ausgeführt wird. In Zeile:3 Zeichen:34 + ... e-Command { $ADobjects = Get-ADObject -LDAPFilter $LDAPFilter } ).Tot ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ResourceUnavailable: (:) [Get-ADObject], ADServerDownException + FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.GetADObject 0,319832 Leider habe ich keine Umgebung mehr mit genug Accounts Ansonsten würde ich genau jetzt bei MS nach den entsprechenden Limits fragen ... Der Call müsste dann ja sogar kostenfrei sein. Zitieren Link zu diesem Kommentar
cj_berlin 1.312 Geschrieben 21. Februar Melden Teilen Geschrieben 21. Februar (bearbeitet) Was mich vor allem angesichts der Person des Urhebers wundert, ist, dass die Tests und die Misere am Ende mit ADWS und nicht mit LDAP erfolgten @daabm Du kannst das Ergebnis doch bestimmt auch mit dem DirectorySearcher reproduzieren - erhöhen sich da die Limits vielleicht? bearbeitet 21. Februar von cj_berlin Zitieren Link zu diesem Kommentar
daabm 1.354 Geschrieben 21. Februar Autor Melden Teilen Geschrieben 21. Februar Klar kann ich das - gelegentlich... Du aber auch. Die Motivation war aber nicht, diejenigen zu erreichen, die System.DirectoryServices nutzen. Sondern die, die mit den "gängigen" Standardvorgehen bei Powershell über Dinge iterieren. Und dabei zu weit rechts filtern 😂 Diesen Mal nur nicht so offensichtlich. Zitieren Link zu diesem Kommentar
Empfohlene Beiträge
Schreibe einen Kommentar
Du kannst jetzt antworten und Dich später registrieren. Falls Du bereits ein Mitglied bist, logge Dich jetzt ein.