ADAMSync

Interessant wird ADLDS erst, wenn Sie Daten replizieren. Dazu dient ADAMSync. An einem konkreten Beispiel möchte ich das erläutern. Stellen Sie sich vor, Sie haben eine Anwendung X, welcher per LDAP bestimmte Informationen abrufen möchte. So könnte ein Faxserver z.B. die Faxnummer des Anwenders auslesen, um das Fax in die Mailbox zustellen zu können. Das Problem hierbei erkennen besonders große Firmen, da nicht alle Felder im Active Directory mit einem Index versehen sind. Entsprechend ineffektiv kann eine Suche dazu aussehen und den DC über Gebühr beanspruchen. Je größer die Infrastruktur ist, desto weniger Interesse besteht natürlich an einer Änderung des Schemas. Schließlich bedeutet eine Änderung eine große Replikation. Das gleiche gilt, wenn Produkte eigene Daten durch eine Schemaerweiterung hinterlegen möchten. Um z.B. eine einfache unidirektionale Replikation des AD zu ADAM zu erreichen, sind mehrere Schritte erforderlich.

Die Beschreibungen und Tests basieren auf ADAMSync Version 10.0.17763.2268 unter Windows 2019 1809 Build 17763.3046
Lesen Sie bitt die ganze Seite und insbesondere das Thema "Delete/Aging".

Applikationspartition

ADAMSync schreibt Daten aus einer Quelle in eine AD LDS Application Partition, die Sie entweder bei der Installation von AD LDS gleich mit eingerichtet haben oder nachträglich einrichten. Eine Beschreibung hierzu finden Sie auf AD LDS/ADAM - der kleine LDAP-Server oder anderen Seiten.

Schema Anpassung

Ehe Sie Objekte in AD LDS importieren können, müssen Sie das Schema des AD LDS entsprechend erweitern. AD LDS kommt nämlich nur mit einem rudimentären Schema. Zum einen liefert das AD LDS-Setup schon einige LDIF-Dateien zum Import mit und sie können aus der Quelle das Schema exportieren und in AD LDS einspielen.

  • Schemaerweiterung für AD-Objekte
    Zuerst muss das Schema von ADAM derart erweitert werden, dass es kompatible mit der Quelle ist und alle Objekte und deren Daten aufnehmen kann. (ca. 1009 Änderungen )

ldifde -i -s localhost:50000 -c CN=Configuration,DC=X #ConfigurationNamingContext -f C:\Windows\Adam\MS-AdamSchemaW2k3.ldf

  • Schemaerweiterung für ADAMSync-Metadaten
    ADAMSync speichert seine Konfiguration selbst auch im ADAM-Verzeichnis. Das Schema muss entsprechend mit folgendem Aufruf erweitert werden:

REM Die Zeile so nutzen und nicht das DC0X oder #configurationcontext ersetzen.
ldifde -i -s localhost:50000 -c CN=Configuration,DC=X #ConfigurationNamingContext -f C:\Windows\Adam\MS-AdamSyncMetadata.ldf

Wenn der aktuelle Anwender nicht zugleich Admin über ADAM ist, dann können Sie einfach Anmeldedaten mit "-b Username Domäne Kennwort" angeben.

Beachten Sie dazu aber auch das Programm "ADSchemaAnalyzer.exe" aus dem Verzeichnis "C:\Windows\Adam", welches mit installiert wird. Damit können Sie das Schema der Quelle und des Ziels miteinander vergleichen:

Die Begriffe sind vielleicht verwirrend, daher hier die Abfolge, wenn Sie mit dem Programm direkt per LDAP die Quelle und das Ziel erreichen können.

  1. Load Target Schema
    Damit ist NICHT der Ziel-LDAPServer (also AD LDS) gemeint sondern damit verbinden sie sich mit der Quelle, um das Schema zu erhalten, welches dann später im Ziel mit bereitgestellt werden müsste.
  2. Load base Schema
    Hier verbinden Sie sich dann mit dem AD LDS-Server, um das aktuell vorliegende Schema zu laden.
  3. Vergleich
    Nun können Sie im Hauptfenster vergleichen, wie sich die Schemas unterscheiden und sie können gezielt auch Klasse, Attribute und PropertySets zum Export markieren
  4. Optional Export als LDF
    So erhalten Sie dann eine LDF-Datei zum Import im Zielsystem

Wenn Sie aus der Quell wirklich nur ausgewählte Objekte und davon nur eine Teilmenge der Felder importieren wollen, dann müssen Sie natürlich in AD LDS nicht das Schema um alle Felder erweitern, die in der Quelle auch vielleicht nur vorkommen können. Meist reicht schon die Erweiterung um "MS-AdamSchemaW2k3.ldf", um die meisten Aufgaben zu bewältigen

ADAMSync Konfiguration erstellen

Für die Konfiguration von ADAMSync gibt es meines Wissens weder von Microsoft noch anderen Personen eine Art GUI. Sie müssen also schon selbst eine XML-Datei erstellen bzw. anpassen. Microsoft stellt ihnen eine Vorlage auf C:\Windows\ADAM\MS-AdamSyncConf.XML" bereit, die Sie am besten kopieren und anpassen.

Ich nutze zur Ablage dieser Konfig-XML, der später noch zu erstellenden Aufruf-Skripte und Ablage der Protokolle in der Regel ein eigenes Verzeichnis und schreibt nicht in C:\Windows\ADAM.
Starten Sie nicht gleich mit der 100% Lösung sondern fangen sie in kleinen Schritten an die Config-Datei zu erweitern.

Hier ein Beispiel zum Sync der Benutzer aus dem Forest "msxfaq.net" in einen AD LDS-Server.

