Reading ldap group member from c# - c#

I have this code to connect to Active Directory and get all the groups that exist, it works and returns all the groups in results :
DirectoryEntry dirEnt = new DirectoryEntry();
using (DirectorySearcher srch = new DirectorySearcher(dirEnt, "(objectClass=Group)"))
{
srch.PageSize = 1000;
SearchResultCollection results = srch.FindAll();
}
I now want to return users of a specific group i.e. Administrators, how would I go about this?
I had tried changing (objectClass=Group) to (objectClass=Group)(cn=admin) but then it returns no results.
All the best

Here's a reference about how to in Active Directory:
Howto: (Almost) Everything In Active Directory via C#

Related

How to Get the list of all User Accounts of Windows in C#? [duplicate]

How can I get a list of users from active directory? Is there a way to pull username, firstname, lastname? I saw a similar post where this was used:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
I have never done anything with active directory so I am completely lost. Any help would be greatly appreciated!
If you are new to Active Directory, I suggest you should understand how Active Directory stores data first.
Active Directory is actually a LDAP server. Objects stored in LDAP server are stored hierarchically. It's very similar to you store your files in your file system. That's why it got the name Directory server and Active Directory
The containers and objects on Active Directory can be specified by a distinguished name. The distinguished name is like this CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com. Like a traditional relational database, you can run query against a LDAP server. It's called LDAP query.
There are a number of ways to run a LDAP query in .NET. You can use DirectorySearcher from System.DirectoryServices or SearchRequest from System.DirectoryServices.Protocol.
For your question, since you are asking to find user principal object specifically, I think the most intuitive way is to use PrincipalSearcher from System.DirectoryServices.AccountManagement. You can easily find a lot of different examples from google. Here is a sample that is doing exactly what you are asking for.
using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine();
}
}
}
Console.ReadLine();
Note that on the AD user object, there are a number of attributes. In particular, givenName will give you the First Name and sn will give you the Last Name. About the user name. I think you meant the user logon name. Note that there are two logon names on AD user object. One is samAccountName, which is also known as pre-Windows 2000 user logon name. userPrincipalName is generally used after Windows 2000.
If you want to filter y active accounts add this to Harvey's code:
UserPrincipal userPrin = new UserPrincipal(context);
userPrin.Enabled = true;
after the first using. Then add
searcher.QueryFilter = userPrin;
before the find all. And that should get you the active ones.
PrincipalContext for browsing the AD is ridiculously slow (only use it for .ValidateCredentials, see below), use DirectoryEntry instead and .PropertiesToLoad() so you only pay for what you need.
Filters and syntax here:
https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
Attributes here:
https://learn.microsoft.com/en-us/windows/win32/adschema/attributes-all
using (var root = new DirectoryEntry($"LDAP://{Domain}"))
{
using (var searcher = new DirectorySearcher(root))
{
// looking for a specific user
searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={username}))";
// I only care about what groups the user is a memberOf
searcher.PropertiesToLoad.Add("memberOf");
// FYI, non-null results means the user was found
var results = searcher.FindOne();
var properties = results?.Properties;
if (properties?.Contains("memberOf") == true)
{
// ... iterate over all the groups the user is a member of
}
}
}
Clean, simple, fast. No magic, no half-documented calls to .RefreshCache to grab the tokenGroups or to .Bind or .NativeObject in a try/catch to validate credentials.
For authenticating the user:
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(username, password);
}
Certainly the credit goes to #Harvey Kwok here, but I just wanted to add this example because in my case I wanted to get an actual List of UserPrincipals. It's probably more efficient to filter this query upfront, but in my small environment, it's just easier to pull everything and then filter as needed later from my list.
Depending on what you need, you may not need to cast to DirectoryEntry, but some properties are not available from UserPrincipal.
using (var searcher = new PrincipalSearcher(new UserPrincipal(new PrincipalContext(ContextType.Domain, Environment.UserDomainName))))
{
List<UserPrincipal> users = searcher.FindAll().Select(u => (UserPrincipal)u).ToList();
foreach(var u in users)
{
DirectoryEntry d = (DirectoryEntry)u.GetUnderlyingObject();
Console.WriteLine(d.Properties["GivenName"]?.Value?.ToString() + d.Properties["sn"]?.Value?.ToString());
}
}
Include the System.DirectoryServices.dll, then use the code below:
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
string userNames="Users: ";
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName == "User")
{
userNames += child.Name + Environment.NewLine ;
}
}
MessageBox.Show(userNames);

Remotely search AD for computer object without OU

