Nachverfolgen von Änderungen im Active Directory 31. Mai 2012

Sehr oft ist es hilfreich, wenn man Änderungen im Active Directory auf einfache Art und Weise nachverfolgen kann. Gerade bei Änderungen im Active Directory ist das eine sehr aufwendige Angelegenheit, die auch noch sehr viel Zeit kosten kann. Ab Windows Server 2008 gibt es die Möglichkeit einer erweiterten Überwachung für Zugriffe auf den Verzeichnisdienst. Allerdings werden durch diese Überwachung auch sehr viele Einträge im Ereignisprotokoll erstellt, die es dann wiederum sehr schwierig machen, die entsprechende Änderung zu finden, an der man interessiert ist.

Wenn man nur an den Änderungen interessiert ist und nicht an der Information wer diese Änderung vorgenommen hat, kann unter Umständen die Powershell und das .Net Framework weiterhelfen. Im Namespace “System.DirectoryServices” gibt es eine Klasse (DirectorySynchronization), mit der man genau diese Änderungen nachverfolgen kann. Mit der Hilfe dieser Klasse, kann man ein Cookie erstellen, was man zu einem späteren Zeitpunkt wieder einlesen kann. Dadurch kann man dann effektiv die Änderungen am Active Directory nachverfolgen.

Als Erstes benötigt man ein DirectorySearcher Objekt.

PS C:\>$dssearch = new-object System.DirectoryServices.DirectorySearcher
PS C:\>$dssearch

Wenn man ein Objekt aus der Klasse DirectorySearcher erstellt, ohne das man weiter Parameter übergibt, dann werde viele Eigenschaften auf Standardwerte gesetzt. So zeigt z.B. die Eigenschaft SearchRoot (der Ort ab dem die Suche beginnt) auf die Domäne.

PS C:\>$dssearch.SearchRoot

Da mich in diesem Beispiel nur die Benutzerobjekte interessieren, setze ich die Eigenschaft “Filter” des DirectorySearcher Objekts um die Benutzer herauszufiltern.

PS C:\>$dssearch.Filter = “(&(objectclass=user)(objectcategory=person))”
PS C:\>$dssearch

Jetzt müssen wir das DirectorySynchronization Objekt erstellen und es unserem DirectorySearcher Objekt zuweisen. Danach wird auch gleich die Suche über die Domäne ausgeführt.

PS C:\>$dssynch = new-object System.DirectoryServices.DirectorySynchronization([System.DirectoryServices.DirectorySynchronizationOptions]::None)
PS C:\>$dssearch.DirectorySynchronization = $dssynch
PS C:\>$dssearch.findAll()

Über die Methode “GetDirectorySynchronizationCookie” des DirectorySynchronization Objekts können wir uns jetzt das Cookie abholen. Dieses Cookie schreiben wir in eine Variable und speichern es anschließen im Ordner “Documents” des aktuell angemeldeten Benutzers.

PS C:\>$cookieBytes = $dssynch.GetDirectorySynchronizationCookie()
PS C:\>[System.IO.File]::WriteAllBytes(“$env:userprofile\documents\dircookie.bin”,$cookieBytes)

Das war es eigentlich auch schon.

Wir können jetzt jederzeit dieses Cookie wiederherstellen und mit dem aktuellen Zustand des Active Directory vergleichen. Um das zu realisieren, müssen wir zu einem späteren Zeitpunkt das Cookie aus der Datei “dircookie.bin” wieder zurückholen. Danach muss ein DirectorySearcherObjekt mit den gleichen Eigenschaften erstellt werden, wie wir es bei der Erstellung des Cookies erzeugt haben. Diesem DirectorySearcher Objekt weisen wir dann das DirectorySynchronization Objekt mit dem entsprechenden Cookie zu und führen anschließend die “FindAll()” Methode aus.

PS C:\>$cookieBytes = [System.IO.File]::ReadAllBytes(“$env:Userprofile\Documents\dircookie.bin”)
PS C:\>$dssearch = new-object System.DirectoryServices.DirectorySearcher
PS C:\>$dssearch.Filter = “(&(objectclass=user)(objectcategory=person))”
PS C:\>$dssynch = new-object System.DirectoryServices.DirectorySynchronization([System.DirectoryServices.DirectorySynchronizationOptions]::None,$cookieBytes)
PS C:\>$dssearch.DirectorySynchronization = $dssynch
PS C:\>$changes = $dssearch.findall()

Mit der Hilfe des cmdlets “Select-object” können wir uns jetzt die geänderten Eigenschaften der Objekte ansehen. In meinem Beispiel habe ich die Eigenschaften “samAccountName”, “userPrincipalName”, “displayname” und “sn” angepasst.

Wer sich den Screenshot etwas genauer ansieht, der wird bemerken, dass ich wesentlich mehr Attribute zurückbekomme. Die Attribute “distinguishedName”, “objectGUID”, “adsPath” und “instanceType” werden immer mit zurückgegeben. Hier noch einmal die zusammengefassten Kommandozeilen als einzelne Skripte:

