The other answers were poorly described, didn't describe how to implement them, and most gave the wrong filter properties. You don't even need to use .Filter
-- you can just assign your properties (last name = .Surname
, first name = .GivenName
) to a UserPrincipal
object, then search on that object using a PrincipalSearcher
in whatever event that triggers the search:
string firstName = txtFirstName.Text;
string lastName = txtLastName.Text;
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal up = new UserPrincipal(ctx);
if (!String.IsNullOrEmpty(firstName))
up.GivenName = firstName;
if (!String.IsNullOrEmpty(lastName))
up.Surname = lastName;
PrincipalSearcher srch = new PrincipalSearcher(up);
srch.QueryFilter = up;
I'm assuming you have textboxes for First and Last Name to get it, with IDs/Names of txtFirstName
and txtLastName
. Note that if you do not have a value in the property you are looking for, do not add it to the UserPrincipal
, or it will cause an exception. That's the reason for the checks I included, above.
You then do a .FindAll
on srch
to get search results into a PrincipalSearchResult
collection of Principal
objects:
using (PrincipalSearchResult<Principal> results = srch.FindAll())
{
if (results != null)
{
int resultCount = results.Count();
if (resultCount > 0) // we have results
{
foreach (Principal found in results)
{
string username = found.SamAccountName; // Note, this is not the full user ID! It does not include the domain.
}
}
}
}
Note that results won't be null even if its .Count()
is 0
, and why both checks are there.
You iterate using that foreach
to get the properties you need, and this answers the question of how to find a user in AD using C#, but note you can only get to a few properties using the Principal
object, and if I reached this question through Google (as I did), I would be very disheartened. If you find that's all you need - great, you're done! But in order to get the rest (and rest my own conscience), you have to dive down, and I'll describe how to do that.
I found you can't just use that username
I put above, but you have to get the whole DOMAIN\doej
kind of name. This is how you do that. Instead, put this in that foreach
loop, above:
string userId = GetUserIdFromPrincipal(found);
and use this function:
private static string GetUserIdFromPrincipal(Principal prin)
{
string upn = prin.UserPrincipalName;
string domain = upn.Split('@')[1];
domain = domain.Substring(0, domain.IndexOf(".YOURDOMAIN"));
// "domain" will be the subdomain the user belongs to.
// This may require edits depending on the organization.
return domain + @"\" + prin.SamAccountName;
}
Once you have that, you can call this function:
public static string[] GetUserProperties(string strUserName)
{
UserPrincipal up = GetUser(strUserName);
if (up != null)
{
string firstName = up.GivenName;
string lastName = up.Surname;
string middleInit = String.IsNullOrEmpty(up.MiddleName) ? "" : up.MiddleName.Substring(0, 1);
string email = up.EmailAddress;
string location = String.Empty;
string phone = String.Empty;
string office = String.Empty;
string dept = String.Empty;
DirectoryEntry de = (DirectoryEntry)up.GetUnderlyingObject();
DirectorySearcher ds = new DirectorySearcher(de);
ds.PropertiesToLoad.Add("l"); // city field, a.k.a location
ds.PropertiesToLoad.Add("telephonenumber");
ds.PropertiesToLoad.Add("department");
ds.PropertiesToLoad.Add("physicalDeliveryOfficeName");
SearchResultCollection results = ds.FindAll();
if (results != null && results.Count > 0)
{
ResultPropertyCollection rpc = results[0].Properties;
foreach (string rp in rpc.PropertyNames)
{
if (rp == "l") // this matches the "City" field in AD properties
location = rpc["l"][0].ToString();
if (rp == "telephonenumber")
phone = FormatPhoneNumber(rpc["telephonenumber"][0].ToString());
if (rp == "physicalDeliveryOfficeName")
office = rpc["physicalDeliveryOfficeName"][0].ToString();
if (rp == "department")
dept = rpc["department"][0].ToString();
}
}
string[] userProps = new string[10];
userProps[0] = strUserName;
userProps[1] = firstName;
userProps[2] = lastName;
userProps[3] = up.MiddleName;
userProps[4] = middleInit;
userProps[5] = email;
userProps[6] = location;
userProps[7] = phone;
userProps[8] = office;
userProps[9] = dept;
return userProps;
}
else
return null;
}
/// <summary>
/// Returns a UserPrincipal (AD) user object based on string userID being supplied
/// </summary>
/// <param name="strUserName">String form of User ID: domain\username</param>
/// <returns>UserPrincipal object</returns>
public static UserPrincipal GetUser(string strUserName)
{
PrincipalContext oPrincipalContext = new PrincipalContext(ContextType.Domain);
try
{
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(oPrincipalContext, strUserName);
return oUserPrincipal;
}
catch (Exception ex) { return null; }
}
public static string FormatPhoneNumber(string strPhoneNumber)
{
if (strPhoneNumber.Length > 0)
// return String.Format("{0:###-###-####}", strPhoneNumber); // formating does not work because strPhoneNumber is a string and not a number
return Regex.Replace(strPhoneNumber, @"(\d{3})(\d{3})(\d{4})", "$1-$2-$3");
else
return strPhoneNumber;
}
Note that the FormatPhoneNumber
function is for North American numbers. It will take a number it finds (##########
) and separate it into ###-###-####
.
You can then get the properties like this, back in that foreach
loop:
string[] userProps = GetUserProperties(userId);
string office = userProps[8];
But, as a whole solution, you can even add these results into a DataRow
column, and return it as part of a DataTable
that you could then bind to a ListView
or GridView
. This is how I did it, sending in a List<string>
filled with the properties I needed:
/// <summary>
/// Gets matches based on First and Last Names.
/// This function takes a list of acceptable properties:
/// USERNAME
/// MIDDLE_NAME
/// MIDDLE_INITIAL
/// EMAIL
/// LOCATION
/// PHONE
/// OFFICE
/// DEPARTMENT
///
/// The DataTable returned will have columns with these names, and firstName and lastName will be added to a column called "NAME"
/// as the first column, automatically.
/// </summary>
/// <param name="firstName"></param>
/// <param name="lastName"></param>
/// <param name="props"></param>
/// <returns>DataTable of columns from "props" based on first and last name results</returns>
public static DataTable GetUsersFromName(string firstName, string lastName, List<string> props)
{
string userId = String.Empty;
int resultCount = 0;
DataTable dt = new DataTable();
DataRow dr;
DataColumn dc;
// Always set the first column to the Name we pass in
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
dc.ColumnName = "NAME";
dt.Columns.Add(dc);
// Establish our property list as columns in our DataTable
if (props != null && props.Count > 0)
{
foreach (string s in props)
{
dc = new DataColumn();
dc.DataType = System.Type.GetType("System.String");
if (!String.IsNullOrEmpty(s))
{
dc.ColumnName = s;
dt.Columns.Add(dc);
}
}
}
// Start our search
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal up = new UserPrincipal(ctx);
if (!String.IsNullOrEmpty(firstName))
up.GivenName = firstName;
if (!String.IsNullOrEmpty(lastName))
up.Surname = lastName;
PrincipalSearcher srch = new PrincipalSearcher(up);
srch.QueryFilter = up;
using (PrincipalSearchResult<Principal> results = srch.FindAll())
{
if (results != null)
{
resultCount = results.Count();
if (resultCount > 0) // we have results
{
foreach (Principal found in results)
{
// Iterate results, set into DataRow, add to DataTable
dr = dt.NewRow();
dr["NAME"] = found.DisplayName;
if (props != null && props.Count > 0)
{
userId = GetUserIdFromPrincipal(found);
// Get other properties
string[] userProps = GetUserProperties(userId);
foreach (string s in props)
{
if (s == "USERNAME")
dr["USERNAME"] = userId;
if (s == "MIDDLE_NAME")
dr["MIDDLE_NAME"] = userProps[3];
if (s == "MIDDLE_INITIAL")
dr["MIDDLE_INITIAL"] = userProps[4];
if (s == "EMAIL")
dr["EMAIL"] = userProps[5];
if (s == "LOCATION")
dr["LOCATION"] = userProps[6];
if (s == "PHONE")
dr["PHONE"] = userProps[7];
if (s == "OFFICE")
dr["OFFICE"] = userProps[8];
if (s == "DEPARTMENT")
dr["DEPARTMENT"] = userProps[9];
}
}
dt.Rows.Add(dr);
}
}
}
}
return dt;
}
You would call this function like this:
string firstName = txtFirstName.Text;
string lastName = txtLastName.Text;
List<string> props = new List<string>();
props.Add("OFFICE");
props.Add("DEPARTMENT");
props.Add("LOCATION");
props.Add("USERNAME");
DataTable dt = GetUsersFromName(firstName, lastName, props);
The DataTable
will be filled with those columns, and a NAME
column as the first column, that will have the user's actual .DisplayName
from AD.
Note: You must reference System.DirectoryServices
and System.DirectoryServices.AccountManagement
, System.Text.RegularExpressions
, System.Data
to use all this.
HTH!