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.
Related
static void Main(string[] args)
{
try
{
DirectoryEntry entry = new DirectoryEntry(
"LDAP://123.45.678.9:389/cn=TestGroup,ou=Groups,dc=test,dc=test2",
"uid=test_user, ou=test, dc=test,dc=test2", "test-abc",
AuthenticationTypes.None);
// List<string> GroupMembers = new List<string>() { "first_last", "first_last" };
StringCollection GroupMembers = new StringCollection();
Object obj = entry.NativeObject;
Console.WriteLine("login success");
DirectorySearcher search = new DirectorySearcher(entry);
search.PropertiesToLoad.Add("uniqueMember");
search.PropertiesToLoad.Add("uid");
search.PropertiesToLoad.Add("mail");
search.SearchScope = SearchScope.Subtree;
search.Filter = "(&(uniqueMember=*))";
//SearchResultCollection resultCollection = search.FindAll();
foreach (SearchResult result in search.FindAll())
{
ResultPropertyCollection resultProperty = result.Properties;
foreach (string GroupMemberDN in resultProperty["uniqueMember"])
{
DirectoryEntry directoryMember = new DirectoryEntry(
"LDAP://123.45.678.9:389/" + GroupMemberDN,
"uid=test_user, ou=test, dc=test2,dc=test3", "test-abc",
AuthenticationTypes.None);
PropertyCollection DirectoryMemberProperties = directoryMember.Properties;
GroupMembers.Add(directoryMember.Properties["mail"][0].ToString());
GroupMembers.GetEnumerator();
foreach (string member in GroupMembers)
{
Console.WriteLine(member);
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
}
}
the foreach loop repeats result of searchresult / resultpropertycollection more than once in a list of 31 users, such that the printed result in console is about 60-70 users, repeated a few times
for example:
test_user1#email.com
test_user1#email.com
test_user2#email.com
test_user3#email.com
test_user4#email.com
test_user5#email.com
test_user6#email.com
test_user7#email.com
test_user8#email.com
test_user9#email.com
test_user1#email.com
test_user1#email.com
test_user2#email.com
// and so on so forth in this pattern..
I'd just to retrieve the 31 users assigned in uniqueMembers of the cn=TestGroup to be printed once like with Console.WriteLine(
Everytime I run the Program, it works (yes) but not correctly.
This is likely because you're outputting the entire GroupMembers collection on each iteration of both the other two foreach loops.
Instead we should move the code that outputs the GroupMembers information to the console after the outer foreach loop (after you've finished populating it).
foreach (SearchResult result in search.FindAll())
{
ResultPropertyCollection resultProperties = result.Properties;
foreach (string groupMemberDN in resultProperties["uniqueMember"])
{
DirectoryEntry directoryMember = new DirectoryEntry(
"LDAP://123.45.678.9:389/" + groupMemberDN,
"uid=test_user, ou=test, dc=test2,dc=test3", "test-abc",
AuthenticationTypes.None);
GroupMembers.Add(directoryMember.Properties["mail"][0].ToString());
}
}
// Now that our collection is fully populated, output it to the console
foreach (string member in GroupMembers)
{
Console.WriteLine(member);
}
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()));
}
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'm trying to get bit locker recovery information for all users from active directory via below functions.
I've tried many different solution but none of them solved my problem.
I can get most of the computer properties like Operation System,Service Pack expect bit locker info.
Is there any special permission or method to search with msFVE-RecoveryInformation ?
Method 1:
DirectoryEntry child = new DirectoryEntry("LDAP://XXXXX");
DirectoryEntries enteries = child.Children;
DirectorySearcher ds = new DirectorySearcher(child);
ds.Filter = "(&(objectCategory=Computer)(cn=computerName))";
ds.SearchScope = SearchScope.Subtree;
SearchResultCollection item = ds.FindAll();
//string dn = item.GetDirectoryEntry().Properties["distinguishedname"].Value.ToString();
string temp1 = System.DirectoryServices.AccountManagement.UserPrincipal.Current.DistinguishedName;
child = new DirectoryEntry(("LDAP://" + temp1), "XXXXX", "XXXXX");
enteries = child.Children;
ds = new DirectorySearcher(child);
ds.Filter = "(objectClass=msFVE-RecoveryInformation)";
ds.SearchScope = SearchScope.Subtree;
SearchResultCollection result = ds.FindAll();
if (result.Count > 0)
{
foreach (SearchResult sr in result)
{
string recoveryKeyPackage = sr.GetDirectoryEntry().Properties["msFVE-KeyPackage"].Value.ToString();
string recoveryGUID = sr.GetDirectoryEntry().Properties["msFVE-RecoveryGuid"].Value.ToString();
string recoveryPassword = sr.GetDirectoryEntry().Properties["msFVE-RecoveryPassword"].Value.ToString();
string recoveryVolumeGUID = sr.GetDirectoryEntry().Properties["msFVE-VolumeGuid"].Value.ToString();
}
}
else
{
Console.Write ("No recovery information found!");
}
Method 2:
public Dictionary<string, string> GetBitlockerKeys(string computerName)
{
DirectoryEntry dEntry = new DirectoryEntry("LDAP://XXXX");
var keys = new Dictionary<string, string>();
using (var computerObject = dEntry)
{
var children = computerObject.Children;
var schemaFilter = children.SchemaFilter;
schemaFilter.Add("msFVE-RecoveryInformation");
foreach (DirectoryEntry child in children) using (child)
{
var recoveryGuid = new Guid((byte[])child.Properties["msFVE-RecoveryGuid"].Value).ToString();
var recoveryPassword = child.Properties["msFVE-RecoveryPassword"].Value.ToString();
if (!keys.ContainsKey(recoveryGuid))
{
keys.Add(recoveryGuid, recoveryPassword);
}
}
}
return keys;
}
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