Querying AD from C# using DirectoryServices.AccountManagement
I ran across a need to query AD and make bulk changes to a subset of AD user accounts. When creating an account in Active Directory using a UserPrincipal object you must specify certain properties on the account. The ones I was interested in are as follows:
PasswordNotRequired
UserCannotChangePassword
PasswordNeverExpires
During my initial creation, I forgot to set PasswordNotRequired to false and needed to go back and set this on a group of accounts. My initial attempt had a problem…it only returns the first 1000 results:
using (var context = new PrincipalContext(ContextType.Domain, FQDC)) { UserPrincipal up = new UserPrincipal(context); up.EmployeeId = "*"; up.Enabled = true; PrincipalSearcher ps = new PrincipalSearcher(); ps.QueryFilter = up; PrincipalSearchResult<Principal> results = ps.FindAll(); if (results != null) ...
Here we setup a PrincipalContext and create a new UserPrincipal with it. We then set the search criteria (some value entered in the EmployeeID field and an Enabled account). After creating a PrincipalSearcher we set a QueryFilter and then return the results. Again, this works great but it only returns 1000 results. As I was expecting closer to 15000, this was a slight problem. 😉
So, how do we return all of the results? The solution is actually quite simple and I’ll highlight the one-line code change below.
... PrincipalSearcher ps = new PrincipalSearcher(up); ...
When creating the PrincipalSearcher, we need to pass in the UserPrincipal object to get the desired result. According to MSDN, the QueryFilter must be set (we set it in the example above) before passing it into the PS object. Also, you can go the long route and set the PageSize in the underlying DirectorySearchwe object, but I find this way to be much easier.
One other side note. Don’t forget how it easy it is to “parallelise” your operations.
if (results != null) { int count = 0; Parallel.ForEach (results, s => { code to do stuff.... }); }
.Net makes it very easy to make your operations parallel.
Great post! Setting the ps.QueryFilter = up was something I was leaving out – this post helped get me where I needed to go. Thought a couple more things worth mentioning: 1) I have no clue what FQDC is and I didn’t need an extra argument on that method. Works fine with just ContextType.Domain by itself. 2) You will not be able to test “results” for null or to do a .Count() on it without a “using System.Linq” line in your namespaces. I found this out the hard way and couldn’t find it anywhere until I tried doing a “.ToList()” on it like another coder had. That finally gave me the error that Linq was required. Since “results” always has a resultSet, even if empty, without Linq, it will claim it is not null, even with an empty resultsSet. Good luck to all the coders out there!