I worked recently on a small project to change Active Directory passwords. We’ve been producing and selling software to manage AD passwords for more than five years but we’ve been using lower-level APIs that are pretty close to the ADSI stack. Given that the .NET FCL now has great support for account management through the System.DirectoryServices.AccountManagement namespace, I decided it was time to employ the ChangePassword() method of the UserPrincipal class to change an AD user’s password.
The UserPrincipal class contains a slew of great methods to manage user accounts regardless of the backing store—AD, AD LDS, SAM, etc. And one of the two constructor overloads seems perfect to get a hook to the user account you want to modify. I grabbed a reference to my user by passing in the correct context, account name and credentials and figured I was ready to go. So I called UserPrincipal.ChangePassword() and guess what…no dice. Instead, I received the following error message.
The ChangePassword method can not be called on an unpersisted Principal object.
The message makes sense—you can’t change the password of a user who has not yet been saved (persisted) in the backing store—but I know the user is already persisted in Active Directory. What gives? I have a reference to the user account, don’t I?
Nope, I didn’t have a reference to the user account in AD after all. I found a little nugget in this SharePoint post as the author points out trying to do the same thing with the UserPrincipal constructor and finding the right solution using the UserPrincipal.FindByIdentity() factory method. I did the same thing and wallah! My change password code works now!
But I had to know a little more about why the UserPrincipal constructor didn’t have a hook to the AD user account I wanted to change. A simple review of the Remarks section of the constructor documentation reveals the reason for the error message and the motivation to use the constructor.
The user principal account is not persisted when it is created. To save the account, call the Save method.
So, here’s the rule with UserPrincipal: Use a factory method like FindByIdentity() when you need a hook to an existing account. Use the constructor when you are creating a new account. Don’t forget to call Save() after you set the new account’s properties!