What would be the best way in C# to use directory entry to find all users with the attribute wWWHomePage filled in.
I am able to see if a specific user has it but I have not used Directory Entry to search all users for something like this.
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, myDomain, Login.authUserName, Login.authPassword);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username);
if (user != null) {
DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);
if (de != null) {
string whatIWant = de.Properties["wWWHomePage"].Value.ToString();
}
}
use DirectoryEntry with DirectorySearcher and specify the search Filter to get what you want.
the Filter template you want is :
(&(objectClass=user)(objectCategory=person)(PROPERTY_NAME=SEARCH_TERM))
where PROPERTY_NAME is the property you want to search in, and SEARCH_TERM is the value. you could use the * as a wildcard search, it would give you all objects that has this property.
here is a quick example :
// set the properties you need.
var propertiesToLoad = new string[] { "sAMAccountName", "wWWHomePage" };
using(var searcher = new DirectorySearcher(new DirectoryEntry(ldap), "(&(objectClass=user)(objectCategory=person)(wWWHomePage=*))", propertiesToLoad))
{
foreach (SearchResult result in searcher.FindAll())
{
if(result == null) continue;
var samAccount = result.Properties["sAMAccountName"][0];
var wWWHomePage = result.Properties["wWWHomePage"][0];
// complete the code with your logic
}
}
Related
Is it possible to get a user's email from Active Directory using employeenumber as the query term?
I am using C#'s System.DirectoryServices and am a little bit lost. The previous developer at my company was using this process, but he had an email and was querying for the employee number. I have changed it to what I believe it should be, but to be honest, I don't understand the code that well.
Is there something wrong with my code? every time i run it, I get a Null Reference error on the DirectoryEntry up_user = line. I assume it is because the previous line is not getting any entities.
Also, is there any good documentation on this topic? Everywhere I look, the posts are from 2011 or 2013.
I have the following:
try
{
string email = string.Empty;
ContextType authenticationType = ContextType.Domain;
PrincipalContext principalContext = new PrincipalContext(authenticationType, "MATRIC");
UserPrincipal userPrincipal = null;
userPrincipal = UserPrincipal.FindByIdentity(principalContext, empnum);
DirectoryEntry up_User = (DirectoryEntry)userPrincipal.GetUnderlyingObject();
DirectorySearcher deSearch = new DirectorySearcher(up_User);
SearchResultCollection results = deSearch.FindAll();
if (results != null && results.Count > 0)
{
ResultPropertyCollection rpc = results[0].Properties;
foreach (string rp in rpc.PropertyNames)
{
if (rp == "mail")
{
email = rpc["mail"][0].ToString();
}
}
if (email != string.Empty)
{
return email;
}
return null;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
UserPrincipal.FindByIdentity only works for finding a user by what AD considers an identifying attribute. These are listed in the IdentityType enumeration. The employee number isn't one of those.
Are you using employeeId or employeeNumber in AD? They are different attributes, although both are just strings with no special meaning or restrictions in AD.
The employeeId attribute is exposed in the UserPrincipal class, so you can search by it with UserPrincipal as described in the answer here:
UserPrincipal searchTemplate = new UserPrincipal(principalContext);
searchTemplate.EmployeeID = employeeId;
PrincipalSearcher ps = new PrincipalSearcher(searchTemplate);
UserPrincipal user = (UserPrincipal)ps.FindOne();
Then you can use the EmailAddress property of the account you find (you don't need to do what you're doing with the DirectorySearcher).
var emailAddress user?.EmailAddress;
If you're using employeeNumber, then you will need to use DirectorySearcher to find it. Something like this:
var search = new DirectorySearcher(new DirectoryEntry("LDAP://yourdomain.com"));
search.Filter = $"(&(ObjectClass=user)(employeeNumber={employeeNumber}))";
search.PropertiesToLoad.Add("mail");
var result = search.FindOne();
string emailAddress = null;
if (result.Properties.Contains("mail")) {
emailAddress = result.Properties["mail"][0].Value as string;
}
I am iterating through some Active Directory PrincipalSearcher Principal results using the code below. In my foreach loop where I am assigning result properties to my _user object I can break in debug and see a result.EmailAddress value. When I try to assign result.EmailAddress to _user.EmailAdress the code will not even compile. I'm guessing EmailAddress is a dynamic property of result. Is there a way to check for this property so I can add the users AD email address to my _user object?
private static void GetAllActiveDirectoryUsers()
{
PrincipalContext context = new PrincipalContext(
ContextType.Domain, Environment.UserDomainName);
UserPrincipal user = new UserPrincipal(context);
// create a principal searcher for running a search operation
PrincipalSearcher pS = new PrincipalSearcher(user);
// run the query
PrincipalSearchResult<Principal> results = pS.FindAll();
foreach (Principal result in results)
{
Console.WriteLine(result.DisplayName);
Console.ReadKey();
User _user = new User();
_user.Description = result.Description;
_user.DisplayName = result.DisplayName;
_user.DistinguishedName = result.DistinguishedName;
_user.Guid = result.Guid ?? null;
_user.Name = result.Name;
_user.Sid = result.Sid?.ToString();
Users.Add(_user);
}
}
It's not a dynamic property. EmailAddress is a property of the UserPrincipal class, which inherits from Principal. Your result is actually of type UserPrincipal, which is why you see the property when you debug, but you are accessing it as type Principal, which doesn't have a property called EmailAddress, so it is not available to you in your code.
If you want access to the EmailAddress property, you need to cast your result to UserPrincipal. Since you are sure all of your results will be user objects, then you can do that in the foreach:
foreach (UserPrincipal result in results)
You will want to get the DirectoryEntry object from the Principal and query it's properties.
Something like this - assuming email is stored in the 'mail' attribute.
var directoryEntry = result.GetUnderlyingObject() as DirectoryEntry;
if (directoryEntry != null && directoryEntry.Properties.Contains("mail"))
{
_user.EmailAddress = directoryEntry.Properties[property].Value.ToString();
}
Here is an extension method that uses the above. It simply takes a string for the attribute that you are searching for.
public static string GetPropertyValue(this Principal principal, string property)
{
var directoryEntry = principal.GetUnderlyingObject() as DirectoryEntry;
if (directoryEntry != null && directoryEntry.Properties.Contains(property))
{
return directoryEntry.Properties[property].Value.ToString();
}
return null;
}
I am trying to fetch all the users of particular group from Active Directory of LDAP server. Authentication becomes success but i am getting null in result.
Following is my code.
Domain-172.11.12.123
Email-sample#email.com
password-123456
using (var context = new DirectoryEntry(user.Domain, user.Email, user.Password, AuthenticationTypes.Secure))
{
try
{
string FirstName;
string LastName;
string ADUserName;
string Email;
using (var searcher = new DirectorySearcher(context))
{
searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname='user3'))";
List<string> Adusers = new List<string>();
System.DirectoryServices.SearchResult result = searcher.FindOne();
}
}
catch (Exception ex)
{
TempData["message"] = "error";
return RedirectToAction("Index", "ADuserList");
}
}
What wrong is going on.
Thanks in advance
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace.
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context for the currently connected AD domain
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// find the group in question
GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, "YourGroupNameHere");
// if found....
if (group != null)
{
// iterate over members
foreach (Principal p in group.GetMembers())
{
Console.WriteLine("{0}: {1}", p.StructuralObjectClass, p.DisplayName);
// do whatever you need to do to those members
}
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Read more about it here:
MSDN docs on System.DirectoryServices.AccountManagement
Update: in order to get all the users of a given OU, the approach is quite different.
You need to create a separate PrincipalContext that defines what OU you're interested in - then you need to use a PrincipalSearcher to get all the users from that OU:
// create your domain context and define what OU to use:
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, null, "OU=YourOU,OU=SubOU,dc=YourCompany,dc=com"))
{
// define a "query-by-example" principal - here, we search for any UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
}
I am trying to get the user information for a specific domain which will be the input of the program. On the basis of the domain name it should return the list of the users name/ or NT Id and SID of the user. I am new for the ldap programming can any one help me for get this list.
If you're on .NET 3.5 and up and talking about Active Directory, then you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// do something here....
var usersSid = user.Sid;
// not sure what you mean by "username" - the "DisplayName" ? The "SAMAccountName"??
var username = user.DisplayName;
var userSamAccountName = user.SamAccountName;
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Update: if you need to loop through all the users of a domain - try this:
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for a UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
UserPrincipal user = found as UserPrincipal;
if(user != null)
{
// do whatever here
var usersSid = user.Sid;
// not sure what you mean by "username" - the "DisplayName" ?
var username = user.DisplayName;
var userSamAccountName = user.SamAccountName;
}
}
Update #2: if you can't (or don't want to) use the S.DS.AM approach - which is the easiest, for Active Directory, by far - then you need to fall back to the System.DirectoryServices classes and methods:
// define the root of your search
DirectoryEntry root = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");
// set up DirectorySearcher
DirectorySearcher srch = new DirectorySearcher(root);
srch.Filter = "(objectCategory=Person)";
srch.SearchScope = SearchScope.Subtree;
// define properties to load
srch.PropertiesToLoad.Add("objectSid");
srch.PropertiesToLoad.Add("displayName");
// search the directory
foreach(SearchResult result in srch.FindAll())
{
// grab the data - if present
if(result.Properties["objectSid"] != null && result.Properties["objectSid"].Count > 1)
{
var sid = result.Properties["objectSid"][0];
}
if(result.Properties["displayName"] != null && result.Properties["displayName"].Count > 0)
{
var userName = result.Properties["displayName"][0].ToString();
}
}
I am querying information from Active Directory. I have code that works, but it's really slow.
This is the code I currently use:
static void Main(string[] args)
{
SearchResultCollection sResults = null;
try
{
//modify this line to include your domain name
string path = "LDAP://EXTECH";
//init a directory entry
DirectoryEntry dEntry = new DirectoryEntry(path);
//init a directory searcher
DirectorySearcher dSearcher = new DirectorySearcher(dEntry);
//This line applies a filter to the search specifying a username to search for
//modify this line to specify a user name. if you want to search for all
//users who start with k - set SearchString to "k"
dSearcher.Filter = "(&(objectClass=user))";
//perform search on active directory
sResults = dSearcher.FindAll();
//loop through results of search
foreach (SearchResult searchResult in sResults)
{
if (searchResult.Properties["CN"][0].ToString() == "Adit")
{
////loop through the ad properties
//foreach (string propertyKey in
//searchResult.Properties["st"])
//{
//pull the collection of objects with this key name
ResultPropertyValueCollection valueCollection =
searchResult.Properties["manager"];
foreach (Object propertyValue in valueCollection)
{
//loop through the values that have a specific name
//an example of a property that would have multiple
//collections for the same name would be memberof
//Console.WriteLine("Property Name: " + valueCollection..ToString());
Console.WriteLine("Property Value: " + (string)propertyValue.ToString());
//["sAMAccountName"][0].ToString();
}
//}
Console.WriteLine(" ");
}
}
}
catch (InvalidOperationException iOe)
{
//
}
catch (NotSupportedException nSe)
{
//
}
finally
{
// dispose of objects used
if (sResults != null)
sResults.Dispose();
}
Console.ReadLine();
}
What would faster code look like to get user information from AD?
You can call UserPrincipal.FindByIdentity inside System.DirectoryServices.AccountManagement:
using System.DirectoryServices.AccountManagement;
using (var pc = new PrincipalContext(ContextType.Domain, "MyDomainName"))
{
var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, "MyDomainName\\" + userName);
}
The reason why your code is slow is that your LDAP query retrieves every single user object in your domain even though you're only interested in one user with a common name of "Adit":
dSearcher.Filter = "(&(objectClass=user))";
So to optimize, you need to narrow your LDAP query to just the user you are interested in. Try something like:
dSearcher.Filter = "(&(objectClass=user)(cn=Adit))";
In addition, don't forget to dispose these objects when done:
DirectoryEntry dEntry
DirectorySearcher dSearcher
Well, if you know where your user lives in the AD hierarchy (e.g. quite possibly in the "Users" container, if it's a small network), you could also bind to the user account directly, instead of searching for it.
DirectoryEntry deUser = new DirectoryEntry("LDAP://cn=John Doe,cn=Users,dc=yourdomain,dc=com");
if (deUser != null)
{
... do something with your user
}
And if you're on .NET 3.5 already, you could even use the vastly expanded System.DirectorySrevices.AccountManagement namespace with strongly typed classes for each of the most common AD objects:
// bind to your domain
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "LDAP://dc=yourdomain,dc=com");
// find the user by identity (or many other ways)
UserPrincipal user = UserPrincipal.FindByIdentity(pc, "cn=John Doe");
There's loads of information out there on System.DirectoryServices.AccountManagement - check out this excellent article on MSDN by Joe Kaplan and Ethan Wilansky on the topic.
You can simplify this code to:
DirectorySearcher searcher = new DirectorySearcher();
searcher.Filter = "(&(objectCategory=user)(cn=steve.evans))";
SearchResultCollection results = searcher.FindAll();
if (results.Count == 1)
{
//do what you want to do
}
else if (results.Count == 0)
{
//user does not exist
}
else
{
//found more than one user
//something is wrong
}
If you can narrow down where the user is you can set searcher.SearchRoot to a specific OU that you know the user is under.
You should also use objectCategory instead of objectClass since objectCategory is indexed by default.
You should also consider searching on an attribute other than CN. For example it might make more sense to search on the username (sAMAccountName) since it's guaranteed to be unique.
I'm not sure how much of your "slowness" will be due to the loop you're doing to find entries with particular attribute values, but you can remove this loop by being more specific with your filter. Try this page for some guidance ... Search Filter Syntax