I'm doing a Query for a certain user in AD and looking to create a list of multiple properties. Code Below. When I do searchResult.Properties["manager"] or searchResult.Properties["mail"] i get the correct result each way. But how would i search for multiple properties?
DirectoryEntry dEntry = new DirectoryEntry(path);
DirectorySearcher dSearcher = new DirectorySearcher(dEntry);
dSearcher.Filter = "(&(ObjectClass=user)(samaccountname=mcavanaugh))";
sResults = dSearcher.FindAll();
foreach (SearchResult searchResult in sResults)
{
var sAMAccountName = searchResult.Properties["samaccountname"][0].ToString().ToLower();
if (sAMAccountName == "mcavanaugh")
{
//Right here is where i would select multiple ad properties
ResultPropertyValueCollection valueCollection = searchResult.Properties["manager, mail"];
foreach (Object propertyValue in valueCollection)
{
var PropertyName = (string)propertyValue.ToString();
testlist.Text = PropertyName;
}
}
}
The Properties property doesn't have an option to access multiple properties at once. Mixing the values from different properties doesn't seem wise. Your best solution is to run the foreach twice, possibly creating a function to be DRY.
void addPropertyValues<T>(SearchResult sr, T testlist, string propName) {
foreach (var pv in sr[propName]) {
testlist.Text = pv.ToString();
}
}
which you can use in your if
if (sAMAccountName == "mcavanaugh") {
addPropertyValues(searchResult, testlist, "manager");
addPropertyValues(searchResult, testlist, "mail");
}
I've been working with this lately. I found where to place multiple properties and put them into an array, assigning them to properties. It's been working out pretty well so far. :
DirectoryEntry myLdapConnection = new DirectoryEntry("LDAP://DC=demo,DC=Com");
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = "(sAMAccountName=" + username + ")";
string[] requiredProperties = new string[] { "cn", "Displayname", "Title", "Department", "l", "Homedirectory", "telephoneNumber", "lockoutTime", "badlogoncount", "passwordexpired", "badPasswordTime", "whenCreated", "sAMAccountName", "pwdLastSet", "thumbnailPhoto", "givenName", "sn", "mail", "msRTCSIP-PrimaryUserAddress", "distinguishedName", "manager" };
foreach(String property in requiredProperties)
search.PropertiesToLoad.Add(property);
//next code will output to a usertextbox that I had set up in a Form. You can convert to console.writeline
if (searchResult != null) {
foreach(String property in requiredProperties)
foreach(Object myCollection in searchResult.Properties[property])
UserTextbox.Text += "\r\n" + (String.Format("{0,0} : {1} : {2}", property, myCollection.ToString(), myCollection.GetType().ToString()));
}
Related
I am using DirectorySearcher to try and get a list of users in AD to sync them with my App and have copied code from various SO sources, however I am not getting any property values. I'm using the following code:
DirectorySearcher search = new DirectorySearcher();
SearchResultCollection results = null;
string sDefaultOU = "LDAP://...";
DirectoryEntry de = new DirectoryEntry(sDefaultOU);
string userName = "DonaldDuck";
search = new DirectorySearcher
{
SearchRoot = de,
PropertiesToLoad = { "displayname", "sAMAccountName"},
Filter = "(sAMAccountName=" + userName + ")"
};
results = search.FindAll();
foreach (SearchResult result in results)
{
String name;
if (result.Properties["sAMAccountName"].Count > 0)
{
name = result.Properties["sAMAccountName"][0].ToString();
}
}
However, instead of name being equal to to "DonaldDuck", it will be "Byte[10]" or Byte[x] where x is the length.
Can anyone see what I am doing wrong.
If I add a filter it returns one user, so I am pretty sure the code is working in terms of searching
Apparently this issue has been faced by others: LDAP DirectoryEntry SearchResult returns data differently in Windows 8 than Win7
AD is using LDAPv3 encoding the values using UTF8, the solution mentioned in the link above might work for you:
if (result.Properties["sAMAccountName"][0].GetType().IsArray)
{
name = System.Text.Encoding.UTF8.GetString((byte[])result.Properties["sAMAccountName"][0]);
}
else
{
name = result.Properties["sAMAccountName"][0].ToString();
}
instead of
foreach (SearchResult result in results)
{
String name;
if (result.Properties["sAMAccountName"].Count > 0)
{
name = result.Properties["sAMAccountName"][0].ToString();
}
}
try
foreach (SearchResult result in results)
{
String name;
if (result.Properties["sAMAccountName"].Count > 0)
{
var thisDE=result.GetDirectoryEntry();
name = thisDE.Properties["sAMAccountName"].Value.ToString();
}
}
EDIT: Example
I use this helper method to search my domain (when userDomainAndName="DOMAIN\UserName") but you should be able to tweak it for what you want.
public static DirectoryEntry GetUserDirectoryEntryFromCurrentDomain(string userDomainAndName)
{
var Split = userDomainAndName.Split(#"\\".ToCharArray());
var DomainNetBiosNAme = Split[0];
var UserName = Split[1];
var QueryString = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={UserName}))";
DirectoryEntry rootDSE = GetDirectoryObject(
"LDAP://" + DomainNetBiosNAme + "/rootDSE");
string domain = "LDAP://" + (string)rootDSE.Properties[
"defaultNamingContext"][0];
var Searcher = new DirectorySearcher(new DirectoryEntry(domain), QueryString);
var Result = Searcher.FindOne();
var tReturn = Result.GetDirectoryEntry();
return tReturn;
}
then to get my users PrimarySMTP address (for example)..
var TheUsersDirectoryEntry=GetUserDirectoryEntryFromCurrentDomain(userDomainAndName);
var TheUsersPrimarySMTP=TheUsersDirectoryEntry.Properties["mail"].Value.ToString();
I try to get a List of all properties from an Object in the Active Directory.
What I have for now is this:
List<User> users = new List<User>();
try
{
DirectoryEntry root = new DirectoryEntry("LDAP://RootDSE");
root = new DirectoryEntry("LDAP://" + root.Properties["defaultNamingContext"][0]);
DirectorySearcher search = new DirectorySearcher(root);
search.Filter = "(&(objectClass=user)(objectCategory=person))";
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("displayname");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("telephoneNumber");
search.PropertiesToLoad.Add("department");
search.PropertiesToLoad.Add("title");
SearchResultCollection results = search.FindAll();
if (results != null)
{
foreach (SearchResult result in results)
{
foreach (DictionaryEntry property in result.Properties)
{
Debug.Write(property.Key + ": ");
foreach (var val in (property.Value as ResultPropertyValueCollection)) {
Debug.Write(val +"; ");
}
Debug.WriteLine("");
}
}
}
}
catch (Exception ex)
{
}
But it gets only the properties I added with PropertiesToLoad. Is it possible to get all properties dynamically?
If you don't specify anything in PropertiesToLoad, you should get all the properties. Just remove the lines with search.PropertiesToLoad.Add.
Getting all the properties of all the users in the domain could be pretty heavy, however.
To get all propertyNames you can do:
SearchResult result = search.FindOne();
ResultPropertyCollection temp = result.Properties;
string[] propertyNamesList = new string[temp.PropertyNames.Count];
temp.PropertyNames.CopyTo(propertyNamesList, 0);
and propertyNamesList will contain everything there.
Trying to learn LDAP queries in c# to access get all groups user is assigned to in active directory:
I am using System.DirectoryServices:
Havent tested it yet but from throwing examples together I have got:
//This should return all groups for particular user
public List<string> GetUserGroups(string UserName)
{
//create connection
DirectoryEntry entry = new DirectoryEntry(_lDAPPath);
DirectorySearcher search = new DirectorySearcher(entry);
//Get user with UserName
string query = "(&(objectCategory=User)(objectClass=person)(name=" + UserName + "*))";//(memberOf=*))";
search.Filter = query;
//properties returned by query
search.PropertiesToLoad.Add("memberOf");
search.PropertiesToLoad.Add("name");
System.DirectoryServices.SearchResultCollection mySearchResultColl = search.FindAll();
List<string> userGroups = new List<string>();
//Should only be one user in foreach loop
foreach (SearchResult result in mySearchResultColl)
{
//for user get each group assigned to
foreach (string prop in result.Properties["memberOf"])
{
if (prop.Contains(UserName))
{
//adds group name to string
userGroups.Add(result.Properties["memberOf"][0].ToString());
}
}
}
return userGroups;
}
hoping this works. does anyone see any poss problems? ta
It would be preferable to test your code and indicate any bugs you can't handle before posting. However here is tested code which I have been using for years. It searches by cn i.e. Common Name (user alias)
public static List<string> GetUserGroupDetails(string userName)
{
DirectorySearcher search = new DirectorySearcher();
List<string> groupsList = new List<string>();
search.Filter = String.Format("(cn={0})", userName);
search.PropertiesToLoad.Add("memberOf");
SearchResult result = search.FindOne();
if (result != null)
{
int groupCount = result.Properties["memberOf"].Count;
for (int counter = 0; counter < groupCount; counter++)
{
string s = (string)result.Properties["memberOf"][counter];
groupsList.Add(s);
// _log.DebugFormat("found group for user {0} : {1}", userName, s);
}
}
else
{
_log.Warn("no groups found for user " + userName);
}
return groupsList;
}
Note that the above code also returns email distribution lists for which the user is a member. When I want to exclude these I filter out the entries stating with "dl-".
I need to display a list of users (in autocomplete) with similar names from the AD. For example, if I search Arthur and there are 2 people with same name (first name or last name), I should display both the names in the autocomplete. I tried to get the names but it is taking around 2-4 minutes to get the names. I am using the following code:
string[] domainNames = new string[] { "domain1", "domain2" };
List<string> userNames = new List<string>();
string user = string.Empty;
foreach (string domain in domainNames)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, "Domain Users");
if (group != null)
{
foreach (Principal p in group.GetMembers(false))
{
if (p.Name.ToLower().Contains(key.ToLower()))
{
if (!userNames.Contains(p.Name))
userNames.Add(p.Name);
}
}
}
}
}
Any way I can speed up the process? I am already using ajax call.
The cn= filter isn't really valid - there's nothing guaranteeing that format. Instead, look up Ambigous Name Resolution - it's designed for this: http://social.technet.microsoft.com/wiki/contents/articles/22653.active-directory-ambiguous-name-resolution.aspx.
DirectorySearcher ds = new DirectorySearcher();
ds.SearchRoot = new DirectoryEntry("LDAP://" + domain, domain + #"\" + userName, password);
ds.Filter = "(&(objectClass=user)(cn=*" + key + "*))";
ds.PropertyNamesOnly = true;
ds.PropertiesToLoad.Add("name");
ds.PropertiesToLoad.Add("cn");
foreach (SearchResult searchResults in ds.FindAll())
{
foreach (string propertyName in searchResults.Properties.PropertyNames)
{
foreach (Object retEntry in searchResults.Properties[propertyName])
{
var user = retEntry.ToString().Split('/').Where(x => x.Contains("CN")).Select(y => y).FirstOrDefault().Split(',').Where(z => z.Contains("CN")).Select(c => c).FirstOrDefault().Split(',').FirstOrDefault().Split('=')[1];
if(!string.IsNullOrWhiteSpace(user))
userNames.Add(user);
}
}
}
Reduced to 30-40 seconds.
I have a method that searches Active Directory for Usernames based on an email address. There are cases where there may be multiple usernames for a given email address, and I'm trying to capture those. I have rewritten my method, but can't seem to get the syntax quite right. the issue is this line I believe.
foreach (Object myObject in result.Properties[property])
thanks,
Jason
private String FindNameByEmail(string emailAddress)
{
DirectoryEntry entry = GetDirectoryEntry();
emailAddress = txtEmailID.Text;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(&(objectCategory=person)(sAMAccountName=*)(mail=" + emailAddress + "))";
string[] properties = new string[] { "SAMAccountName" };
foreach (String property in properties)
search.PropertiesToLoad.Add(property);
SearchResultCollection result = search.FindAll();
if (result != null)
{
foreach (String property in properties)
foreach (Object myObject in result.Properties[property])
lblUserName.Text = myObject.ToString();
return "User Found";
}
else
{
lblStatus.Text = "user does not exist";
return "User Does Not Exist";
}
}
EDIT: Changed it to output to a list of strings
Here we go:
List<string> usernames = new List<string>();
if (result != null)
{
foreach (SearchResult sr in result)
{
usernames.Add((string)sr.Properties["SAMAccountName"][0]);
}
}
listBox1.DataSource = usernames; //Where listbox is declared in your markup
listBox1.DataBind();
Just Replace your if (result != null) logic with myne