<?xml version="1.0"?>
<doc>
  <configuration>
    <description>msxfaq.net</description>
    <security-mode>object</security-mode>
    <source-ad-name>msxfaq.net</source-ad-name>
    <source-ad-partition>dc=msxfaq,dc=net</source-ad-partition>
    <source-ad-account></source-ad-account>
    <account-domain></account-domain>
    <target-dn>dc=msxfaq,dc=adlds</target-dn>
    <query>
      <base-dn>dc=msxfaq,dc=net</base-dn>
      <object-filter>
         (&amp;(objectClass=user)(objectCategory=person))
      </object-filter>
      <attributes>
      <include>objectSID</include>
      <include>mail</include>
      <include>userPrincipalName</include>
      <include>middleName</include>
      <include>manager</include>
      <include>givenName</include>
      <include>sn</include>
      <include>department</include>
      <include>telephoneNumber</include>
      <include>title</include>
      <include>homephone</include>
      <include>mobile</include>
      <include>pager</include>
      <include>msDS-UserAccountDisabled</include>
      <include>samAccountName</include>
      <include>employeeNumber</include>
      <exclude></exclude>
      </attributes>
    </query>
    <user-proxy>
      <source-object-class>user</source-object-class>
      <target-object-class>userProxy</target-object-class>
    </user-proxy>
    <schedule>
      <aging>
        <frequency>0</frequency>
        <num-objects>0</num-objects>
      </aging>
      <schtasks-cmd></schtasks-cmd>
    </schedule>
  </configuration>
  <synchronizer-state>
    <dirsync-cookie></dirsync-cookie>
    <status></status>
    <authoritative-adam-instance></authoritative-adam-instance>
    <configuration-file-guid></configuration-file-guid>
    <last-sync-attempt-time></last-sync-attempt-time>
    <last-sync-success-time></last-sync-success-time>
    <last-sync-error-time></last-sync-error-time>
    <last-sync-error-string></last-sync-error-string>
    <consecutive-sync-failures></consecutive-sync-failures>
    <user-credentials></user-credentials>
    <runs-since-last-object-update></runs-since-last-object-update>
    <runs-since-last-full-sync></runs-since-last-full-sync>
  </synchronizer-state>
</doc>

Für meine Tests nutze ich eine deutlich verkürzte und damit auch einfachere Version, die nur den Displayname kopiert und keine ProxyUser unterstützt.

<?xml version="1.0"?>
  <doc>
    <configuration>
      <description>msxfaq.net Source1</description>
      <security-mode>object</security-mode>
      <source-ad-name>msxfaq.net</source-ad-name>
      <source-ad-partition>dc=msxfaq,dc=net</source-ad-partition>
      <source-ad-account></source-ad-account>
      <account-domain></account-domain>
      <target-dn>DC=app1,dc=msxfaq,dc=adlds</target-dn>
      <query>
        <base-dn>OU=ADLDSSource1,dc=msxfaq,dc=net</base-dn>
        <object-filter>
           (&#124;(objectCategory=Person))
        </object-filter>
        <attributes>
          <include>Displayname</include>
          <exclude></exclude>
        </attributes>
      </query>
      <schedule>
        <aging>
          <frequency>0</frequency>
          <num-objects>0</num-objects>
        </aging>
        <schtasks-cmd></schtasks-cmd>
      </schedule>
    </configuration>
  <synchronizer-state>
  <dirsync-cookie></dirsync-cookie>
    <status></status>
    <authoritative-adam-instance></authoritative-adam-instance>
    <configuration-file-guid></configuration-file-guid>
    <last-sync-attempt-time></last-sync-attempt-time>
    <last-sync-success-time></last-sync-success-time>
    <last-sync-error-time></last-sync-error-time>
    <last-sync-error-string></last-sync-error-string>
    <consecutive-sync-failures></consecutive-sync-failures>
    <user-credentials></user-credentials>
    <runs-since-last-object-update></runs-since-last-object-update>
    <runs-since-last-full-sync></runs-since-last-full-sync>
  </synchronizer-state>
</doc>

Für den Anfang sind die Felder am Anfang interessant, über die die Quelle, das Ziel und der Scope definiert wird.

XML

Beispiel

Beschreibung

<description>
Syncprofil1

Die Description ist nicht sonderlich wichtig, es sei denn sie nutzen mehrere Synchronisationsprofile für die gleiche Application Instanz. Da immer nur ein Profil aktiv in AD LDS geladen werden kann, ist dann diese Beschreibung die einzige Möglichkeit die Profile schnell zu unterscheiden.

<source-ad-name>
msxfaq.net

Hier muss der FQDN (DNS) ihres Forests oder Domain angegeben werden, den ADAMSync anspricht. Normalerweise geben Sie einfach die DNS-Domain an und ADAMSync findet einen Domain Controller.

ADAMSync is querying for a writeable replica of msxfaq.net.
Establishing connection to source server W2019dc227.msxfaq.net:389.

Sie können aber auch direkt einen FQDN eines Domaincontrollers angeben. Allerdings sollten Sie dann den Fehler im Log ignorieren, denn er sucht zuerst nach der Domain:

ADAMSync is querying for a writeable replica of W2019dc227.msxfaq.net.
Error: DCLocator call failed with error 1355. Attempting to bind directly to string.
Establishing connection to source server W2019dc227.msxfaq.net:389.
<source-ad-partition>
dc=msxfaq,dc=net

Hiermit spezifiziere ich dann den Distinguished Name der Quell-Partition. Damit ist wirklich die Domain bzw. Partition gemeint.

Wenn Sie hier eine OU angeben, dann habe zumindest ich folgenden Fehler bekommen.

Ldap error occured. ldap_search_ext_s: Unwilling To Perform.
Extended Info: 000020F7: LdapErr: DSID-0C0909FE, comment: Error processing control, data 0, v4563.
<source-ad-account>
svc-adam

Damit ist der Benutzername gemeint, mit dem sich ADAMSync an der Quelle anmeldet. Das kann ein SamAccountname oder ein UPN sein. Wenn das Feld leer ist, nutzt ADAMSync das Konto des angemeldeten Benutzers. Wenn der Zugriff nicht möglich ist, dann landet im Log etwsa wie folgt:

Establishing connection to source server DC01.carius.de:389.
Ldap error occured. ldap_bind_s: Local Error. 
Extended Info: .
<account-domain>
MSXFAQ

Der zum Anmeldekonto passende NT4-Domainname. Sie können So eine Anmeldung mit "domain\username" nachbilden.

<target-dn>
dc=msxfaq,dc=adlds

Das Ziel, in das ADAMSync schreibt.

In 2003 ADAM, you were able to specify a sub-ou or container of the of the ADAM partition, for instance OU=accounts,dc=fabrikam,dc=com. This is not possible in 2008+ AD LDS. You must specify the head of the partition, dc=fabrikam,dc=com
Quelle: https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/adamsync-101/ba-p/400165

Wenn Sie eine OU anlegen und hier angeben, dann wird sie einfach ignoriert und produziert keinen Fehler.

<base-dn>

Part

Ich kann bei der Suche ein oder mehrere OUs angeben, auf die gefiltert wird. Das beschränkt aber nur die geschriebenen Elemente. ADAMSync wird dennoch die komplette Quell-Partition lesen und auch ins Protokoll schreiben.

You can specify multiple base DNs in the XML file, but it is important to note that due to the way the dirsync engine works the entire directory will still be scanned during synchronization
https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/adamsync-101/ba-p/400165

<query>

 

Hier landen in weiteren Felder dann die Suchbasis und ein Objektfilter, um z.B. nur eine Auswahl zu importieren. Ein Filter wie "(Objectclass=*) repliziert vermutlich zu viel. Für die ersten Schritte sollten Sie sich auf eine Auswahl beschränken.

"(Objectclass=user") ist aber auch nicht optimal, das auch Computerkonten gefunden werden. Sie überspringen aber OUs, ohne die ADSync die darunter liegenden Objekte nicht anlegen kann. Wer alle Benutzer haben möchte, kann mit folgendem Filter starten:

(|(objectCategory=Person)(objectCategory=OrganizationalUnit))

Er liefert alle Benutzer und OUs.

Wenn Sie mit "Base-DN" die Quelle nicht weiter filtern, legt ADAMSYNC damit den kompletten OU-Baum  als Kopie an.

<include>
<exclude>

 

Sie können nur Include oder Exclude nutzen. Ich würde erst mit ganz wenig Attributen anfangen, z.B.:

<doc>
  <configuration>
    <query>
      <attributes>
        <include>userPrincipalName</include>
        <include>Displayname</include>
        <exclude></exclude>
      </attributes>
    <query>
  <configuration>
<doc>

Und dann nach und nach welche addieren, was die Fehlersuche einfacher macht, wenn bestimmte Inhalt von Attributen nicht kompatibel sind.

Import

Die vorbereitete Konfiguration wird nun in den AD LDS-Server importiert.

adamsync.exe /install localhost:50000 source1.xml

Technisch importiert ADAMSync die XML-Datei in die hinterlegte Ziel-Partition.

Sie sehen aber auch hier, dass das Feld nur ein String ist und auch nur eine Konfiguration unterstützt. Das wird später noch relevant, wenn wir mehrere Quellen in einen AD LDS-Server replizieren wollen. Wenn Sie ADAMSYNC ohne Parameter aufrufen, dann gibt es Hinweise, dass durchaus mehrere Konfigurationen möglich wären. Hier eine gekürzte Ausgaben

PS C:\temp\adamsync> c:\windows\adam\adamsync
No options were specified on the command line.
Usage: adamsync.exe <actions> <AD LDS Instance> <action params> [options]
<AD LDS Instance>:
  hostname:port                         -- Remote or local AD LDS instance specified as a hostname and port

<Actions and Params>:
  /l[ist]                               -- List configurations available
  /d[elete]  <config_name>              -- Delete configuration specified
                                        -- Ex:  adamsync /delete fab1:5000 "dc=fabrikam,dc=com"
  /i[nstall] <input_file>               -- Install configuration at <input_file>
                                        -- Ex:  adamsync /install fab1:50000 MS-AdamSyncConf.XML
  /export    <config_name> <out_xml>    -- Save the configuration file to <out_xml>
  /sync      <config_name>              -- Sync the configuration specified
                                        -- Ex:  adamsync /sync fab1:50000 "dc=fabrikam,dc=com"

Sie können mit ADAMSync durchaus mehrere Konfigurationen in einem AD LDS-Server verwalten aber immer nur genau eine Konfiguration pro Application Partition hinterlegen. Ein "ADAMSYNC /List localhost:50000" liefert aber schon alle Application Partitionen mit der dort abgelegten Konfiguration.

Achtung:
Eine Synchronisation liest nicht nur die Konfiguration sondern schreibt sie auch wieder geändert zurück!

Sync und Fehlersuche

Nachdem die Konfiguration in die Partition hinterlegt wurde, kann Sie mit der Option "/sync" aufgerufen werden. Sie müssen dazu natürlich den AD LDS-Server und die gewünschte Konfiguration angeben.

REM Start mit Ausgabe auf Konsole
adamsync.exe /sync localhost:50000 "DC=app1,DC=msxfaq,DC=adlds " /log -

REM oder start mit Ausgabe in Datei
adamsync.exe /sync localhost:50000 "DC=app1,DC=msxfaq,DC=adlds " /log adamsync.txt

Ohne die Angabe einer Protokolldatei sehen Sie keinerlei Ausgabe und bei einem Fehler gibt es auch keinen Errorlevel. In der Protokolldatei finden sich aber immer entsprechende Fehler oder Meldungen. Allerdings kann die Datei sehr groß werden, da ADAMSync immer die komplette Quell-Partition einliest und die Objekte nur anhand der ObjectGUID anzeigt. Es ist also nicht einfach zu sehen, welche Objekt da gerade gelesen wird, zumal das Format der GUID auch noch umgeschrieben wurde. Hier mal ein Beispiel, bei der ein Objekt geschrieben wird.

Establishing connection to target server localhost:50000.
Saving Configuration File on DC=app1,DC=msxfaq,DC=adlds
Saved configuration file.
ADAMSync is querying for a writeable replica of msxfaq.net.
Establishing connection to source server W2019dc227.msxfaq.net:389.
Using file .\damD3FC.tmp as a store for deferred dn-references.
Populating the schema cache
Populating the well known objects cache
Starting synchronization run from dc=msxfaq,dc=net.
Starting DirSync Search with object mode security.

Processing Entry: Page 1, Frame 1, Entry 0, Count 0, USN 0
Processing source entry <guid=ec4b25b552d1ec42ad9352dac28105ad>
Previous entry took 0 seconds (5, 0) to process