Good Day All,
I have been asked to write out some code to find a computer object in AD I haven't been given the OU's that these objects could be in (which isn't helpful at all - obviously). I've determined that I can remotely connect to the AD server with the lines:
DirectoryEntry startingPoint = new DirectoryEntry("LDAP://DC=Zone,DC=Corp,DC=COM", "UserName", "Password");
//Find all OUs
DirectorySearcher searcher = new DirectorySearcher(startingPoint);
searcher.Filter = "(objectCategory=Computer)";
foreach (SearchResult result in searcher.FindAll())
{
}
Unfortunately, the only information I do have is that all the objects will be no further than 3 OU's deep. So I'd like to know how to go through each of the OU's going that deep in each one I come to until I find the designated host name. I'm not exactly sure how to work that out. Any assistance would be appreciated.
First, I would change the SearchScope to Subtree and then include the hostname as part of the filter. I would then use FindOne() instead of FindAll():
DirectoryEntry startingPoint = new DirectoryEntry("LDAP://DC=Zone,DC=Corp,DC=COM", "UserName", "Password");
DirectorySearcher searcher = new DirectorySearcher(startingPoint)
{
SearchScope = SearchScope.Subtree,
Filter = $"(&(objectCategory=Computer)(cn={hostname}))"
};
SearchResult result = searcher.FindOne();

How to get all groups on a local machine with managed code (no P/Invoke)

So I was looking on SO for the answer to this question and it seemed as everything I came across was for Active Directory or something similar. There were no examples for this for the local machine.
Basically the goal here is to get all the user groups in the system. The same ones you'd see if you launch Computer Management, traverse to System Tools > Local Users and Groups > Groups
using (PrincipalContext context = new PrincipalContext(ContextType.Machine))
{
using (GroupPrincipal groupPrincipal = new GroupPrincipal(context))
{
groupPrincipal.Name = "*";
PrincipalSearcher principalSearcher = new PrincipalSearcher();
principalSearcher.QueryFilter = groupPrincipal;
PrincipalSearchResult<Principal> groupList = principalSearcher.FindAll();
foreach (Principal group in groupList)
{
// Do something with info
}
}
}

How to Retrieve A/D User Information for a user

I am trying to reproduce some of the functionality of the app 'Active Directory Users and Computers", and am not finding an easy way to retrieve the A/D information for a given user, using the account name, or "samaccountname".
Currently, I create a DirectoryEntry using the domain user and password, then use the entry to instantiate a DirectorySearcher to perform a FindAll(). I then run through the resulting SearchResultCollection's SearchResults, resolving UserPrincipals and their various properties to assemble all the A/D users in the OU. This takes way too long.
DirectoryEntry entry = new DirectoryEntry(
LDAPString, domainuserid, password, AuthenticationTypes.Secure);
DirectorySearcher srch = new DirectorySearcher(entry);
SearchResultCollection results = srch.FindAll();
foreach (SearchResult sr in results)
{
DirectoryEntry de = sr.GetDirectoryEntry();
if (de.Name.IndexOf("CN=") > -1)
{
foreach (string propKey in sr.Properties.PropertyNames)
{
user = new ADUser();
UserPrincipal up = GetServiceUser(sr.Properties["samaccountname"][0].ToString());
user.AccountName = up.SamAccountName;
user.Name = up.Name;
foreach (string propKey in sr.Properties.PropertyNames)
{
switch (propKey.Trim().ToLower())
{
case "givenname":
user.SurName = sr.Properties[propKey][0].ToString();
break;
ETC...
}
}
}
}
}
I've done timings and this part of the process seems to take way more time than it should. It occurs to me that I might retrieve all the SamAccountNames of A/D users, display them in a list, and only when selected to retrieve all the relevant data for that user. I've not run into a way to do this retrieval one at a time, as-needed, and it seems that would be more efficient. Is there a programmatic way to get an A/D user's information solely by use of the SamAccountName?
Edited to Add:
I thought I had found an answer to this, but then found it to be not quite what I was looking for.
Once I wrote a little AD tool, I used FindOne to retrieve and modify detail data via the SamAccountName. I don't know exactly how performant it was compared to your solution (it really was some time ago), but it worked pretty well by then (also with plenty of items):
// get the user entry
var s = new DirectorySearcher(entry);
s.Filter = "(samaccountname=" + username + ")";
SearchResult user = s.FindOne();
// read / do some changes
var d = user.GetDirectoryEntry();
d.Properties[...]
d.Invoke(...);
d.CommitChanges();
You'll want to make use of the srch.Filter to refine your LDAP query:
DirectorySearcher srch = new DirectorySearcher(entry); // You already have this line
srch.Filter = string.Format("(&(objectCategory=person)(objectClass=user)(sAMAccountName={0}))", samAccountName);
Now you can actually just use srch.FindOne() instead of srch.FindAll() since samaccountname is unique.

How can I get a list of users from active directory?

How can I get a list of users from active directory? Is there a way to pull username, firstname, lastname? I saw a similar post where this was used:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
I have never done anything with active directory so I am completely lost. Any help would be greatly appreciated!
If you are new to Active Directory, I suggest you should understand how Active Directory stores data first.
Active Directory is actually a LDAP server. Objects stored in LDAP server are stored hierarchically. It's very similar to you store your files in your file system. That's why it got the name Directory server and Active Directory
The containers and objects on Active Directory can be specified by a distinguished name. The distinguished name is like this CN=SomeName,CN=SomeDirectory,DC=yourdomain,DC=com. Like a traditional relational database, you can run query against a LDAP server. It's called LDAP query.
There are a number of ways to run a LDAP query in .NET. You can use DirectorySearcher from System.DirectoryServices or SearchRequest from System.DirectoryServices.Protocol.
For your question, since you are asking to find user principal object specifically, I think the most intuitive way is to use PrincipalSearcher from System.DirectoryServices.AccountManagement. You can easily find a lot of different examples from google. Here is a sample that is doing exactly what you are asking for.
using (var context = new PrincipalContext(ContextType.Domain, "yourdomain.com"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine();
}
}
}
Console.ReadLine();
Note that on the AD user object, there are a number of attributes. In particular, givenName will give you the First Name and sn will give you the Last Name. About the user name. I think you meant the user logon name. Note that there are two logon names on AD user object. One is samAccountName, which is also known as pre-Windows 2000 user logon name. userPrincipalName is generally used after Windows 2000.
If you want to filter y active accounts add this to Harvey's code:
UserPrincipal userPrin = new UserPrincipal(context);
userPrin.Enabled = true;
after the first using. Then add
searcher.QueryFilter = userPrin;
before the find all. And that should get you the active ones.
PrincipalContext for browsing the AD is ridiculously slow (only use it for .ValidateCredentials, see below), use DirectoryEntry instead and .PropertiesToLoad() so you only pay for what you need.
Filters and syntax here:
https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
Attributes here:
https://learn.microsoft.com/en-us/windows/win32/adschema/attributes-all
using (var root = new DirectoryEntry($"LDAP://{Domain}"))
{
using (var searcher = new DirectorySearcher(root))
{
// looking for a specific user
searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={username}))";
// I only care about what groups the user is a memberOf
searcher.PropertiesToLoad.Add("memberOf");
// FYI, non-null results means the user was found
var results = searcher.FindOne();
var properties = results?.Properties;
if (properties?.Contains("memberOf") == true)
{
// ... iterate over all the groups the user is a member of
}
}
}
Clean, simple, fast. No magic, no half-documented calls to .RefreshCache to grab the tokenGroups or to .Bind or .NativeObject in a try/catch to validate credentials.
For authenticating the user:
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(username, password);
}
Certainly the credit goes to #Harvey Kwok here, but I just wanted to add this example because in my case I wanted to get an actual List of UserPrincipals. It's probably more efficient to filter this query upfront, but in my small environment, it's just easier to pull everything and then filter as needed later from my list.
Depending on what you need, you may not need to cast to DirectoryEntry, but some properties are not available from UserPrincipal.
using (var searcher = new PrincipalSearcher(new UserPrincipal(new PrincipalContext(ContextType.Domain, Environment.UserDomainName))))
{
List<UserPrincipal> users = searcher.FindAll().Select(u => (UserPrincipal)u).ToList();
foreach(var u in users)
{
DirectoryEntry d = (DirectoryEntry)u.GetUnderlyingObject();
Console.WriteLine(d.Properties["GivenName"]?.Value?.ToString() + d.Properties["sn"]?.Value?.ToString());
}
}
Include the System.DirectoryServices.dll, then use the code below:
DirectoryEntry directoryEntry = new DirectoryEntry("WinNT://" + Environment.MachineName);
string userNames="Users: ";
foreach (DirectoryEntry child in directoryEntry.Children)
{
if (child.SchemaClassName == "User")
{
userNames += child.Name + Environment.NewLine ;
}
}
MessageBox.Show(userNames);

Categories

Resources