📜 ⬆️ ⬇️

Active Directory Sync

On duty, I had to deal with Active Directory. I had to read, experiment with classes, but as a result everything worked perfectly.

First of all I would like to describe a little Directory Synchronization object, which appeared in .net framework 2.0. You can read about it and other advantages of the 2nd framework on the microsoft website (http://msdn.microsoft.com/en-us/magazine/cc188700.aspx). Personally, the article helped me figure it out, although I do not have an abundance of information on the network for dyrSync.


For synchronization, I use the Web-Service, the actions are as follows:
')
1. The web service receives an LDAP request path.
[WebMethod]
public void Synchronize ( string DomainPath, string Filter, string EntryPath)
{

DirectoryEntry de = new DirectoryEntry (DomainPath);
using (SqlConnection conn = new SqlConnection (Globals.ConnectionString))
{
conn.Open ();
SqlCommand command = conn.CreateCommand ();
command.CommandText = "INSERT INTO SyncTable (Snapshot, OU) VALUES (@Snapshot, @OU, @ExchangeServer)" ;
command.Parameters.AddWithValue ( "@Snapshot" , GetSyncData (de, filter));
command.Parameters.AddWithValue ( "@OU" , EntryPath);

command.ExecuteNonQuery ();
conn.Close ();
}

}

2. GetSyncData returns AD cookie:
public byte [] DirectorySync (DirectoryEntry DomainDE, string Filter)
{
ADInit.Init ();
try
{
using (DirectorySearcher srch = new DirectorySearcher (DomainDE, Filter))
{
srch.SearchScope = SearchScope.Base;
srch.DirectorySynchronization = new DirectorySynchronization ();

foreach (SearchResult se in srch.FindAll ())
{
}

MemoryStream ms = new MemoryStream ();
BinaryFormatter bFormat = new BinaryFormatter ();
bFormat.Serialize (ms, srch.DirectorySynchronization.GetDirectorySynchronizationCookie ());
ms.Close ();

return ms.GetBuffer ();
}
}
catch (Exception Ex)
{
throw Ex;
}
}

We have to use the Domain as an entry point, because DirSynch works with the root element of the tree. The filter can be in the form: OU = myOU or something like that. In my case, I simply walked around this feature: I created a hierarchical structure from which you can choose both the LDAP path to the domain and the LDAP path to this object. Filter the same - through the name of the object that you want to synchronize.

3. Reverse sync + reconciliation
[WebMethod]
public bool GetRegionEntry ( string Domain, string Filter, string EntryPath)
{
DirectoryEntry de = new DirectoryEntry (Domain);

RegionInfo _regionInfo = regionEntry.GetEntry ();
using (SqlConnection conn = new SqlConnection (Globals.ConnectionString))
{
conn.Open ();
SqlCommand command = conn.CreateCommand ();
command.CommandText = "SELECT * FROM SyncTable WHERE OU = @OU" ;
command.Parameters.AddWithValue ( "@OU" , EntryPath);

SqlDataReader reader = command.ExecuteReader ();
if (reader.Read ())
{
return regionEntry.GetSyncDelta (de, Filter, ( byte []) reader [ "Snapshot" ]);
}
else
{
throw new Exception (“Can't read Sync Cookie from Database!”);

}
conn.Close ();
}
return _regionInfo;
}


Here, go to your discretion, or get a bool (there is a difference - there is no difference), or get the delta in full.
/// public Dictionary <string, object> GetSyncDelta (DirectoryEntry DomainDE, string Filter, byte [] _cookie)

public bool GetSyncDelta (DirectoryEntry DomainDE, string Filter, byte [] _cookie)
{
Dictionary < string , object > _delta = new Dictionary < string , object > ();

BinaryFormatter bf = new BinaryFormatter ();
byte [] cookie = ( byte []) bf.Deserialize ( new MemoryStream (_cookie));

DirectorySynchronization dirSync = new DirectorySynchronization (cookie);
DirectorySearcher srch = new DirectorySearcher (DomainDE, Filter);
srch.DirectorySynchronization = dirSync;

foreach (SearchResult sr in srch.FindAll ())
{
foreach ( string attrName in sr.Properties.PropertyNames)
{
_delta.Add (attrName, sr.Properties [attrName]);
}
return true ;
}

return false ;
// return _delta;
}


( Dictionary < string , object > is not serialized by standard methods in XML, try using your own arrays with keyvaluepair, or rewrite serialization).

4. In order to save the entire change history of the record, create another table, add an identifier field to it and put a trigger on INSERT / UPDATE:
ALTER TRIGGER [SyncTableTrg]
ON [dbo]. [SyncTable]
AFTER INSERT , UPDATE
AS
BEGIN
INSERT INTO SyncTableLog (OU, Snapshot)
SELECT OU, Snapshot FROM inserted
END


So get a good story about each of the records that are synchronized. Although this is not the only way.

Thanks for attention

ps do not judge strictly. first experience

Source: https://habr.com/ru/post/29180/


All Articles