How to get All attributes from an Active Directory user in C#
All attributes that any class can have are defined in Active Directory Schema
Use this to query for the user class. Then just call GetAllProperties method
var context = new DirectoryContext(DirectoryContextType.Forest, "amber.local");
using (var schema = System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema.GetSchema(context))
{
var userClass = schema.FindClass("user");
foreach (ActiveDirectorySchemaProperty property in userClass.GetAllProperties())
{
// property.Name is what you're looking for
}
}
However AD schema may vary from one AD environment to another. For example, third party programs or Exchange Server may extend schema with custom attributes. It means that the solution with pre-defined columns will work only for a specific environment.
Caspi
Updated on June 05, 2022Comments
-
Caspi almost 2 years
I have been searching for quite some time for a solution using C# code that can query an Active Directory user for all the attributes it has registered to it, whether or not they have a NULL Value. These attributes are visible through the Attribute editor tab in the properties of the user in ADSI Edit on the domain server.
AD user attributes in ADSI edit
I need to dynamically retrieve these attributes, which means I probably can't reliably get these attribute names through the ADSI documentation on MSDN and because not all of these attributes might be user object specific: https://msdn.microsoft.com/en-us/library/ms675090(v=vs.85).aspx
Here is what I have tried so far, but only got a fraction of the attributes of the user object:
PS command
Get-ADUser -Identity administrator -Properties
: This retrieved a good part of the attributes, but not nearly all of them and I do not know what .NET Classes and methods are invoked during this command, sinceTypeName = Microsoft.ActiveDirectory.Management.ADUser
, which does not exist in the .NET framework. How can I see the specific methods that are using from .NET in PS?-
C# calling this method:
public bool GetUserAttributes(out List<string> userAttributes, string userName) { userAttributes = new List<string>(); var valueReturn = false; try { const string pathNameDomain = "LDAP://test.local"; var directoryEntry = new DirectoryEntry(pathNameDomain); var directorySearcher = new DirectorySearcher(directoryEntry) { Filter = "(&(objectClass=user)(sAMAccountName=" + userName + "))" }; var searchResults = directorySearcher.FindAll(); valueReturn = searchResults.Count > 0; StreamWriter writer = new StreamWriter("C:\\LDAPGETUSERADEXAMPLE.txt"); foreach (SearchResult searchResult in searchResults) { foreach (var valueCollection in searchResult.Properties.PropertyNames) { userAttributes.Add(valueCollection.ToString() + " = " + searchResult.Properties[valueCollection.ToString()][0].ToString()); try { writer.WriteLine("Bruger attribut:" + valueCollection); } catch (Exception) { throw; } } }
-
C# calling this method:
public List<string> GetADUserAttributes() { string objectDn = "CN=testuser,OU=TEST,DC=test,DC=local"; DirectoryEntry objRootDSE = new DirectoryEntry("LDAP://" + objectDn); List<string> attributes = new List<string>(); foreach (string attribute in objRootDSE.Properties.PropertyNames) { attributes.Add(attribute); } return attributes; }
What should I do to not filter out any attributes of the user object I am trying to retrieve from?
I am aware that Active Directory by default will only shows attributes that are default or have a value in them, I am trying to overcome this limitation.
EDIT 1:
I have temporarily postponed the specific question. I have been trying to benchmark which of these methods are the fastest at retrieving (READ Operation) the SAM account name of 10.000 individual AD users called for example "testuser", the methods I benchmark are the following:
- Time to complete: about 500 msec : ADSI - system.directoryservices
- Time to complete: about 2700 msec: Principal - searcher system.directoryservices.accountmanagement
- Time to complete: about NOT WORKING :LDAP - System.DirectoryServices.Protocols
- Time to complete: about 60 msec : SQL - System.Data.SqlClient
I am querying for the user information from a workstation - Windows 10 machine in the domain I am querying. the workstation (4 vcpu), DC (2vpu) and DB (2vcpu) server is run as Hyper V vm's.
-
David L over 7 yearsLink-only or link-primarily answers are strongly discouraged. Please consider citing the source but copying the relevant solution into your answer.
-
Caspi over 7 years@Dimitry: Thanks it seems just what I was looking for, I will test the solution and let you know if it worked :) In terms of the schema being limited to a specific environment, I am trying to solve this problem by dynamically creating the DB columns upon installation, rather than having fixed columns, which Is why I can't rely on documentation on the attributes for my needs. I do realise that I will need to watch for changes to the schema and extend my DB structure if the schema changes as a result. However the purpose of the database is only to be read from, by the frontend website as cache
-
oldovets over 7 yearsBe aware, in case if you are going to store AD data in cache you will have to synchronize it with AD. E. g. somebody changes firstname attribute of a user. In other words your cache should replicate with AD. This can be done by several methods (DirSync, USN polling). Other solution is to collect the entire AD snapshot (e. g. all users in ad) periodically.
-
Caspi over 7 years@Dimitry: I have already thought about that challenge, but didn't know how to watch the AD changes, but checking for USN is offcourse exactly what is needed. can you provide a couple of references to the mentioned methods. I plan to do realtime replication watching the USN number and writing the change to my DB, however I don't know yet how much overhead will incur to ensure a very fast replication, but I believe that the domain controllers can handle this, since they replicate each other all the time...
-
oldovets over 7 yearsDon't use the Change Notifications technique. It simply does not work. Also I'd recommend you to read The .NET Developer's Guide to Directory Services Programming book. It contains examples of how to track changes using Directory Synchronization. The replication process is very fast. Domain controller will return changes, occurred since the moment of last replication. There are some tricky parts that you have to handle, e. g. sync membership in case of user rename\deletion (rename\delete user in group membership in DB as LDAP will return you distinguished names instead of SID)
-
oldovets over 7 yearsAnd I'd recommend you to use LDAP classes (not ADSI). These classes are located under System.DirectoryServices.Protocols namespace (LdapConnection, etc). From my experience, methods of classes like DirectoryEntry\DirectorySearcher may hang with no response occasionally, so the entire application hangs forever
-
Caspi over 7 years@DmitryAlexandrov: Can you provide any ADSI sample code that would cause the application to hang, so I can test this my self? I am trying to benchmark some different methods of getting the AD user data. At the moment I am strugling to get the attributes of the user object "testuser" I retrieved through System.DirectoryServices.Protocols SendRequestst() method. It seems the attributes of the user object is now being retrieved, as a query on any of the attributes, like "cn" results in NULLReferenceexception even though attributes have not been filtered...
-
oldovets over 7 yearsSuch code may hang: using (var entry = new DirectoryEntry("LDAP://contoso.com")) { string[] attributes = {"distinguishedName", "canonicalName"}; DirectorySearcher ds = new DirectorySearcher(entry, "(objectClass=domainDNS)", attributes, SearchScope.Base); var result = ds.FindOne(); } It will work in 99% environments and in 1% it will hang periodically
-
oldovets over 7 yearsMoreover, there is a known memory leak issue using methods like DirectorySearcher.FindXXX. See: stackoverflow.com/questions/5631972/… and connect.microsoft.com/VisualStudio/feedback/details/750167/…. Here is an example of LdapSeacher based on LdapConnection: dunnry.com/blog/2008/06/05/…. I use pretty similar code in my project and everything works fine
-
oldovets over 7 yearsAnd be aware, in case if you need to search for deleted objects in tombstone you need to add ShowDeletedControl to you controls collection when doing SendRequest and if you need the query to also return ntSecurityDescriptor attribute for an object see the following MSDN article: msdn.microsoft.com/en-us/library/windows/desktop/…
-
oldovets over 7 yearsAlso, you may look at LinqToLdap framework: linqtoldap.codeplex.com Didn't use it by myself.
-
oldovets over 7 yearsAnd according to perfomance: System.DirectoryServices.AccountManagement classes use ADSI. ADSI classes call LDAP functions under the hood. So in case of performance the chain will be like LDAP -> ADSI -> AccountManagement, where LDAP classes are the fastest in the chain. However in case if you are going to use SQL, SQL update queries will be 10 or more times slower than any Active Directory queries (my project is doing the same thing - it synchronizes MS SQL database with current AD state). Look at ADUC\ADSIEdit tools. They query LDAP on the fly. If the performance suits you, just do the same
-
Caspi over 7 years@DmitryAlexandrov. Thanks for all the comments, it is really helpful, I haven't been to active on resolving this questions, since at the moment I am trying to design this AD query functionality in a ASP.NET application. I have hower made progress on setting some read benchmarks, see EDIT 1 for more information. Though I haven't been able to get LDAP protocols to work, so thanks for the code examples on that. Regarding Directoryservices hanging and memory leaks, were you able to zero in on the cause of that, since I haven't experied it hanging at the moment.
-
oldovets over 7 years@Caspi: The program hangs on WaitForSingleObject method on socket while waiting for network response from the remote computer. To discover more deeply I would have to disassemble native code. Had not enough time to do that. Regarding leak error you can also read the following post stackoverflow.com/questions/10291009/…. To avoid hangs on DirectoryEntry.RefreshCache method I created async version of the method that throws timeout if hangs.
-
oldovets over 7 yearsAlso, you may want to take a look at direct AD querying using Jet API. See en.wikipedia.org/wiki/Extensible_Storage_Engine blogs.msdn.microsoft.com/windowssdk/2008/10/22/… and managedesent.codeplex.com
-
Caspi over 7 years@DmitryAlexandrov: Thank you so much, the answer you proposed was exactly what I was looking for and it has all the attributes that was missing from querying the active directory user through other means. However I have one additional question: "Can you though querying the Schema also get the value of a particular property or perhaps also change it and submit the change to the active directory?"
-
oldovets over 7 yearsActviveDirectorySchemaProperty class contains Syntax field - enum, that shows you what type of value is stored in the attribute. You need to parse the attribute value according to its syntax. For example, objectGuid property has OctetString type, which means that the value stored as an array of bytes. Also attribute can have single value or multiple values (IsSingleValued is responsible for that). If you want to change and commit the value you need to know the attribute syntax to convert the value in required format and set it to the object.
-
Caspi over 7 years@DmitryAlexandrov: That makes a lot of sence, had not looked into Syntax property at the time, thank you again for all your invaluable knowledge :) Perhaps we could chat together in stackoverflow chat, perhaps I could also provide som valuable knowledge like you have done :)
-
oldovets over 7 years@Caspi: You're welcome, BTW, feel free to contact me in chat if you have any further questions or issues regarding your AD project
-
Caspi over 4 years@oldovets I know its a long time since I made this post but I have some things I would like to discuss with you regarding my project in chat if you have the interest and time. Can I contact you?
-
oldovets over 4 years@Caspi Yes, sure
-
Caspi over 4 years@oldovets awesome, I cant seem to find a way to contact you through your profile. I guess its because I dont have enough reputation yet. So could you perhaps contact me instead on my profile if that is an option? :) Alternatively you can connect with me on my linkedin profile: www.linkedin.com/in/casper-rubæk
-
oldovets about 4 years@Caspi ok, I’ll contact you via linked in then
-
oldovets about 4 years@Caspi forgot my linked in password and can’t restore it. Let’s make it simple. Add me via skype. My nickname is oldovets
-
Caspi about 4 years@oldovets sorry I cant find you on skype. perhaps you can add me instead on join.skype.com/invite/fi57HzS5i0Am
-
oldovets about 4 years@Caspi added you via skype. Did you receive my messages?
-
Caspi about 4 years@oldovets. Thanks I have replied to your message :)
-
oldovets about 4 years@Caspi strange, I do not see any from you. Skype issue maybe