c# DirectoryEntry.Properties vs DirectoryEntry.InvokeGet? - c#

I have a strange problem when I tried to retrieve the "AccountExpirationDate" from the active directory.
I use the following code to retrieve the user:
DirectoryEntry dirEntry = new DirectoryEntry(Path);
DirectorySearcher search = new DirectorySearcher(dirEntry);
// specify the search filter
search.Filter = "(&(objectClass=user)(mail=" + email + "))";
// perform the search
SearchResult result = search.FindOne();
DirectoryEntry user = result.GetDirectoryEntry();
And then I retrieve the "AccountExpirationDate":
object o1 = user.Properties["accountExpires"].Value; //return a COM object and I cannot retrieve anything from it
object o2 = user.Properties["AccountExpirationDate"].Value; //return null
object o3 = user.InvokeGet("AccountExpirationDate"); //return the DateTime
So I would like to what happened here?
Why I cannot use DirectoryEntry.Properties to retrieve the AccountExpirationDate?
What is the different between DirectoryEntry.Properties vs DirectoryEntry.InvokeGet?
Thanks a lot.

You can tell a directorySearcher which properties to load as follows:
// specify the search filter
search.Filter = "(&(objectClass=user)(mail=" + email + "))";
search.PropertiesToLoad.Add("AccountExpirationDate");
search.PropertiesToLoad.Add("displayname");
after performing search you need to go through the properties of the SearchResult to get values
i.e.
object o1 = result.Properties["AccountExpirationDate"][0];
DirectoryEntry.Properties - Gets the Active Directory Domain Services properties for this DirectoryEntry object.
DirectoryEntry.InvokeGet - Gets a property from the native Active Directory Domain Services object.
//Microsoft doesn't recommend the use of InvokeGet method.

Related

How can I find computers with LastLogonTimestamp less than a certain date OR null

The following code returns all the computerprincipals that have logon date prior to 3 months ago but does not get those with null for a lastlogontimestamp
PrincipalContext context = new PrincipalContext(ContextType.Domain);
PrincipalSearchResult<ComputerPrincipal> computers = ComputerPrincipal.FindByLogonTime(context, DateTime.Now.AddMonths(-3), MatchType.LessThanOrEquals);
How can I elegantly also add to "computers" those that have null valued "lastlogontimestamp" values?
I did away with the ComputerPrincipal.FindByLogonTime, since it can't find null LogonTime and went with the old classic, the DirectorySearcher
DirectorySearcher Computersearcher = new DirectorySearcher
{
SearchRoot = new DirectoryEntry(baseOU),
Filter = "(&(whenCreated<=" + WhenCreated + ")(!(userAccountControl=2))(|(lastLogonTimestamp<=" + DateInt + ")(lastLogonTimestamp=0))(objectClass=computer))",
SearchScope = SearchScope.Subtree,
PageSize = 1000,
Sort = new SortOption("Name", SortDirection.Ascending)
};
SearchResultCollection ComputerResults = Computersearcher.FindAll();
}
This has the unfortunate side effect that the observable collection that I used to create, no longer displays the Name in my WPF Listbox (despite setting DisplayNamePath).
A whole new issue, but the current one is "solved"

Search LastName and FirstName in AD

