DirectoryEntry to change password: Different behavior between Vista/Server2008

sholsapp picture sholsapp · Apr 11, 2010 · Viewed 11.5k times · Source

On a Vista dev machine I used this code successfully to change user "Administrator" password:

directoryEntry.Invoke("SetPassword", "new");

When I moved it over to my Server 2008 dev machine that code did not work, and I was forced to use the following code:

directoryEntry.Invoke("ChangePassword", new object[] { "old", "new" });

My question is, why?

For both cases, I created my DirectoryEntry object as such:

DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1}", computerName, username));

Thanks! 8)

In case you guys find it helpful, heres the actual code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.DirectoryServices;
using System.Security.Principal;

namespace AccountMod
{
   class Program
   {
        static void Main()
        {
           Console.WriteLine("Attempting reset...\n");
           try
           {
               String machineNameAndUser =    WindowsIdentity.GetCurrent().Name.ToString();
               String machineName =    WindowsIdentity.GetCurrent().Name.ToString().Substring(0,    machineNameAndUser.IndexOf('\\'));
            Console.WriteLine("Computer's name: " + machineName);
            ResetPassword(machineName, "Administrator", "new");
            //ChangePassword("Administrator", "current", "new");                      Console.WriteLine("Finished...");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
                Console.WriteLine(e.InnerException);
            }
            Console.ReadKey();

        }

        public static void ResetPassword(string computerName, string username, string newPassword)
        {
            DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1}", computerName, username));
            directoryEntry.Invoke("SetPassword", newPassword);
            //directoryEntry.Invoke("ChangePassword", new object[] { "current", "new" });
        }
    }
}

Answer

marc_s picture marc_s · Apr 11, 2010

Are you (or could you upgrade to) .NET 3.5? The AD integration for users, groups, computers has been massively improved in .NET 3.5 - check out the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 for details.

In your case, you could do something like:

// establish context for local machine
PrincipalContext ctx = new PrincipalContext(ContextType.Machine);

// find the "Administrator" account
UserPrincipal admin = UserPrincipal.FindByIdentity(ctx, "Administrator");

// set the password to a new value
admin.SetPassword("new-top-secret-password");
admin.Save();

and you're done! The WinNT: provider is very limited in what it can do and should be avoided if ever possible.