#Cookie speichern
$dssearch = new-object System.DirectoryServices.DirectorySearcher
$dssearch.Filter = "(&(objectclass=user)(objectcategory=person))"
$dssynch = new-object System.DirectoryServices.DirectorySynchronization([System.DirectoryServices.DirectorySynchronizationOptions]::None)
$dssearch.DirectorySynchronization = $dssynch
$dssearch.findAll()
$cookieBytes = $dssynch.GetDirectorySynchronizationCookie()
[System.IO.File]::WriteAllBytes("$env:userprofile\documents\dircookie.bin",$cookieBytes)
#Cookie laden und geaenderte Attribute suchen
$cookieBytes = [System.IO.File]::ReadAllBytes("$env:userprofile\documents\dircookie.bin")
$dssearch = new-object System.DirectoryServices.DirectorySearcher
$dssearch.Filter = "(&(objectclass=user)(objectcategory=person))"
$dssynch = new-object System.DirectoryServices.DirectorySynchronization([System.DirectoryServices.DirectorySynchronizationOptions]::None,$cookieBytes)
$dssearch.DirectorySynchronization = $dssynch
$changes = $dssearch.findAll()
$changes | select-object -expandproperty Properties

Leider kann man nicht die ursprünglichen Werte der geänderten Attribute sehen, sondern nur die aktuellen Werte. Man kann auch nicht feststellen, wer diese Werte geändert hat. Meiner Meinung nach reicht das aber für viele Einsatzzwecke völlig aus. Wer sich in die einzelnen Klassen einlesen möchte, kann das hier tun:

8 Comments
Daniel Schubert Juni 12th, 2012

Die Anleitung hat mich zwar schon weitergebracht, ich hätte da jedoch noch eine Frage:

“$dssearch.Filter = “(&(objectclass=user)(objectcategory=person))””

wird ja dazu eingestellt, damit nur die Benutzerobjekte angezeigt werden. Was gebe ich denn als Filterzeile an wenn ich nur die Namen und die dazugehörigen Telefonnummern anzeigen lassen möchte?

mfG
Schubert

Frank Röder Juni 12th, 2012

Dann musst Du dem DSSearcher Objekt noch erklären, welche Attribute geladen werden sollen. Für einfach folgende Zeile ein bevor Findall() ausgeführt wird.

“Attribut1″,”Attribut2″,”Attribut3″ | Foreach-Object {$null = $dssearch.PropertiesToLoad.Add($_) }

Dadurch werden dann die Attribute geladen, die Du angegeben hast.

Viele Grüße

Frank

Daniel Schubert Juni 13th, 2012

Danke! Hat geholfen! :)

Daniel Schubert Juni 18th, 2012

Da mir hier beim letzten Mal so schnell und erfolgreich geholfen wurde, stelle ich hier direkt noch eine Frage! :D
Ich möchte jetzt von den Änderungen an den Telefonnummern einen Logfile erstellen.

Out-File C:\pshell\test.txt -Append -InputObject $changes -width 120

Das war bisher mein Ansatz, allerdings zeigt der Logfile dann nur “Path” und “Properties” an. Vielleicht eine Idee was ich hinzufügen muss damit auch geänderte Telefonnummern mit hineingeschrieben werden?
Google bringt da irgendwie nichts brauchbares.

Danke!

Frank Röder Juni 18th, 2012

Ich würde das nicht mit out-file sondern mit export-csv exportieren. Dadurch kannst Du danach das File mit Excel schön analysieren.
Was zeigt Dir denn ein $changes in der Konsole an? Zusätzlich würde ich ein neues Objekt mit new-object erstellen, in dem ich nur die Attribute übernehme, die ich auch geändert habe. Diese Objekte würde ich dann wiederum in einem Array abspeichern.

Übrigens gibt es ein Forum auf der Technetwebseite, was deutsch ist und auch gut besucht wird:

http://social.technet.microsoft.com/Forums/de-de/powershell_de/threads

Dort wird Dir auch recht fix geholfen.

Viele Grüße

Frank

Daniel Schubert Juni 19th, 2012

Das zeigt mir Powershell an:

Name Value
—- —–
objectguid {45 28 221 29 26 10 145 67 159 244 17 214 82 248 26 159}
instancetype {4}
telephonenumber {123456}
adspath {LDAP://CN=Schubert\, Daniel,OU=01 User RIEDEL IT,OU=00_USER RIEDEL,DC=riedel,DC=net}

Davon möchte ich nun nur den Namen und die neue Telefonnummer in eine Tabelle oder in ein Textdokument schreiben lassen.
Danke für den Tipp mit dem Forum, dort habe ich aber leider auch nicht weitere Hilfe gefunden.

Frank Röder Juni 19th, 2012

$changes | select-object name,telephonenumber | export-csv

das sollte passen.

Viele Grüße
Frank

Daniel Schubert Juni 20th, 2012

Funktioniert leider nicht. Da kommt nur Folgendes:

#TYPE Selected.System.DirectoryServices.SearchResult
“name”,”telephonenumber”
,

mfG Daniel

Leave a Reply

*