DirSync-API im Detail

Für die Anfragen und die Erkennung von Änderungen im Active Directory ist das DirSync-Control eine effektive Schnittstelle. Diese Seite beschreibt die Details zum Einsatz und welche Ergebnisse geliefert werden.

Fokus

Bei der Verbindung mittels DirSync wird sowohl ein Server als auch eine "OU angegeben. Eine optionale Angabe von OUs ist nicht möglich. Die Angabe von AD-Feldern über "$objSearcher.PropertiesToLoad.Add("samaccountname")" führt sogar eher zu einem "Filter" auf Änderungen der angegebenen Felder. Auch ist es nicht möglich, sich an "GC:" oder per "LDAP://<servername>" zu binden. Insofern kann so eine Prüfung immer nur die Partition komplett überwachen:

[string]$searcher = "LDAP://srv01/dc=msxfaq,dc=local"
[System.DirectoryServices.DirectorySearcher]$objSearcher = `
    New-Object System.DirectoryServices.DirectorySearcher([ADSI]$searcher)

while ($true) {
  $objSearcher.DirectorySynchronization = 
     new-object system.directoryservices.DirectorySynchronization([System.DirectoryServices.DirectorySynchronizationOptions]::IncrementalValues,$DirSynccookie);
  foreach($res in $objSearcher.FindAll()) {
    $res.properties
  }
  start-sleep 1
}

Möchte ich also neben der Domäne auch die Konfigurationspartition überwachen, dann muss dies ein eigener Prozess übernehmen. Dies ist ein Unterschied zur USN-basierten Überwachung, die die USN "pro Server", also über alle AD-Objekte hinweg überwachen kann. Allerdings zu dem Preis, dass die geänderten Felder nicht so elegant übergeben werden.

Bei einer Überwachung per USN können Sie sich aber auch auf OUs und andere Dinge filtern. DirSync liefert immer alle Änderungen der kompletten Partition.

Feldauswahl

Es ist aber möglich, die Felder zu spezifizieren, die auf Änderungen überwacht werden sollen. Allerdings liefert die Suche dann auch immer nur die Objekte, bei denen sich die gefilterten Felder geändert haben.

objSearcher.PropertiesToLoad.add("Displayname") | out-null
objSearcher.PropertiesToLoad.add("samaccountname") | out-null

Das ist sicher eher etwas für Sonderfälle.

Cookie

Bei der Suche wird ein "Cookie" mit übergeben, den sich das Programm natürlich speichern muss, um beim nächsten Start genau dort wieder aufzusetzen, um die nächsten Änderungen zu erhalten.

  • Cookie ändert sich auch bei Leeranfrage
    Auch wenn die Anfrage keine geänderten Objekte zurück meldet, ändert sich der Cookie.
  • Cookie und DC
    Mit dem Cookie muss man immer den gleichen DC anfragen. Wenn dieser nicht da ist, müssen Sie selbst überlegen, ob sie darauf warte, dass der DC wieder da ist oder wirklich wechseln. Oft ist es besser zu warten. Bei einem Schwenk auf einen anderen DC wird man zwar keine Änderungen verpassen aber wohl Änderungen erneut vorgelegt bekommen. Zur Sicherheit sollte man bei einem DC-Wechsel wieder mit einem leeren Cookie anfangen.
  • Cookie und mehrere Antworten
    Liefert eine Suche mehrere Records, dann kann man zwar zwischenzeitlich den Cookie abfragen, aber erhält immer den Cookie für das Ende der Datenmenge. Es gibt also keine Zwischencookies. Insofern muss ein Programm alle Elemente verarbeiten oder den alten Cookie wieder verwenden.
  • Alte Elemente überspringen
    Wenn Sie alte Änderungen ignorieren wollen, dann können sie natürlich eine Anfrage von Anfang starten, alle Element nach $null lesen und so den aktuellen Cookie erhalten. Ich habe aber auch festgestellt, dass ein

Will man also alle alten Änderungen einfach überspringen und mit dem aktuellen Zeitpunkt frisch anfragen, muss man dennoch alle alten Änderungen mit einem "FindAll()" einmal anlesen, um die Liste der Änderungen zu erhalten. Eine Teilsuche mit FindNext() hingegen geht nicht. Auch eine Beschränkung der Ergebnisse mit "Sizelimit" oder "Pagesize" ist mir nicht geglückt.

Berechtigungen

Allerdings braucht man entsprechende Berechtigungen. Fehlversuche finden Sie z.B. im Eventlog auf dem DC:

Um diese API zu nutzen muss das Konto natürlich kein Domain-Admin sein. Das erforderliche Recht kann explizit auf den Eigenschaften der Domäne unter "Sicherheit" vergeben werden:

Ich habe die Einstellung gleich dreimal "bebildert", da links die englische Beschreibung schon ganz gut passt aber die deutsche Übersetzung auf Windows 2003 (Mitte) und Windows 2008 (rechts) leicht mit ähnlich klingenden Berechtigungen verwechselt werden kann.

Es reicht übrigens aus, das Recht nur auf der Domäne zu geben. Es muss nicht auf untergeordneten OUs vergeben werden

Es gibt genau genommen vier Berechtigungsstufen:

Berechtigungen Bedeutung

Keine Replication Rechte

Der Client kann eine Replikations-Anfragen stellen. Er kann aber natürlich im Rahmen seiner AD-Rechte die Objekte lesen und muss beim Aufruf auch das Flag "ObjectSecurity = 1" setzen

Alternativ kann eine normale LDAP-Abfrage natürlich über "whenChanged" oder "USNLastModified" nach geänderten Objekten suchen. Er bekommt natürlich "nur" das geänderte Objekt.

“Replicating Directory Changes In Filtered Set"

Diese Recht erlaubt auf die Daten zuzugreifen, die "ReadOnly"-Domain Controller lesen dürfen. Das Recht haben aber auch "Enterprise Domain Controller", die nicht mehr Rechte benötigen. DCs einer anderen Domäne brauchten z.B. keinen Zugriff auf alle geschützte Informationen. Es ist eine Teilmenge der Properties

“Replicating Directory Changes”

Diese Recht erlaubt die Anfragen von Änderungen aber unterschlägt "sensible" Felder wie z.B. Kennworte. Diese Berechtigung nutzt z.B. SharePoint, um die Anwender in seine eigene Datenbank zu übertragen. FIM und ADSYNC nutzen auch dieses Recht und sogar Exchange gewährt sich dieses Recht.

The AdminSDHolder object on the domain is updated to remove the "Allow" ACE that grants the "Exchange Trusted Subsystem" group the "Write DACL" right on the "Group" inherited object types.
https://support.microsoft.com/en-us/topic/reducing-permissions-required-to-run-exchange-server-when-you-use-the-shared-permissions-model-e1972d47-d714-fd76-1fd5-7cdcb85408ed

"Replicating Directory Changes All"

Mit diesem Recht können Sie wirklich "alles" replizieren.

Rückgabe

Bei der Nutzung des DirSync-Controls wird ein Objekt zurück gegeben, welches die geänderten Felder enthält. Dabei werden die folgenden Properties immer mit übergeben (mit Beispieldaten)

  • Objectguid
    Beispielausgabe: {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
    Dies ist die eindeutige GUID des Objekts im gesamten Forest, die selbst bei Umbenennungen des Objekts oder Verschiebungen innerhalb der Domäne aber auch innerhalb des Forest unverändert bleibt.
    MSDN: Object-Guid http://msdn.microsoft.com/en-us/library/ms679021(v=VS.85).aspx
  • adspath
    Beispielausgabe {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
    Dies ist der aktuelle LDAP-Pfad des Objekts bzw. der neue Pfad, wenn es verschoben wurde.
  • distinguishedname
    Beispielausgabe {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
    Der bekannte Distinguished Name
    MSDN: Obj-Dist-Name http://msdn.microsoft.com/en-us/library/ms675516(v=VS.85).aspx
  • instancetype
    Beispielausgabe {4}
    Diese Bitmaske zeigt an, wie das Objekt auf dem Server vorliegt:
    MSDN: Instance-Type (http://msdn.microsoft.com/en-us/library/ms676204%28VS.85%29.aspx)
    0x00000001 The head of naming context.
    0x00000002 This replica is not instantiated
    0x00000004 The object is writable on this directory
    0x00000008 The naming context above this one on this directory is held
    0x00000010 The naming context is in the process of being constructed für the first time via replication.
    0x00000020 The naming context is in the process of being removed from the local DSA.

Hinzu kommen dann noch die geänderten Felder:

Aktion Ausgaben (Propertyname, Wert)

Neuer Benutzer wird angelegt

objectguid                 {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
adspath                    {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
distinguishedname          {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
instancetype               {4} primärygroupid             {513}
lmpwdhistory               {}
objectsid                  {1 5 0 0 0 0 0 5 21 0 0 0 145 77 73 228 225 96 169 245 183 120 187 168 75 5 0 0}
whencreated                {02.09.2010 07:51:14}
ntsecuritydescriptor       {1 0 20 140 184 9 0 0 212 9 0 0 20 0 0 0 140 0 0 0 4 0 120 0 2 0 0 ...
ntpwdhistory               {}
dbcspwd                    {}
accountexpires             {9223372036854775807}
name                       {NeuerUser}
objectcategory             {CN=Person,CN=Schema,CN=Configuration,DC=msxfaq,DC=local}
samaccounttype             {805306368}
codepage                   {0}
countrycode                {0}
logonhours                 {}
parentguid                 {95 184 232 163 41 12 208 69 134 75 187 52 102 106 127 103}
objectclass                {top, person, organizationalPerson, User} UserPrincipalName          {NeuerUser@msxfaq.local} Useraccountcontrol         {512}
displayname                {NeuerUser}
supplementalcredentials    {}
samaccountname             {NeuerUser} unicodepwd                 {}
pwdlastset                 {0}

Das Anlegen eines neuen Benutzers sieht genau so aus, als wenn bei einem bestehenden Benutzer einfach alle Felder. Das Feld "ParentGUID" kann man als Hinweis dafür nehmen, dass das Objekt neu angelegt wurde. Es taucht aber auch noch bei einem Verschieben auf.

Benutzer wird per MMC für Exchange 2003 aktiviert

objectguid                     {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
adspath                        {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
instancetype                   {4}
mdbusedefaults                 {True}
legacyexchangedn               {/o=MSXFAQ/ou=Erste administrative Gruppe/cn=Recipients/cn=NeuerUser}
homemta                        {CN=Microsoft MTA,CN=SRV01,CN=Servers,CN=Erste administrative Gruppe...
msexchmailboxguid              {181 65 219 126 184 216 103 68 133 24 170 24 111 172 18 244}
mailnickname                   {NeuerUser}
msexchUseraccountcontrol       {0}
msexchmailboxsecuritydescri... {1 0 4 128 120 0 0 0 148 0 0 0 0 0 0 0 20 0 0 0 4 0 100 0 1 0 0 0 0 ...
msexchhomeservername           {/o=MSXFAQ/ou=Erste administrative Gruppe/cn=Configuration/cn=Servers/cn=SRV01}

Sie sehen, welche Felder die Exchange 2003 Erweiterungen in er MMC schreiben.

RUS 2003 bearbeitet den User

objectguid                     {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
adspath                        {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
homemdb;range=1-1              {CN=Postfachspeicher (SRV01),CN=Erste Speichergruppe,CN=InformationS...
adspath                        {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
msexchpoliciesincluded         {{243E377F-034E-4449-8DD4-2A53A39719D8},{26491CFC-9E50-4857-861B-0CB8DF22B5D7}}
msexchalobjectversion          {47}
textencodedoraddress           {c=DE;a= ;p=MSXFAQ;o=Exchange;s=NeuerUser;}
showinaddressbook              {CN=Globale Standardadressliste,CN=All Global Address Lists,CN=Address L...
instancetype                   {4}
objectguid                     {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
mail                           {NeuerUser@msxfaq.local}
proxyaddresses                 {SMTP:NeuerUser@msxfaq.local, X400:c=DE;a= ;p=MSXFAQ;o=Exchange;s=NeuerUser;}

Und kurz darauf verarbeitet der RUS das neue Objekt und vergibt die restlichen Daten. Anscheinend macht er das auf "zwei Schritte", d.h.

Beschreibung des Benutzers wird geändert

objectguid                     {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
adspath                        {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
instancetype                   {4}
description                    {Beschreibung}

Neben andere Feldern wird auch die "Description" gemeldet

Beschreibung einer OU wird geändert

objectguid                     {204 235 132 86 116 154 157 77 150 58 102 9 230 99 173 84}
adspath                        {LDAP://srv01/OU=msxfaq,DC=msxfaq,DC=local}
distinguishedname              {OU=msxfaq,DC=msxfaq,DC=local}
instancetype                   {4}
description                    {MSXFAQ}

Neben andere Feldern wird auch die "Description" gemeldet

DN des Users wird umbenannt

objectguid                     {121 36 243 15 213 231 22 79 137 122 111 134 12 32 223 135}
adspath                        {LDAP://srv01/CN=MailUser2,OU=msxfaq,DC=msxfaq,DC=local}
distinguishedname              {CN=MailUser2,OU=msxfaq,DC=msxfaq,DC=local}
instancetype                   {4}
name                           {MailUser2}
parentguid                     {204 235 132 86 116 154 157 77 150 58 102 9 230 99 173 84}

Der "Name" wird geändert und in ParentGUID steht die GUID der OU, unter der der User ist.

User in andere OU verschieben

objectguid                     {123 252 32 87 184 154 182 76 137 229 215 208 167 153 109 35}
adspath                        {LDAP://srv01/CN=Tes2003,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=Tes2003,OU=test,DC=msxfaq,DC=local}
instancetype                   {4}
name                           {Tes2003}
parentguid                     {95 184 232 163 41 12 208 69 134 75 187 52 102 106 127 103}

Das Feld "lastknownparent" wird bei einem Verschieben nicht geliefert sondern nur die ParentGUID. Übrigens ändert sich das Feld "Member" bei einer Gruppe, in der das Objekt Mitglied ist, nicht. Der DC verwaltet seine Gruppenmitgliedschaften demnach nicht über den DN als absoluten Namen sondern vermutlich die ObjectGUID

OU mit Unterobjekte n wird verschoben

objectguid                     {62 110 43 9 70 240 163 79 132 69 24 198 95 2 26 169}
adspath                        {LDAP://srv01/OU=test2003,OU=test,DC=msxfaq,DC=local}
distinguishedname              {OU=test2003,OU=test,DC=msxfaq,DC=local}
instancetype                   {4}
name                           {test2003}
parentguid                     {95 184 232 163 41 12 208 69 134 75 187 52 102 106 127 103}

Das Feld "lastknownparent" wird bei einem Verschieben nicht geliefert sondern nur die ParentGUID. Die "Veränderung" der Objekte in der OU wird nicht als Änderung gemeldet !

Benutzer in Gruppe aufnehmen

objectguid                     {89 49 144 45 118 51 176 75 128 108 50 110 167 52 177 36}
adspath                        {LDAP://srv01/CN=Gruppe1,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=Gruppe1,OU=test,DC=msxfaq,DC=local}
member;range=1-1               {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}

Das Feld "member" bei der Gruppe wird differenziell geändert aber beim Benutzer ändert sich nichts. Will man auf Benutzer triggern, muss man sich also hier aus Member den User aussuchen und selbst eine "Änderung" simulieren.

Wird ein Benutzer zwischen zwei Abfragen in mehrere Gruppen addiert, dann werden in jeder Gruppen die Mitglieder geändert und entsprechend gibt es je gruppe einen eigenständigen Eintrag. Werden in einer Gruppe mehrere Personen addiert, dann ist auch dies nur "ein Event", bei dem aber das Feld ein Array ist.

Primäre Gruppe ändern

objectguid                     {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
adspath                        {LDAP://srv01/CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
distinguishedname              {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
instancetype                   {4} primärygroupid                 {1120}

objectguid                     {89 49 144 45 118 51 176 75 128 108 50 110 167 52 177 36}
distinguishedname              {CN=Gruppe1,OU=test,DC=msxfaq,DC=local}
adspath                        {LDAP://srv01/CN=Gruppe1,OU=test,DC=msxfaq,DC=local}
member;range=0-0               {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}

Modified Object: LDAP://srv01/CN=Domänen-Benutzer,CN=Users,DC=msxfaq,DC=local
objectguid                     {98 160 67 12 158 240 110 70 175 121 202 202 105 6 209 243}
member;range=1-1               {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}
adspath                        {LDAP://srv01/CN=Domänen-Benutzer,CN=Users,DC=msxfaq,DC=local}
distinguishedname              {CN=Domänen-Benutzer,CN=Users,DC=msxfaq,DC=local}

Hier sind gut die drei Änderungen zu sehen, dass zuerst die primäryGroupID und dann in beiden Gruppen die Member geändert werden. Beim Benutzer selbst ändert sich nichts bei MemberOf

Benutzer aus Gruppe entfernen

objectguid                     {89 49 144 45 118 51 176 75 128 108 50 110 167 52 177 36}
distinguishedname              {CN=Gruppe1,OU=test,DC=msxfaq,DC=local}
adspath                        {LDAP://srv01/CN=Gruppe1,OU=test,DC=msxfaq,DC=local}
member;range=0-0               {CN=NeuerUser,OU=test,DC=msxfaq,DC=local}

Wird der Benutzer aus der Gruppe gelöscht, dann ändert sich die Gruppe aber nichts beim Benutzer. Wird ein Benutzer zwischen zwei Abfragen in mehrere Gruppen addiert, dann werden in beiden Gruppen die Mitglieder geändert und entsprechend gibt es Gruppe einen eigenständigen Eintrag. Werden in einer Gruppe mehrere Personen entfernt, dann ist dies "ein Event", bei dem aber das Feld ein Array ist.

Hinweis:
Wird ein Benutzer insgesamt gelöscht, dann wird er zwar aus der Gruppe entfernt, aber dieser Event wird nicht gefeuert. Jeder DC entfernt selbst den Benutzer aus den Gruppen.

Benutzer löschen

objectguid                     {84 30 220 218 75 122 104 68 140 73 169 194 33 254 152 132}
distinguishedname              {CN=NeuerUser\0ADEL:dadc1e54-7a4.....1fe9884,CN=Deleted Objects,DC=...}
adspath                        {LDAP://srv01/CN=NeuerUser\0ADEL:dadc1ea4...9884,CN=Deleted Objects,DC=...}
instancetype                   {4}
lmpwdhistory                   {}
mdbusedefaults                 {}
lastknownparent                {OU=test,DC=msxfaq,DC=local}
ntpwdhistory                   {}
dbcspwd                        {}
mailnickname                   {}
mail                           {}
name                           {NeuerUser...
isdeleted                      {True}
description                    {}
objectcategory                 {}
codepage                       {}
countrycode                    {} unicodepwd                     {}
parentguid                     {174 109 204 142 17 226 138 70 128 35 4 146 20 113 50 129} UserPrincipalName              {}
showinaddressbook              {}
samaccounttype                 {} primärygroupid                 {}
accountexpires                 {}
displayname                    {}
homemta                        {}
supplementalcredentials        {}
textencodedoraddress           {}
msexchalobjectversion          {}
pwdlastset                     {}
msexchpoliciesincluded         {}

Wird ein Benutzer gelöscht, werden anscheinend auch alle Felder "genullt", zumindest liefert das DirSync Control diese Daten so aus. Der Benutzer verschwindet aber auch aus den Gruppen, in denen er Mitglied ist. Dies triggert allerdings KEINE Änderung an. Anscheinend rationalisiert der DC diese Änderung einfach weg. Schließlich kann jeder DC für sich die Mitgliedschaften einer Gruppe aktualisieren, wenn das Mitglied gelöscht wird.

Dass das Objekt "gelöscht" worden ist, kann man an dem Attribut "isdeleted" erkennen. Der originale Ort findet sich unter "lastknownparent"

Die Liste der Mitglieder von Gruppen, in denen der Benutzer enthalten war, wird aber nicht aktualisiert. Jeder DC macht das selbst.

ändern und umbenennen

Wird in einer Pausenzeit das Objekt zu erst verändert und dann umbenannt oder verschoben, dann erhält DirSync zwei Änderungen . Dabei ist die Verschiebung immer zuerst und die Änderungen , auch wenn diese zeitlich vor der Verschiebung erfolgt sind. werden danach mit dem neuen DN angegeben.

Mehrfache Änderungen

Werden Felder zwischen zwei Abfragen mehrfach geändert oder das Objekt mehrfach verschoben, dann werden die Zwischenschritte nicht vom DC übergeben, sondern immer nur die Änderung zum aktuellen Stand.

ändern und Löschen

Wird ein Objekt geändert und dann gleich gelöscht, dann wird zusammen mit dem Delete-Event auch das ändern der anderen Felder auf "null" gemeldet

Feld ändern und zurückändern

Wird der Inhalt eines Felds zwischen zwei Abfragen von einem WertA auf einen WertB und dann wieder auf WertA geändert, dann wird die Änderung auf WertB nicht erkannt aber die Rückkehr auf WertA als Änderung gemeldet

Konto wird gesperrt

Interessant ist die Überwachung z.B. bezüglich von "Lockouts" von Benutzern. Wenn ein Konto gesperrt wird, wird dies natürlich auch im AD vermerkt.

objectguid                     {203 249 66 165 56 151 139 64 164 162 88 150 114 78 179 37}
adspath                        {LDAP://srv01/CN=adptUser1,OU=adpt,OU=test,DC=msxfaq,DC=local}
instancetype                   {4}
distinguishedname              {CN=adptUser1,OU=adpt,OU=test,DC=msxfaq,DC=local}
lockouttime                    {129280136916942816}

So lässt sich schon schnell ermitteln, wann ein Konto ausgesperrt wurde, auch ohne Überwachung von Eventlogs. Nach Ablauf der Sperrzeit kann sich der Benutzer dann wieder anmelden. Allerdings wird dabei keine Änderung mehr im Active Directory durchgeführt, d.h. das Feld "Lockouttime" zeit weiter den Zeitpunkt der Sperrung an.

Eine Änderung der Domainrichtlinien wirkt sich übrigens auch im Active Directory aus

objectguid                     {67 74 97 113 100 234 191 72 151 34 115 189 157 180 170 103}
adspath                        {LDAP://srv01/DC=msxfaq,DC=local}
distinguishedname              {DC=msxfaq,DC=local}
lockoutobservationwindow       {-12000000000}
lockoutduration                {-12000000000}

Und wird die Policy geändert, dass Konten wieder länger ausgesperrt sind, dann wird sogar ein zwischenzeitlich zugelassendes Konto wieder als gesperrt geführt, wenn die Lockouttime nun noch nicht "alt" genug ist

Gruppenrichtlinien

Wenn Sie Gruppenrichtlinien ändern, dann wird auch die "Versionnumber" im Active Directory verändert.

objectguid                     {248 102 90 2 189 254 179 65 189 218 81 23 197 205 83 208}
adspath                        {LDAP://srv01/CN={31B2F340-...B984F9},CN=Policies,CN=System,DC=msxfaq,DC=local}
distinguishedname              {CN={31B2F340-...B984F9},CN=Policies,CN=System,DC=msxfaq,DC=local}
instancetype                   {4}
versionnumber                  {65553}

Gruppe Löschen

Eine solche Aktion ist sehr knifflig abzufangen. Das Löschen der Gruppe selbst ist einfach zu erkennen.

isdeleted                      {True}
distinguishedname              {CN=adptgruppe\0ADEL:a87bbd587da9,CN=Deleted Objects,DC=msxfaq,DC=local}
objectguid                     {40 160 31 0 78 183 178 66 180 212 168 123 189 88 125 169}
name                           {adptgruppe...
adspath                        {LDAP://srv01/CN=adptgruppe\0ADEL:a87bbd587da9,CN=Deleted Objects,DC=msxfaq,DC=local}
instancetype                   {4}
objectcategory                 {}
parentguid                     {174 109 204 142 17 226 138 70 128 35 4 146 20 113 50 129}
samaccounttype                 {}
description                    {}
lastknownparent                {OU=adpt,OU=test,DC=msxfaq,DC=local}

Leider liefert DirSync aber keine Information, welche Mitglieder davor enthalten waren, sondern nur Felder der Gruppe selbst, die vorhanden und nun "genullt" wurden. Dies ist eine "Lücke", wenn Sie auf Gruppenmitgliedschaften bestimmte Dinge anwenden. ähnlich können Sie dies auch vom RUS. Wird eine Empfängerrichtlinie gelöscht, dann bleibt nur ein kompletter Rebuild aller Benutzer bezogen auf ihre Gruppen. Einzig über den Namen und eventuell den "LastKnowParent", kann ein Skript erkennen, dass eine "seiner Richtlinien-Gruppen" entfernt wurde und entsprechend agieren.

Das könnte darauf hinaus laufen alle Mitglieder aller anderen Richtlinien-Gruppen durchzuarbeiten oder einfach alle Benutzer basierend auf den Richtlinien-Gruppen oder OU-Pfaden zu verarbeiten.

Ratsam wäre hier, wenn die Gruppe nicht gelöscht, sondern nur alle Mitglieder entfernt werden, so dass ADPT die entsprechenden Event starten kann und erst danach die Gruppe entfernt wird.

Mehrzeilige Felder

Bei diversen Objekten können Felder auch mehrzeilig sein, z.B. beim Benutzer das Feld "Strasse". Das wird als "ein Feld" gesehen. Wird also in einem solchen Feld eine Zeile geändert, addiert, gelöscht, dann wird als Meldung immer das komplette Feld zurück gegeben

Multivalue Felder

Anders verhält es sich z.B. bei ProxyAdresses, welches nicht ein "mehrzeiliger String" sondern ein Array ist. Beim Addieren wird es erst mal normal ausgegeben

Modified Object: LDAP://srv01/CN=Tes2003,OU=test2003,OU=test,DC=msxfaq,DC=local
objectguid        : {123 252 32 87 184 154 182 76 137 229 215 208 167 153 109 35}
path              : LDAP://srv01/CN=Tes2003,OU=test2003,OU=test,DC=msxfaq,DC=local
adspath           : {LDAP://srv01/CN=Tes2003,OU=test2003,OU=test,DC=msxfaq,DC=local}
distinguishedname : {CN=Tes2003,OU=test2003,OU=test,DC=msxfaq,DC=local}
action            : Modify
field             : proxyaddresses
value             : {smtp:test2@test.test, smtp:test1@test.test}

Wird dann aber z.B. nur ein Feld addiert., dann wird dennoch das komplette Array gemeldet.

field             : proxyaddresses
value             : {smtp:test3@test.test, smtp:test2@test.test, smtp:test1@test.test}

Solche Felder verhalten sich also anders als z.B. das Feld "Member" einer Gruppe

Kennwort wird geändert

Eine Änderung eines Kennworts ändern sich sogar einige Felder auf einmal, welche einen Trigger auslösen.

objectguid        : {43 146 108 119 237 82 225 77 143 76 46 93 68 169 243 13}
path              : LDAP://srv01/CN=User1,OU=test,DC=msxfaq,DC=local
adspath           : {LDAP://srv01/CN=User1,OU=test,DC=msxfaq,DC=local}
distinguishedname : {CN=User1,OU=test,DC=msxfaq,DC=local}
action            : Modify
field             : ntpwdhistory
value             : {}

action            : Modify
field             : lmpwdhistory
value             : {}

action            : Modify
field             : dbcspwd
value             : {}

action            : Modify
field             : pwdlastset
value             : {129332136364439920}

action            : Modify
field             : unicodepwd
value             : {}

action            : Modify
field             : supplementalcredentials
value             : {}

Interessant ist dies z.B. für Prozesse, die Kennworte abgleichen. Mit ADMT könnten so etwas sogar auch mit ADPT integriert werden.

Damit sollten die relevanten Testfälle entsprechend verarbeitet worden sein.

Alte Werte und neue Werte

Das DirSync-Control liefert immer nur die "neuen Werte" zurück. Wenn Sie also zum Vergleich auch den vorherigen Wert benötigen, dann bleibt ihnen nichts anderes übrig, als diesen Wert irgendwo zu speichern.

Binden auf geänderte Objekte

In der Antwort ist sowohl der Distinguishedname als auch der ADSPath enthalten. Der ADS-Pfad ist gleich in einem Format, dass dieser für eine Serverbindung verwendet werden kann. Wenn ein Programm oder Modul nun mit dem ADSPath sich verbinden will, muss es einen Sonderfall behandeln.

Der ADSPath für gelöschte Objekte kann nicht direkt gebunden werden.

Entsprechend kann man ein gelöschtes Objekt natürlich nicht per ADSI binden um weitere Details zu erhalten. Über die GUID des Objekts könnte aber ein nachgelagertes System diesen Delete entsprechend auswerten. Generell ist die GUID ein sehr viel besserer primärer, eindeutiger und vor allem beständiger Schlüssel, um z.B. nachgeschaltete Systeme zu verarbeiten.

Fehler auf dem DC

In dem Cookie ist auch die USN zum DC codiert. Das stellen Sie spätestens dann fest, wenn Sie sich den Stand des DCs per Snapshot gesichert haben, dann weiter synchronisieren und den DC dann wieder zurück rollen. Wenn Sie dem neuen fortgeschriebenen Cookie dann wieder dann DC ansprechen, sollten Sie im Directory-Eventlog des angefragten DomainControllers (Hier Windows 2008 R2) eine Fehlermeldung erhalten:

Log Name:      Directory Service
Source:        Microsoft-Windows-ActiveDirectory_DomainService
Event ID:      2884
Task Category: Replication
Level:         Error
Keywords:      Classic User:          MSXFAQ\Username
Computer:      DC1.msxfaq.net
Description:
During an Active Directory Domain Services replication request, the local domain 
controller (DC) identified an untrusted client which has received replication data 
from the local DC using already-acknowledged USN tracking numbers.
Read-only DCs and DirSync clients are examples of untrusted clients.

Because the client believes it is has a more up-to-date Active Directory Domain 
Services database than the local DC, the client will not apply future changes to 
its copy of the Active Directory Domain Services database or replicate them to its 
direct and transitive replication partners that originate from this local DC.

If not resolved immediately, this scenario will result in inconsistencies in the 
Active Directory Domain Services databases of this source DC and one or more direct 
and transitive replication partners. Specifically the consistency of Users, 
computers and trust relationships, their passwords, security groups, security 
group memberships and other Active Directory Domain Services configuration data 
may vary, affecting the ability to log on, find objects of interest and perform 
other critical operations.

To determine if this misconfiguration exists, query this event ID using 
http://support.microsoft.com or contact your Microsoft product support.

The most probable cause of this situation is the improper restore of Active 
Directory Domain Services on the local domain controller or the remote 
Read-Only domain controller. User Actions:
If this situation occurred because of an improper or unintended restore, 
forcibly demote the affected DC. Untrusted client:
00000000-0000-0000-0000-000000000000 
Partition:
DC=msxfaq,DC=com USN reported by non-DC client:
28423 USN reported by Local DC:
26932

Wenn ein Prozess als "Local System" läuft, dann ist natürlich der Computername als "User" angegeben.

Die Lösung ist abhängig vom Anwendungsfall individuell. Wenn Sie ihrem Programm einen "Resync" befehlen können, (Also Start bei USN=0), dann dürfte dies der einfachste Weg sein. Manchmal reicht es auch das anfragende Programm auf einen anderen DC zu verweisen. Dieser hat andere USNs und dann machen die meisten Programme eh einen Resync.

Besonderheiten

So schön die Verwendung von DirSync ist, so gibt es auch ein Thema, welches gesondert behandelt werden muss. Wird ein Objekt "verschoben", dann kann DirSync dies noch erkennen und den Zielpfad melden. Aber wenn es sich z.B. Um eine OU handelt, die verschoben wird, dann meldet DirSync nur den verschobenen Container aber nicht die untergeordneten Objekte. Wer also z.B. Objekte anhand ihres DN bestimmten Einstellungen unterwerfen will, muss dies beachten.

Weitere Links