Processing Entry: Page 1, Frame 1, Entry 18, Count 1, USN 0
Processing source entry <guid=b7e54ed4e5dff74da96cfc4ab4ad1717>
Processing in-scope entry b7e54ed4e5dff74da96cfc4ab4ad1717.
Adding target object CN=adamsyncuser1,OU=ADLDSSource1,DC=app1,dc=msxfaq,dc=adlds.
Adding attributes: sourceobjectguid, objectClass, instanceType, displayName, lastagedchange, 
Previous entry took 0 seconds (31, 31) to process

Updating the configuration file DirSync cookie with a new value.

Beginning processing of deferred dn references.
Finished processing of deferred dn references.
Finished (successful) synchronization run.
Number of entries processed via dirSync: 1
Number of entries processed via ldap: 0
Processing took 0 seconds (0, 0).
Number of object additions: 1
Number of object modifications: 0
Number of object deletions: 2
Number of object renames: 0
Number of references processed / dropped: 0, 0
Maximum number of attributes seen on a single object: 5
Maximum number of values retrieved via range syntax: 0

Beginning aging run.
Aging requested every 0 runs. We last aged 1 runs ago.
Saving Configuration File on DC=app1,DC=msxfaq,DC=adlds
Saved configuration file.

Ich hoffen, dass eventuelle Fehler bei ihnen so sichtbar sein, dass Sie diese beseitigen können.

Matching

Eine wichtige Funktion bei einem Verzeichnisabgleich  ist die Zuordnung von Quelle zum Ziel. Es kann ja sein, dass das Quellobjekt verschoben, Umbenannt oder sonst wie verändert wird, dass der Abgleichprozess das Objekt im Ziel nicht mehr finden kann. Ich habe mir daher die Felder am Ziel genauer angeschaut. Repliziert wurde nur das Feld "Displayname". Im AD LDS habe ich dann folgende Felder gefunden

dn: CN=adamsyncuser1,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds
changetype: add
objectClass: top
objectClass: syncEngineAuxObject
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: adamsyncuser1
distinguishedName: CN=adamsyncuser1,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds
instanceType: 4
whenCreated: 20220807213859.0Z
whenChanged: 20220807214447.0Z
displayName: adamsyncuser1
uSNCreated: 18433
uSNChanged: 18447
name: adamsyncuser1
objectGUID:: XMnLCBW7qECzg97w6sci+A==
badPwdCount: 0
badPasswordTime: 0
pwdLastSet: 133043819398151978
objectSid:: AQUAABbMXXXG7wdxZERcKU3GGEa0doExZGX13A==
objectCategory:  CN=Person,CN=Schema,CN=Configuration,CN={138759E7-4933-4FB2-B3BB-70E3BE6BD024}
dSCorePropagationData: 16010101000000.0Z
msDS-UserAccountDisabled: TRUE
sourceObjectGuid:: t+VO1OXf902pbPxKtK0XFw==
lastAgedChange: 20220807214447.0Z

Interessant finde ich folgende beiden Felder:

  • sourceObjectGuid
    Hier steht die ObjectGUID des Quellobjekts drin.
  • lastAgedChange
    Das ist ein Zeitstempel, wann das Objekt das letzte mal geändert wurde. Es wird wohl beim Schreiben aktiviert.

Das Feld "sourceObjectGuid" wird anscheinend genutzt, um auch Umbenennungen elegant zu verarbeiten und nicht das alte Objekt zu löschen und mit neuem Namen neu anzulegen. Das sehen wir auch im Log.

Processing Entry: Page 1, Frame 1, Entry 18, Count 1, USN 0
Processing source entry <guid=b7e54ed4e5dff74da96cfc4ab4ad1717>
Processing in-scope entry b7e54ed4e5dff74da96cfc4ab4ad1717.
(sourceobjectguid=\b7\e5\4e\d4\e5\df\f7\4d\a9\6c\fc\4a\b4\ad\17\17) exists in target. Converting object creation to object modification.
Renaming target object CN=adamsyncuser1,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds to CN=adamsyncuser1,<GUID=bf12dc906312a345ba039cf1776ba4cd>.
Modifying attributes: displayName, lastagedchange,
Previous entry took 0 seconds (47, 0) to process

Damit das funktioniert, müssen Sie natüröich das Schema um "MS-AdamSyncMetadata.ldf" erweitern und dort steht z.B. drin: (Auszug)

dn: CN=ms-DS-Source-Object-Guid,CN=Schema,CN=Configuration,DC=X
changetype: add
objectClass: attributeSchema
ldapDisplayName: sourceObjectGuid
attributeID: 1.2.840.113556.1.4.1885
attributeSyntax: 2.5.5.10
searchFlags: 1
oMSyntax: 4

dn: CN=ms-DS-Last-Aged-Change,CN=Schema,CN=Configuration,DC=X
changetype: add
objectClass: attributeSchema
ldapDisplayName: lastAgedChange
attributeID: 1.2.840.113556.1.4.1888
attributeSyntax: 2.5.5.11
searchFlags: 1
oMSyntax: 24

dn: CN=ms-DS-Configuration-File,CN=Schema,CN=Configuration,DC=X
changetype: add
objectClass: attributeSchema
ldapDisplayName: configurationFile
attributeID: 1.2.840.113556.1.4.1889
attributeSyntax: 2.5.5.12
searchFlags: 1
oMSyntax: 64

dn:
changetype: modify
add: schemaUpdateNow
schemaUpdateNow: 1
-

Diese Felder gehören zu ADAMSync und so merkt sich ADAMSync, welches Zielobjekt zu welchem Quellobjekt gehört. Die ObjectGUID wird auch zum Matchen genutzt.

Processing Entry: Page 1, Frame 1, Entry 3, Count 1, USN 0
Processing source entry <guid=3caef86ef8dd904bb4a9834e9f93ddf0>
Processing in-scope entry 3caef86ef8dd904bb4a9834e9f93ddf0.
(sourceobjectguid=\3c\ae\f8\6e\f8\dd\90\4b\b4\a9\83\4e\9f\93\dd\f0) exists in target. Converting object creation to object modification.
Renaming target object CN=SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c},CN=Users,DC=app1,DC=msxfaq,DC=adlds to CN=SystemMailbox{bb558c35-97f1-4cb9-8ff7-d53741dc928c},<GUID=60d402c1b1d3ea4fa2f37a80d5f7bf48>.
Modifying attributes: lastagedchange, 
Previous entry took 0 seconds (0, 0) to process