I want to retrieve the first name and last name of the user that is logged in his/her machine using AD. I use the following code:
string server = ConfigurationManager.AppSettings["ActiveDirectory.Server"];
DirectoryEntry entry = new DirectoryEntry(#"LDAP://" + server);
DirectorySearcher searcher = new DirectorySearcher(entry);
User user = GetUser(entry);
searcher.Filter = "sAMAccountName=" + user.UserAD;
searcher.PropertiesToLoad.Add("memberof");
SearchResult result = searcher.FindOne();
private static User GetUser(DirectoryEntry userEntry)
{
Usuario user = new User();
string[] username = HttpContext.Current.Request.ServerVariables["AUTH_USER"].Split('\\');
//THIS IS WHAT I NEED BUT IT DOES RETURN null.
//User.Name= (string)userEntry.Properties["givenName"].Value;
//User.LastName= (string)userEntry.Properties["sn"].Value;
user.Domain = username[0];
user.UserAD = username[1];
return user;
}
Now, I know searcher.PropertiesToLoad have a [memberof] and [adspath], the last one gives me the first and last name separated with a comma, something like CN="gates, billy" but I dont want to use substrings and index, is there any property like [firstName], [lastName] in the list properties?
I did search that DirectoryEntry have a property called givenName and sn but this returns null
The PropertiesToLoad set is exactly what you need to modify. Active Directory will return only the properties which are defined in this set, that's why you don't see givenName and sn. Just add these properties as well:
searcher.PropertiesToLoad.Add("givenName");
searcher.PropertiesToLoad.Add("sn");
Alternatively, just add the property * to load all of them:
searcher.PropertiesToLoad.Add("*");

Get "Home Directory" attribute from active directory

I'm trying to get Home Directory attribute value from active directory..
I used the following code:
public static void GetExchangeServerByWwidLdap(string wwid)
{
var exchange = string.Empty;
using (var ds = new DirectorySearcher())
{
ds.SearchRoot = new DirectoryEntry("GC:something");
ds.SearchScope = SearchScope.Subtree;
//construct search filter
string filter = "(&(objectclass=user)(objectcategory=person)";
filter += "(employeeid=" + wwid + "))";
ds.Filter = filter;
string[] requiredProperties = new string[] { "homeDirectory", "homemta" };
foreach (String property in requiredProperties)
ds.PropertiesToLoad.Add(property);
SearchResult result = ds.FindOne();
}
}
When I check result object data, I'm seeing only 2 values: "homemta" and "adspath".
Where is the "homeDirectory" value?
I entered AD website and searched the same values for same users - through the website I can see the all the data I searched for so I assuming that I have code issue somewhere.
What am I doing wrong?
You're trying to retrieve homeDirectory from global catalog.
It’s not there.
You can e.g. bind to the user by ADsPath property (i.e. “LDAP://…” string), then query the homeDirectory attribute of that user.
Or, if you only have a single domain, you can search within that domain instead of searching the GC. In this case you'll be able to retrieve all the properties you want.

Active Directory: find details of users in group without mass search

OK, working with active directory, my one remaining requirement is to pull back all the users in a given group and get their details - first name, last name, username, email.
Having read up around this I was surprised to discover that there doesn't seem to be a way of doing this in an efficient fashion. I've worked out two ways of getting the results, but both seem absurdly wasteful.
The first is to search members of a group, like this:
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + server, username, password);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = string.Format("(|(CN={0})(CN={1}))", "Group1", "Group2");
search.PropertiesToLoad.Add("member");
SearchResultCollection results = search.FindAll();
Which brings back a single result containing usernames, and usernames only. You can dissect this into an array of individual usernames, but then to get the details of each user you have to search AD again on a per-name basis.
The other approach is to do this:
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://" + server, username, password);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = string.Format("(&(&(objectClass=user)(objectCategory=person))(memberOf=*))");
search.PropertiesToLoad.Add("memberOf");
search.PropertiesToLoad.Add("name");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("displayName");
SearchResultCollection mySearchResultColl = search.FindAll();
foreach (SearchResult result in mySearchResultColl)
{
foreach (string prop in result.Properties["memberOf"])
{
if (prop.Contains("Group1") || prop.Contain("Group2"))
{
//add user to list
}
}
}
Which gets me what I want but involves retrieving every active directory user and then iterating through the collection to find ones that match. That works fine on my little test directory, but I shudder to think how slow it's going to be on a system with thousands of users.
I'm aware you can do what I need using the PrinicpalContext object, but as best I can tell that only works if the code is running in the same domain, which I can't guarantee. I need to be able to query across domains.
Is there a better way of doing this? Or am I just going to have to suck on the performance issues?
If you only need to find direct members, you can use Attribute Scope Query (ASQ).
This requires domain/forest functional level of 2003 (forgot domain or forest).
DirectoryEntry groupEntry = new DirectoryEntry("LDAP://<server>/<group DN>", "user", "pwd");
DirectorySearcher searcher = new DirectorySearcher(groupEntry);
searcher.SearchScope = SearchScope.Base;
searcher.AttributeScopeQuery = "member";
searcher.Filter = "(&(objectCategory=person)(objectClass=user))";
searcher.PropertiesToLoad.Clear();
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("displayName");
foreach (SearchResult result in searcher.FindAll())
{
Console.WriteLine(result.Path);
}
For nested group members, you may use the LDAP_MATCHING_RULE_IN_CHAIN matching rule.
This requires domain/forest functional level of 2008 R2 (again, forgot domain or forest).
DirectoryEntry rootEntry = new DirectoryEntry("GC://<server>", "user", "pwd");
DirectorySearcher searcher = new DirectorySearcher(rootEntry);
searcher.SearchScope = SearchScope.Subtree;
searcher.Filter = "(&(objectCategory=person)(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=<group DN>))";
searcher.PropertiesToLoad.Clear();
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("mail");
searcher.PropertiesToLoad.Add("displayName");
foreach (SearchResult result in searcher.FindAll())
{
Console.WriteLine(result.Path);
}
Limitations:
Both methods don't handle primary group membership
ASQ don't work across domain, an exception will be thrown if member from another domain is found. So normally you can only perform this for global groups.
You could utilize an ANR search for some of those attributes For more information, see this article - the functionality has been there since 2000:
http://support.microsoft.com/kb/243299
In order to search for other attributes not in the default set, you'd need to tweak the schema (which may not be suitable in your situation).

Active Directory search - filter by Manager

I'm trying to get a list of users from the Active Directory, who have a specified manager.
I used the following LDAP filter without success:
(manager=CN=Misterboss_n*)
However, it returns no result. Users have the following value in the manager attribute:
"CN=Misterboss_n,OU=xyz user,DC=xyz,DC=local"
What am I doing wrong? If I replace the above filter with something like this:
(givenName=John*)
it works okay (returns all users whose given name is John).
Wider context:
public List<ADUserDetail> GetAllEmployeesUnderMisterboss()
{
List<ADUserDetail> userlist = new List<ADUserDetail>();
string filter = "";
_directoryEntry = null;
DirectorySearcher directorySearch = new DirectorySearcher(SearchRoot);
directorySearch.Asynchronous = true;
directorySearch.CacheResults = true;
filter = "(manager=CN=Misterboss_n*)";
directorySearch.Filter = filter;
SearchResultCollection userCollection = directorySearch.FindAll();
foreach (SearchResult users in userCollection)
{
DirectoryEntry userEntry = new DirectoryEntry(users.Path, LDAPUser, LDAPPassword);
ADUserDetail userInfo = ADUserDetail.GetUser(userEntry);
userlist.Add(userInfo);
}
return userlist;
}
Thanks for the help!
I don't think there is a start-of-field search available for DN-typed properties. You will have to use the full DN of the manager. If you don't know the full DN, find the manager's LDAP object first and use its distinguishedName property.
Be sure to escape the DN value properly before building your filter - not every character that is valid in a DN is also valid in an LDAP filter expression:
* as \2a
( as \28
) as \29
\ as \5c
NUL as \00
/ as \2f
For code samples, see this related thread where I answered a very similar question: Getting all direct Reports from Active Directory

Categories