Active Directory in .Net 3.5
For one of my recent endeavors in planet .NET I had to deal with vanquishing a problem that required the use of Active Directory libraries in .NET. Having almost 0 experience in active directory libraries (apart from using the role providers once for an Asp.Net web site) and having full trust in Google I started to proceed..
I was surprised that "Google" failed me at first followed by MSDN followed by lack of any good examples (heh I need someone to blame : ) ). There were very few posts regarding using the .Net 3.5 Active Directory libraries. I did find a lot of examples using the 2.0 version. So here is a posting of all the links and some weird issues that I ran with the Libraries.
The best link I figured for knowing about these libraries is the one given below. Believe it or not, this never showed up in my normal Google search. It ended up being one of the links displayed when I was searching for something else..Anyway the best link is
http://msdn.microsoft.com/en-us/magazine/cc135979.aspx
Other links that I had found very useful were
- http://glorix.blogspot.com/2008/01/net-35-finally-brings-some-decent.html
- http://costoda.blogspot.com/2008/01/getting-information-from-active.html
These should be a very good place for getting you started.
Apart from that, here are some issues for which I was banging my head against the wall for a few hours. Hopefully these should save someone the pain
- Getting more than 1000 records returned (or 2000, 3000 whatever is configured in Active Directory) in the PrincipalSearcher
So everything seemed fine in ".NET AD Library Wonderland" and I was cruising around using the libraries until I found out that a wild card search returned only 1000 users when there should have been around 6000. Here was the code I had
using (var principalContext = new PrincipalContext(ContextType.Domain, Controller, Container))
{
var u = new UserPrincipal(principalContext);
// Set properties on the user principal object.
u.SamAccountName = "*";
u.Enabled = true;
// Create a PrincipalSearcher object to perform the search.
using (var ps = new PrincipalSearcher())
{
ps.QueryFilter = u;
PrincipalSearchResult<Principal> users = ps.FindAll();
}
}
So I figured that there should be a Limit setting on the Query filter or maybe on the PrincipalSearcher object that would allow me to set the result set size… But guess what there was NONE!!
Finally with some help from this link, I figured that you had to ALSO supply the UserPrincipal object to the PrincipalSearcher Object, and Just setting it to ps.QueryFilter only returns 1000 users..very weird. So here is the same code with the change shown.
using (var principalContext = new PrincipalContext(ContextType.Domain, Controller, Container))
{
var u = new UserPrincipal(principalContext);
// Set properties on the user principal object.
u.SamAccountName = "*";
u.Enabled = true;
// Create a PrincipalSearcher object to perform the search.
using (var ps = new PrincipalSearcher(u))
{
ps.QueryFilter = u;
PrincipalSearchResult<Principal> users = ps.FindAll();
}
}
- Getting the right "LastLogin" value!
Imagine my Joy when I found that you could just get the "LastLogin" value using the UserPrincipal object. Something like "principal.LastLogon". Unfortunately my Joy was short lived as I found that it always returned some sort of "Cached Value" instead of the most recent Login value (run against the same Domain Controller). I was able to use other tools, browse to the Domain Controller and see that the "last Login" date was different..
So I had to change the code to explicitly query for the "lastLogon" property explicitly as shown below..
DateTime? lastLoginDateFromAd = GetLastLoginDateExplicitly(principal);
And the method is:
private static DateTime? GetLastLoginDateExplicitly(UserPrincipal principal)
{
DateTime? fromDe = null;
var deUser = principal.GetUnderlyingObject() as DirectoryEntry;
if (deUser != null)
{
var lastLogonThisServer = new Int64();
if (deUser.Properties["lastLogon"].Value != null)
{
var lgInt =
(IADsLargeInteger) deUser.Properties["lastLogon"].Value;
lastLogonThisServer = ((long) lgInt.HighPart << 32) + lgInt.LowPart;
fromDe = DateTime.FromFileTime(lastLogonThisServer);
}
}
return fromDe;
}
Hope this helps someone..
Hari


Comments