Das hilft, wenn Sie z.B. eine Konfiguration ersetzen oder ein Objekt in eine andere OU verschoben wird.

Konflikte

Um die Funktion von ADAMSync besser zu verstehen, habe ich eine Quelle mit einem Ziel verbunden und dann verschiedene Veränderungen vorgenommen. Zuerst habe ich aus der Quelle einen Sync in das Ziel vorgenommen und sichergestellt, dass auch nachfolgende Synchronisationen keine Änderungen mehr bewirken. Ich habe zudem erst "nur" eine Synchronisation gestartet und wenn sich nichts geändert hat, einmal einen Import+Sync gestartet. Damit wollte ich sicherstellen, dass kein Dirsync-Cookie o.ä. etwas bewirken.

Veränderung   Reaktion

Zusätzlicher User im ADLDS

 

Das Objekt bleibt bestehen. Es bekommt keinen Zeittempel in "lastAgedChange", wenn Aging=0 ist. , so dass es weder per DirsyncAPI noch per Aging (siehe weiter unten) gelöscht wird.

Wenn ich dann ein Objekt mit dem gleichen CN in der Quelle anlege, dann addiert er ein Objekt im Ziel mit einer GUID als CN. Es findet zumindest anhand des CN kein Matching statt.

Allerdings verschwindet das Objekt bei aktiviertem Aging.

UPN-Konflikt

 

Wen ich einen Benutzer samt UPN synchronisiere und der UPN ist im Ziel schon durch ein anderes Objekt belegt, dann wird das Objekt nicht angelegt und eine Fehlermeldung angezeigt.

Objekt in der Quelle gelöscht

 

Obwohl ADAMSync sich einen DirSync Cookie speichert und theoretisch mit den entsprechenden Berechtigungen auch Löschungen erkennen könnte, werden Objekte im Ziel bei mir nicht gelöscht. Selbst mit einem FullSync oder Reimport der Konfiguration wird nichts gelöscht.

Es ist wohl ein Kandidat für das "Aging" und die DirSync-API wird nur für neue/geänderte Objekte genutzt?

Objekt in der Quelle in eine ebenfalls replizierte OU verschoben

 

Objekt wird im AD LDS ebenfalls verschoben.

Objekt in der Quelle in eine nicht replizierte OU verschoben

 

Objekt wird gelöscht. Hier scheint er die "Änderung" über die DirSync-API zu erkennen

Objekt in AD LDS CN umbenannt

 

Wenn ich in AD LDS ein repliziertes Objekt umbenenne, dann wird es beim nächsten Sync nicht umbenannt. Es hat sich ja in der Quelle wohl nicht geändert. Wenn ich aber ein "FullSync mit /fs machen oder die Konfiguration neu importiere, dann wird das Objekt wieder zurück umbenannt. Interessanterweise werden OUs nicht umbenannt.

Objekt in AD LDS gelöscht

 

ADAMSync legt das Objekt beim "normalen Sync" nicht an. Erst ein FullSync mit "/fs" statt "/sync" erzeugt das fehlende Objekt

Sie sollten also mitnehmen, dass Sie besser nie etwas an Objekten im AD LDS etwas ändern, die durch ADAMSync verwaltet werden oder danach einen "FullSync" Starten. ADAMSync erkennt beim Sync nur neue oder geänderte Objekte aber anscheinend keine "Deletes".

Das nicht sind alle Sonderfälle aber die Funktion schreckt mich eher ab, ADAMSync produktiv einzusetzen. Da ist viel zu wenig "Selbstheilung" drin.

Scheduling

Nun müssen Sie nur noch dafür sorgen, dass folgende Zeile regelmäßig aufgerufen wird, z.B. durch den Windows Taskplaner. Auch die Aufrufe selbst würde ich in ein BATCH, VBSCRIPT oder aktuell wohl besser PowerShell-Skript stecken, welches gleich mehrere Dinge nachrüstet:

  • Geeigneten Namen für die Protokolldatei festlegen
  • ältere Protokolldateien löschen z.B. alles > 30 Tage
  • ADAMSYNC mit den Parametern aufrufen und auf den Abschluss warten
  • Die letzte Protokolldatei auf Fehler prüfen
  • Job-Status z.B. für das Monitoring bereitstellen oder direkt melden

Die Langfassung erstelle ich immer pro Kunde passende, denn gerade beim Logging und Alarmierung gibt es doch sehr viele unterschiedliche Systeme. Daher hier nur eine Kurzfassunt

param (
   [string]$config="DC=app1,DC=msxfaq,DC=adlds",
   [string]$configfile = "source1.xml",
   [string]$adldsserver = "localhost:50000"
)
Write-Host "Start-ADAMSync"
Write-Host "Parameter config      : $($config)"
Write-Host "Parameter configfile  : $($configfile)"
Write-Host "Parameter ADLDSserver : $($adldsserver)"

$logfileprefix = "C:\temp\adamsync\Adamsynclog-$($config.replace(""DC="","""").replace("","",""-""))-$(get-date -Format yyyyMMdd-HHmmss)"
Write-Host "Logfile: $($logfileprefix)"

Write-Host "Importing configfile"
Start-Process `
   -Wait `
   -WindowStyle Hidden `
   -FilePath "$env:SystemRoot\ADAM\adamsync.exe" `
   -ArgumentList "/install $adldsserver $configfile /log ""$($logfileprefix)-import.log"""

Write-Host Start Sync
Start-Process `
   -Wait `
   -WindowStyle Hidden `
   -FilePath "$env:SystemRoot\ADAM\adamsync.exe" `
   -ArgumentList "/sync $adldsserver $config /log ""$($logfileprefix)-sync.log"""

notepad.exe "$($logfileprefix)-sync.log"

Write-Host "End-ADAMSync"

Beachten sie, dass dieses Skript jedes Mal die Konfiguration neu einliest und damit einen FullSync macht, da der vorherige Sync in der Konfiguration überschrieben wird. Wenn Sie dies nicht möchten, dann sollten Sie den ersten "Start-Process" nur einmal ausführen.

Überwachen

