I want to get incremental changes from Active Directory using C#. And for that I am trying to build a solution as mentioned in the following article.
When I am moving users from OU1 to OU2, then
highestcommittedusn
get incremented but when I am querying for changes (search.Filter = "(uSNChanged>=13000)"
) using following code, I DO NOT get any updates.same case is for adding a user inside an OU.
Sample Code is mentioned below:
public static void GetUpdates()
{
var myLdapConnection = createDirectoryEntry();
var search = new DirectorySearcher(myLdapConnection);
search.Filter = "(uSNChanged>=13000)";
search.SearchScope = System.DirectoryServices.SearchScope.Subtree;
var results = search.FindAll();
Console.WriteLine(results.Count);
}
public static DirectoryEntry createDirectoryEntry()
{
DirectoryEntry ldapConnection = new DirectoryEntry("LDAP://adfs.fed.abcd.com/DC=adfs,DC=fed,DC=abcd,DC=com");
ldapConnection.Path = "adfs.fed.abcd.com";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
private static long GetHighestUsn()
{
using (LdapConnection connection = new LdapConnection(ldapPath))
{
var filter = "(&(objectClass=*))";
var searchRequest = new SearchRequest(null, filter, System.DirectoryServices.Protocols.SearchScope.Base, "highestCommittedUSN");
var response = connection.SendRequest(searchRequest) as SearchResponse;
var usn = response.Entries[0].Attributes["highestcommittedusn"][0];
return Convert.ToInt64(usn);
}
return 0;
}
Any help is highly appreciated.
Edit:
- I have only ONE domain controller.
CodePudding user response:
For GetHighestUsn
I think the null
from new SearchRequest(null ...
should be replaced by an empty string to get RootDSE.
Secondly you mix System.DirectoryServices
and System.DirectoryServices.Protocols
: you should better stick to one. It is not clear what is the value for ldapPath
in new LdapConnection(ldapPath))
. If it is "adfs.fed.abcd.com" and you have more than one domain controller, you cannot know exactly which one will answer, this is related to last remark.
Last, USNChanged
attribute is a non-replicated attribute, meaning that you should always request the same domain controller to get updates. Another domain controller will store a complete different value for the same object.
Normally every object should return USNChanged
attribute unless you have specified a finite list of attributes to return in which case you must include it as well.
EDIT: Include sample code (Powershell)
- System.DirectoryServices
$entry = [System.DirectoryServices.DirectoryEntry]::new("LDAP://localhost:50005/O=MyAppInstance")
$entry.psbase.AuthenticationType = [System.DirectoryServices.AuthenticationTypes]::Secure
$searcher = [System.DirectoryServices.DirectorySearcher]::new($entry)
$searcher.Filter = "(uSNChanged>=13004)"
$searcher.SearchScope = [System.DirectoryServices.SearchScope]::Subtree
$results = $searcher.FindAll()
$results.Count
- System.DirectoryServices.Protocols
$con = [System.DirectoryServices.Protocols.LdapConnection]::new("localhost:50005")
$con.AuthType = [System.DirectoryServices.Protocols.AuthType]::Negotiate
$con.SessionOptions.ProtocolVersion = 3
$con.Bind()
$req = [System.DirectoryServices.Protocols.SearchRequest]::new()
$req.DistinguishedName = "O=MyAppInstance"
$req.Scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
$req.Filter = "(uSNChanged>=13004)"
$res = [System.DirectoryServices.Protocols.SearchResponse]$con.SendRequest($req)
$res.Entries.Count
CodePudding user response:
Answer: The code was correct.
I needed to provide "Read all user information" permission to the user (which was making the request) and after that I starting getting usnchanged property with every object including user.
Steps to enable permission is given in below article. https://social.technet.microsoft.com/Forums/en-US/b34f7295-4989-4440-93af-cebd6d66c711/cannot-read-the-usnchanged-attribute-for-some-users-why?forum=winserverDS