PowerShell und LDAP, GC

Seit es die Windows AD Commandlets mit "Get-ADUser" und anderen Commandlets gibt, könnten sie es als überflüssig ansehen, per LDAP direkt die Domaincontroller abzufragen. Die AD-Commandlets nutzen ja nicht mal die LDAP-Ports 389/TCP, 636/TLS, oder 3268/TCP für den GC sondern einen "WebService", der vom DC für die Clients bereit gestellt wird. Das ist einfach, "Cloud-Tauglich" aber nicht immer schnell.

Diese Seite beschränkt sich auf Code-Beispiele für PowerShell, auf die ich selbst ach immer mal wieder zurückgreifen.

ADSI und PowerShell

Natürlich kann man auch per PowerShell über das ADSI-Kürzel direkt auf Objekte zugreifen.

$objUser = [adsi]"LDAP://cn=User1,ou=Anwender,dc=msxfaq,dc=de"

Wichtig ist hier die genaue Schreibweise mit LDAP in Großbuchstaben und "//"-Zeichen. Ein paar sehr gute Anleitungen gibt es auf:

GC Suche mit Powershell

Für die Powershell habe ich auch ein Beispiel. Hier ist ein Codeschnipsel, mit dem ich einfach den GC finde und befrage und danach zu jedem Objekt ein Feld ausgeben lasse

$root = [system.directoryservices.activedirectory.forest]::getcurrentforest().rootdomain.name
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"GC://$root")
#$objSearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"GC://$root",$sourceUser,$sourcepass)
$objSearcher.PageSize = 1000
$objSearcher.Filter = "(&(mail=*))"
$objSearcher.PropertiesToLoad.Add("mail") | Out-Null
$objSearcher.PropertiesToLoad.Add("ProxyAddresses") | Out-Null


$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults) {
    $mail = $objResult.properties.mail[0]
    write-host "Processing $mail"
    $adobject = [adsi]$objResult.path
    write-host "Bound " + $adobject.displayname
}

Natürlich kann man mit dem Directory Searcher auch "schmutzig" programmieren und mit einer Zeile ein Objekt instanziieren, Suchbedingungen vorgeben und das Ergebnis erhalten:

(New-Object System.DirectoryServices.DirectorySearcher("Samaccountname=Administrator")).findone()

Schauen Sie sich einfach die verschiedenen Overloads für die Instanzierung des Objekts an. Interessant ist in dem Zuge auch ein weiterer Accelerator, der den Code noch weiter verkürzt.

([adsisearcher]"Samaccountname=Administrator").findone()

Allerdings nutzt dieser Searcher dann wieder nur die Default Domäne des angemeldeten Benutzers und keinen GC. Um dies zu ändern, muss man aber nun New-Object verwenden, da ADSISEARCHER keine erweiterten Konstruktoren kann.

(New-Object System.DirectoryServices.DirectorySearcher([ADSI]"GC://servername","Samaccountname=Administrator")).findone()

So geht es dann auch schnell und relativ schmutzig. Zugegeben, ein Zugriff per Get-ADUser aus den Windows 2008 Commandlets ist strukturierter aber leider auch viel langsamer, da diese Commandlets die Active Directory Web Services nutzen. Aber dafür funktionieren Sie auch über HTTP/HTTPS und benötigen keine direkte LDAP-Verbindung.

Schreiben und AD-Replikation

Sie können natürlich per PowerShell auch schreiben. Ich kenne alleine drei Optionen

  • Native per LDAP/ADSI
    Die nutzen einfach das [ADSI]-Objekt
  • Module ActiveDirectory (addsadministration)
    Diese Modul erlaubt über entsprechende Commandlets die Manipulation von LDAP-Daten.
  • Produkt-Modules
    Exchange und Skype for Business bringen natürlich eigene PowerShell-Module zur Verwaltung ihrer Objekte mit, die aber auch Objekte anlegen und löschen können. Gerade Exchange ist das sehr früh (2007) vorgeprescht und kennt neben "New-Mailbox" auch ein "New-User". Leider hat Exchange kein eigenes Präfix vorgesehen, was für Verwirrung sorgen kann.

Anders als beim Lesen müssen Sie beim Schreiben aber die AD-Replikation beachten. Wer z.B. mit einem Commandlet ein Objekt anlegt oder ändert und kurz darauf im gleichen Skript das Objekte wieder liest, könnte veraltete Daten sehen. Mit jedem Aufruf kann nämlich ein anderer Domaincontroller zum Einsatz kommen. Sie müssen also die AD-Replikation abwarten oder immer den gleichen DC nutzen.

Ich mache es daher so, dass ich vor der Änderung an einem Objekt mit das Objekt und einen dazu passenden Domaincontroller der Domäne hole und in einer Variablen speichere. Bei jedem Aufruf gebe ich dann immer den gleichen DC über den Parameter "-DomainController $dcname" vor, der bei den meisten Commandlets vorhanden ist.

Weitere Links

Sehr viele Beispiele und Tools auf dieser Webseite nutzen VBScript und ADSI, um bestimmte Tätigkeiten durchzuführen.