AD LDS installieren und betreiben Sie, wenn sie eine wichtige Applikation haben, die nicht direkt das AD fragen darf oder kann. Daher versteht es sich von selbst, dass Sie auch AD LDS und insbesondere ADMSync überwachen sollten. Fehler oder fehlende Objekte sind nicht einfach zu erkennen und dennoch negative Effekte haben. Sie sollten daher schauen, wie die nicht nur die Funktion von AD LDS überwachen (Siehe AD LDS/ADAM) sondern auch ADAMSYNC überwachen. Dazu gibt es mehrere Optionen.

  • Logfile
    Ich würde bei ADAMSync immer ein "/log logdatei.txt" angeben. Optional sogar mit laufender Nummer, Name der Konfiguration oder Zeitstempel, denn die Datei wird überschrieben und nicht angehängt. Schon Microsoft schreibt dazu:

The only way to determine if the synchronization was successful is to check the log file. This highlights the importance of generating the log
Quelle: https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/adamsync-101/ba-p/400165

  • Eventlog
    Fehler, allerdings keine Information zu ADAMSYNC, d.h. weder Import noch Replikationsfehler werden hier protokolliert. Eventuell aber Fehler, die durch ADAMSYNC letztlich in der AD LDS-Instanz sichtbar werden, z.B. fehlerhafte Anmeldungen.
  • adamsync /cs
    Über die Statusabfrage holt sich ADAMSYNC au dem AD LDS die Information über den letzten Synchronisation. ADAMSync schreibt den Status dazu in die per Import hinterlegte Konfiguration. Diese Informationen können Sie sich natürlich auch per LDAP direkt abrufen: Hier ein Auszug:
  <synchronizer-state>
    <dirsync-cookie></dirsync-cookie>
    <status></status>
    <authoritative-adam-instance>localhost:50000</authoritative-adam-instance>
    <configuration-file-guid></configuration-file-guid>
    <last-sync-attempt-time>20220807103757.0Z</last-sync-attempt-time>
    <last-sync-success-time>20220807103758.0Z</last-sync-success-time>
    <last-sync-error-time>20220807103758.0Z</last-sync-error-time>
    <last-sync-error-string>Ldap error occured. ldap_search_ext_s: Unwilling To Perform. 
         Extended Info: 000020F7: LdapErr: DSID-0C0909FE, comment: Error processing control, data 0, v4563.
     </last-sync-error-string>
    <consecutive-sync-failures></consecutive-sync-failures>
    <user-credentials></user-credentials>
    <runs-since-last-object-update></runs-since-last-object-update>
    <runs-since-last-full-sync></runs-since-last-full-sync>
  </synchronizer-state>
  • Exit Code
    Ich habe bei meinem Versuchen noch nie einen "Errorlevel<>0" von ADAMSYNC.EXE zurück bekommen.

Damit bleibt effektiv nur das Parsen der Protokolldatei auf Fehler oder synthetische LDAP-Abfragen gegen AD LDS wie z.B. zählen der OBjekte, als Möglichkeit.

Deletions

Wer Objekte anlegt, muss sie auch um das Löschen kümmern. ADAMSync löscht Objekte, wenn Sie in der Quelle als "gelöscht" erkannt werden. Dazu bedient sich ADAMSYNC der DirSync-API. Im Log-File sehen Sie diese Löschungen sowohl beim jeweiligen Objekte als auch in der Zusammenfassung.

Processing Entry: Page 1, Frame 1, Entry 18, Count 1, USN 0
Processing source entry <guid=b7e54ed4e5dff74da96cfc4ab4ad1717>
Deleting target object CN=adamsyncuser1,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds.
Previous entry took 0 seconds (0, 0) to process


Updating the configuration file DirSync cookie with a new value.

Beginning processing of deferred dn references.
Finished processing of deferred dn references.

Finished (successful) synchronization run.
Number of entries processed via dirSync: 0
Number of entries processed via ldap: 0
Processing took 0 seconds (0, 0).
Number of object additions: 0
Number of object modifications: 0
Number of object deletions: 0
Number of object renames: 0
Number of references processed / dropped: 0, 0
Maximum number of attributes seen on a single object: 0
Maximum number of values retrieved via range syntax: 0

Beginning aging run.
Aging requested every 0 runs. We last aged 1 runs ago.
Saving Configuration File on DC=app1,DC=msxfaq,DC=adlds
Saved configuration file.

Wie sie aber weiter oben bei den "Konflikten" schon sehen konnten, hat ADAMSync nicht in allen Fällen ein Objekt gelöscht, wenn ich es erwartet habe. Interessanterweise speichert sich ADSync schon ein "Dirsync-Cookie", dessen Funktion ich auf der Seite DirSync-API schon beschrieben habe.

Updating the configuration file DirSync cookie with a new value.

Achtung: Wenn Sie die ADLDS-Konfiguration überschreiben, sollten sie auch alle dazu passenden Objekte im Ziel entfernen, da ohne den Cookie ADSync nicht löschen kann.

Das führt bei der normalen Replikation dazu, dass keine Objekte in AD LDS gelöscht werden, wenn diese in der Quelle gelöscht werden. Objekte im AD LDS werden sehr wohl gelöscht, wenn Sie in der Quelle in eine OU verschoben werden, die nicht eingeschlossen ist. Die DirSync API liefert zwar auch "deletions", wenn der ausführende Benutzer die entsprechenden Rechte hat. Ich habe aber ADAMSync als DomainAdmin der Quelle ausgeführt und ADAMSync hat dennoch nichts gelöscht.

Aging

Damit kommen wir dann zum Thema "Aging", welches Sie aktiveiren können. Wenn ADAMSync weder über die DirSyncAPI noch über einen Vorher/Nachher-Vergleich, noch über einen Soll/Ist-Vergleich zuverlässig erkennen kann, wenn ein Objekt in der Quelle gelöscht wurde, dann bleibt nur eine "Kennzeichnung" mit nachfolgendem Löschen.

Interessant ist dabei ein Artikel von Eric Fleischmann (Bei Microsoft 2002-2012) auf den Clint Boessen http://clintboessen.blogspot.com/2011/08/adamsync-aging.html  verweist und der sehr gut die Thematik beschreibt. Leider ist er Artikel weder bei Microsoft noch sonst wo offiziell erreichbar. Aber ich habe ihn nur noch im GoogleCache von RSSING gefunden. Da ich Sorge habe, dass der Content irgendwann  ganz verloren geht, habe ich ihn mir ausnahmeweise als Zitat gesichert.

Change visibility in the directory...or lack there of (aka "what's the point of aging?") October 28, 2006, 6:14 pm
===============================
I’m often asked about aging in adamsync so I thought I’d present the more general problem here for people to ponder. Hopefully this gives some context around the problem which aging in adamsync is supposed to address.
Imagine you are writing a tool which sync’s changes out of AD. You (the person running this tool) have some set of permissions…whatever they may be. You are syncing along happily.
One day you get a phone call…”My user was moved from OU=bar in to OU=foo yet the sync target still shows me in OU=bar. What gives?” You begin to investigate only to find out that you don’t have permissions to OU=foo. As a result, you don’t have any of the objects in OU=foo in your target location. The reason is straight forward….you don’t have permissions to the target, so when the object moved from bar to foo you never saw this change. You couldn’t see this change! You didn’t have permissions to OU=foo.
This is one of many such cases. If you don’t have the ability to see some object in the target location, it is hard to say anything about your view of it from the source. You could still have the object in the source location and have no idea that it moved out of your view. The reason is of course straight forward….you can’t see the target so you didn’t see that mod and we don’t have any construct where the source can say “out of your purview but not here anymore.” So you simply don’t realize the object has changed.
Historically, this was not nearly as much of a problem. Most people use DirSync to sync changes out of AD. In Win2K, in order to use DirSync you needed to be a domain admin. So, you could see most things that happen (out of the box anyway). In Win2k3 we built a feature for DirSync that made this problem more common….DirSync object security mode. In this mode anyone can use DirSync to sync out of any partition they so choose, and DirSync only shows changes for objects you have access to see. This is a very useful feature.
So now let’s consider adamsync, a simple DirSync client, and the problem I've mentioned above. When we wrote adamsync we wanted to ensure that we could handle the scenario where you are not an admin and want to sync data out. So, we default the tool to object security mode. This is fairly convenient for non-admins that wish to use the tool.
However, consider a very mainstream case. You are using adamsync to sync objects out of some domain NC. Objects are deleted. You don’t have permissions to see the deletions (remember I said that you “lose changes” when you move an object to a place you don’t have perms to? Well normal users don’t have permissions to the deleted objects container out of the box. So it’s a very common mainstream case for this problem….). As a result, you never reflect the deletion in your target container in ADAM. You’re woefully out of date.
One fix for this problem could be that you just give more perms. In the deleted objects case, just give the user who is syncing permissions to read the deleted objects container. But some people might not find that acceptable, depending upon their scenario.
Enter aging. We wrote aging to be a periodic background thread that goes and checks to make sure objects which we haven’t seen change in a while are actually still there. So you can imagine that every now and then you go back and check to ensure that all objects you have in the target are still in the source. This is the aging approach. While the specifics are configurable, that’s the basic idea.
Aging is just one such mechanism. There are lots of approaches to this problem that one could consider. It’s just the one we chose for adamsync.
One minor point I’ll raise before ending this post. Aging in ADAMSync in R2 is unfortunately not working properly. There is a bug that basically breaks it in some cases. It’s hard to say when but you should assume you’ll hit it at some point….no idea if you will, but you never know. So if you need aging pre-LH (ie, you have a compelling scenario where you want to sync as a non-admin) please open a QFE request with PSS. Or just give perms to deleted objects for now (or whatever the container is which you can't see)…a much easier quick-fix.
Quelle: http://blogs.technet.com/b/efleis/archive/2006/10/28/change-visibility-in-the-directory-or-lack-there-of-aka-what-s-the-point-of-aging.aspx 
SSeite ist leider nicht mehr Online, da Herr Fleischmann Microsoft verlassen hat und daher sein "persönliches TechNet Blog" archiviert wurde.

Die Beschreibung interpretiere ich wie folgt:

  • ADSync macht immer einen Merge und löscht erst mal nichts.
  • AADSync hat ein „Aging“, was ich aktivieren kann um Objekte zu löschen, die lange nicht mehr „gesehen“ wurden.
  • Dazu schreibt ADAMSync das Feld „ms-DS-Last-Aged-Change“ im Ziel mit einem Zeitstempel.

Die Funktion wird über die Configuration.XML vorgegeben.

<doc>
  <configuration> 
    <schedule>
      <aging>
         <frequency>3</frequency>
         <num-objects>0</num-objects>
      </aging>
      <schtasks-cmd></schtasks-cmd>
    </schedule>
  </configuration> 
</doc>

RRelevant sind hier die Werte bei "Frequency" und "num-objects, über die das Aging aktiviert wird. Die Dokumentation von Microsoft ist hier etwas unklar.

  • Frequency
    http://technet.microsoft.com/en-s/library/cc737713.aspx
    "Contains the number of synchronization runs that will occur before an aging search will be performed. This value must be a positive integer. "
    EEin Wert von "0" deaktiviert die Löschung, eine 1 sorgt für ein Cleanup bei jedem Lauf und eine 5 z.B. nur bei jedem 5ten Lauf.
  • num-objects
    http://technet.microsoft.com/en-s/library/cc778153.aspx
    "Contains the number of objects to search for in each aging search."
    Bei "0" werden alle obsoleten Objekte entfernt. Wenn ich z.B. "10" eintrage, dann werden bei jedem Lauf maximal 10 Objekte entfernt. Es dauert also mehrere Läufe, bis alle obsoleten Objekte weg sind.

Ich kann also einmal steuern, nach vielen Durchläufen ein mit mehr aktualisiertes Objekt entfernt wird und auch bestimmen, wie viele Objekte bei einem Durchlauf maximal entfernt werden.

Bei einer "Single Sync"-Lösung mit einer Quelle können Sie die "Frequency=1" und "num-objects=0" setzen, damit immer gleich alle Objekte entfernt werden, die es in der Quelle nicht mehr gibt.

Im Log sieht das z.B.: so aus:

Beginning aging run.

Processing target entry <guid=bf12dc906312a345ba039cf1776ba4cd>
20220807125732.0Z was when we last saw OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds.
Modifying attributes: lastagedchange,

Processing target entry <guid=94c13da949524a4ca493e0ffa208f517>
20220807125931.0Z was when we last saw OU=ADLDSSource2,DC=app1,DC=msxfaq,DC=adlds.
Modifying attributes: lastagedchange,

Processing target entry <guid=031d46850003ae4b9189e6e3af3addd7>
20220807220721.0Z was when we last saw CN=Adamsynccontact,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds.
Renaming target object CN=test,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds to CN=2395cd2c-6d90-4906-a5f0-4230c3c849d5,<WKGUID=ab8153b7768811d1aded00c04fd8d5cd,dc=app1,dc=msxfaq,dc=adlds>.
Renaming target object CN=adamsyncuser1,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds to CN=0c99190f-62f1-4088-84a6-002c67a5e579,<WKGUID=ab8153b7768811d1aded00c04fd8d5cd,dc=app1,dc=msxfaq,dc=adlds>.
Deleting target object CN=Adamsynccontact,OU=ADLDSSource1,DC=app1,DC=msxfaq,DC=adlds.
Finished aging run.
Saving Configuration File on DC=app1,DC=msxfaq,DC=adlds
Saved configuration file.

Interessant ist dabei, dass "Modifying attributes: lastagedchange" auch nicht jedes Mal gestartet wird, sondern ebenfalls nur in den Intervallen.

Anscheinend wird dann bei einem Import mit aktiviertem Aging auch das Feld lastagedchange mit dem aktuellen Zeitstempel aktualisiert und dann alle Objekte gelöscht, die beim aktuellen Import nicht enthalten waren.

Die Zählung der Durchläufe landet in der Configration.XML in der AD LDS Partition im Feld "runs-since-last-object-update":

<doc>
  </synchronizer-state>
    <runs-since-last-object-update>2</runs-since-last-object-update>
    <runs-since-last-full-sync></runs-since-last-full-sync>
  </synchronizer-state>
</doc>

Meinen Kontakt hat er direkt gelöscht während andere Objekte nach "LostAndFound" verschoben wurden:

Allerdings gibt es einige Aussagen von Microsoft zum Thema Aging, die vom Einsatz generell abraten.

aging in ADAMSync had a number of problems in WS03, some of which were fixed but I generally recommend that customers do not use aging. Blog posts that are still around from that time need to be treated with caution.
To get a deletion in AD to delete in AD LDS via ADAMSync does not generally required aging. By default in WS03SP1 and later a standard authenticated user account should be able to see deletes without any special permissions if you are running ADAMSync in security-mode "object".
Quelle: https://social.technet.microsoft.com/Forums/windowsserver/en-US/f3c907ad-65c7-4ca0-be26-2f8d9d127a39/adamsync-aging?forum=winserverDS

Wobei hier auch ein "... standard authenticated user account should be able to see deletes..." steht. Bei mir hat es aber nicht immer geklappt und das "Should" ist wohl wörtlich zu nehmen.

Alternativen

Sie müssen nicht ADAMSync nutzen. AD LDS ist auch einfach nur ein LDAP-Server, den Sie per LDAP sowohl Lesen als auch Schreiben schreiben können. Damit können sie jedes Programm oder Skript nutzen, um die gewünschten Informationen zu importieren. 

ADAMSync ist in der Funktion eingeschränkt. So kann es keine Felder umschreiben und repliziert nur vom Active Directory zu ADAM in einer Richtung. Auch werden keine Kennworte mit abgeglichen und auch das "Delete" ist über Aging gelöst.

Aus diesem Grunde gibt es mehrere Alternativen:

  • MIIS
    Der Microsoft Idendity Integration Server ist das Produkt von Microsoft, um mehrere Verzeichnisse miteinander abzugleichen. Schade dabei ist, dass der Server nicht mal schnell installiert ist, sondern er zum einen 25.000 US$ kostet, einen SQL-Server und Windows Enterprise Server benötigt und auch sonst eben nicht mal schnell für den Abgleich von einigen Kontakten eingesetzt werden kann. Man muss sich schon damit beschäftigen. Dafür kann er dann aber auch wirklich nahezu alle Quellen und Ziele beschreiben und auch Kennworte abgleichen
  • MiniSync
    Als "kleine Lösung" habe ich mir mit MiniSync eine auf VBScript basierte kleine Lösung geschaffen, mit der ich aus einem Verzeichnis Informationen auslese und in ein Ziel synchronisiere. Ich kann auch Feldinhalte im Ziel nach meinen Wünschen anpassen aber natürlich keine Kennworte abgleichen.

Es gibt mittlerweile noch andere Werkzeuge zum LDAP-Abgleich verschiedener Hersteller. Aber die wenigsten sind für kleines Geld zu bekommen. Auf der Seite Verzeichnisabgleich finden sie einige Link.

Einschätzung

ADAMSync funktioniert in den Grenzen, die diese Software nun mal hat. Sie ist definitiv keine leistungsfähige Synchronisationssoftware und insbesondere die eingeschränkte "Delete"-Funktion von losgelösten Objekten im AD LDS, die es in der Quelle nicht mehr gibt, dürfte immer wieder zu Problemen führen. Der "Fix" dies über ein Aging zu machen, bei dem alle aktiven Objekte mit einem Zeitstempel versehen werden, produziert auch nur viele überflüssige Änderungen im AD LDS, die CPU, DISK und Replikation belasten.

Allerdings ist es bei Windows dabei, wird hoffentlich aktualisiert und ist an vielen Stellen als möglich Lösung beschrieben. Bessere Tools sind kommerziell und damit teuer oder Skripte, die nicht jeder versteht oder anpassen kann und will.

In deutlichen Worten. „ADAMSYNC ist eine mangelhafte Software“. Mit dem Aging fixt der Entwickler etwas, was er bei richtiger Verwendung der DirSyncAPI nicht bräuchte

Sie haben also die Wahl, ob sie mit ADAMSync starten und ihr Glück versuchen oder sich eine andere Lösung ausgucken. Der AD LDS-Server darunter ist allerdings frei von jedem Zweifel.

Weitere Links

ADAMSYNC is a command-line utility that performs a one-way synchronization of data from Active Directory into ADAM. Adamsync uses an XML-based con-figuration file that drives the parameters of the ongoing synchronization