Monday, 16 March 2015

C# - Change Service Account Username and Password

In C#, We modify Windows service account username and password using the WMI class Win32_Service and we can also update service account information using the Win32 API function ChangeServiceConfig. In this article, I am going to write C# examples to change service account name and password in local machine and remote machine.

Summary:

Change Service Account Information using Win32 API

Use the below C# sample code to update service account username and password. It uses the Win32 API function ChangeServiceConfig. The ChangeServiceConfig function changes the configuration information for the specified service in the service control manager database.
private const int SC_MANAGER_ALL_ACCESS = 0x000F003F;
private const uint SERVICE_NO_CHANGE = 0xffffffff; //this value is found in winsvc.h
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(IntPtr hService, UInt32 nServiceType, 
    UInt32 nStartType,UInt32 nErrorControl,String lpBinaryPathName,String lpLoadOrderGroup,
    IntPtr lpdwTagId, [In] char[] lpDependencies, String lpServiceStartName, 
    String lpPassword, String lpDisplayName);

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, 
     uint dwDesiredAccess);

[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, 
    CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(string machineName, string databaseName, 
     uint dwAccess);

public static bool ChangeServiceAccountInfo(string serviceName, string username,
       string password)
{
    try
    {
        IntPtr scm_Handle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
        if (scm_Handle == IntPtr.Zero)
          throw new System.Runtime.InteropServices.ExternalException(
                      "Open Service Manager Error");

        IntPtr service_Handle = OpenService(scm_Handle, serviceName,
                                        SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
        if (service_Handle == IntPtr.Zero)
          throw new System.Runtime.InteropServices.ExternalException("Open Service Error");
        //Changing the user account and password for the service.
        if (!ChangeServiceConfig(service_Handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 
             SERVICE_NO_CHANGE, null, null,IntPtr.Zero, null, username, password, null))
        {
            int nError = Marshal.GetLastWin32Error();
            Win32Exception win32Exception = new Win32Exception(nError);
            throw new System.Runtime.InteropServices.ExternalException("Could not change 
          user account and password : " + win32Exception.Message);
        }
        Console.WriteLine("Service account information changed successfully");
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
        return false;
    }
}

Change Service Login Username and Password using WMI in C#

The below C# code sample uses the WMI class Win32_Service to update windows service user name and password. To use WMI service, we need to add the reference System.Management.

Note: Use the fully qualified user name (i.e. TestDomain\Morgan) instead of simple user name (Morgan) if your remote computer is connected with Active Directory domain.

using System.Management;
//--------------------------
public static void ChangeServiceAccountInfobyWMI(string serviceName, string username,
          string password)
{
    string mgmntPath = string.Format("Win32_Service.Name='{0}'", serviceName);
    using (ManagementObject service = new ManagementObject(new ManagementPath(mgmntPath)))
    {
        object[] accountParams = new object[11];
        accountParams[6] = username;
        accountParams[7] = password;
        uint returnCode = (uint)service.InvokeMethod("Change", accountParams);
        if (returnCode == 0)
        {
             Console.WriteLine("Service account information changed successfully");
        }
        else
        {
             Console.WriteLine("Failed to change Service account information");
             Console.WriteLine("Error code: " + returnCode);
             // Support link to check the message for corresponding Return code:
             // https://msdn.microsoft.com/en-us/library/aa393660(v=vs.85).aspx
        }
    }
}

Update Service Account Username and Password in Remote Server

Use the below C# code sample to update windows service user name and password in remote machine using the WMI class Win32_Service. To use WMI service, we need to add the reference System.Management and use admin credentials if required to update service information in remote computer.

Note: Use the fully qualified user name (i.e. TestDomain\Morgan) instead of simple user name (Morgan) if your remote computer is connected with Active Directory domain.

If you are receiving error code, get the error message for corresponding return code from this Microsoft support link: https://msdn.microsoft.com/en-us/library/aa393660(v=vs.85).aspx


using System.Management;
//--------------------------
static void ChangeRemoteServiceAccountInfo(string remoteComputer, string serviceName, 
            string username, string password)
{
    try
    {
        ConnectionOptions connectionOptions = new ConnectionOptions();
        // Use credentials if needed
        //connectionOptions.Username = "Administrator";
        //connectionOptions.Password = "AdminPassword";
        //connectionOptions.Impersonation = ImpersonationLevel.Impersonate;
        ManagementScope scope = new ManagementScope("\\\\" + remoteComputer + 
                                             "\\root\\CIMV2", connectionOptions);
        scope.Connect();
        string mgmntPath = string.Format("Win32_Service.Name='{0}'", serviceName);
        using (ManagementObject service = new ManagementObject(scope,
                                 new ManagementPath(mgmntPath),new ObjectGetOptions()))
        {
            object[] accountParams = new object[11];
            accountParams[6] = username;
            accountParams[7] = password;
            uint returnCode = (uint)service.InvokeMethod("Change", accountParams);
            if (returnCode == 0)
            {
                Console.WriteLine("Service account information changed successfully");
            }
            else
            {
                Console.WriteLine("Failed to change Service account information");
                Console.WriteLine("Error code: " + returnCode);
                // Support link to check the message for corresponding Return code:
                // https://msdn.microsoft.com/en-us/library/aa393660(v=vs.85).aspx
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

Advertisements
Advertisements

13 comments:

  1. I couldn't change service account and password on remote computers.
    I got no errors, But also didn't get service account changed.

    ReplyDelete
    Replies
    1. Have you refreshed the service?. or close services.msc and reopen it

      Delete
    2. Yes, I did many times.
      is it work with windows server 2012 R2.

      Delete
    3. A timeout was reached (30000 milliseconds) while waiting for the Visual Studio ETW Event Collection Service service to connect.

      DCOM got error "1053" attempting to start the service VsEtwService120 with arguments "Unavailable" in order to run the server:
      {E72D8978-ABF1-48CC-9B25-8F2787A66C1E}

      I got these errors on event viewer when i tried to change service account on a local machine.

      Delete
    4. Hi Ahmed, I am working to re-produce the issue here. meanwhile, can you try to change the service account for any other service (i.e. "RemoteRegistry") other than VsEtwService120.

      In my initial analyze, I have found the problem may occurs due the service VsEtwService120 may have dependency of other services.

      Delete
    5. Hello Morgan, first of all thank you for your interest and help. And please not that I got above errors whenever I try to change the service account for any services. till now I have tried with SQL, Remote Registry and spooler services.

      Delete
    6. static void ChangeRemoteServiceAccountInfo(string remoteComputer, string serviceName,
      string username, string password)
      {
      try
      {
      ConnectionOptions connectionOptions = new ConnectionOptions();
      connectionOptions.Impersonation = ImpersonationLevel.Impersonate;
      ManagementScope scope = new ManagementScope("\\\\" + remoteComputer +
      "\\root\\CIMV2", connectionOptions);
      scope.Connect();
      string mgmntPath = string.Format("Win32_Service.Name='{0}'", serviceName);
      using (ManagementObject service = new ManagementObject(scope,
      new ManagementPath(mgmntPath), new ObjectGetOptions()))
      {
      object[] accountParams = new object[11];
      accountParams[6] = username;
      accountParams[7] = password;
      object outParams = service.InvokeMethod("Change", accountParams);
      if ((uint)outParams == 0)
      {
      Console.WriteLine("Service account information changed successfully");
      }
      else
      {
      Console.WriteLine("Failed to change Service account information");
      }
      }

      }
      catch (Exception ex)
      {
      Console.WriteLine(ex.ToString());
      }
      }

      Delete
    7. string password = System.Web.Security.Membership.GeneratePassword(20,6);
      UserPrincipal aduser = GetUser(accountbox.Text);
      aduser.SetPassword(password);
      string user = aduser.UserPrincipalName;
      string server = serverslist.SelectedValue;
      string service = servicelist.SelectedValue;
      ChangeRemoteServiceAccountInfo(server,service,user,password);

      Delete
    8. it's my code. did i do anything wrong.
      I'm using VS 2013 FW 4.5

      Delete
    9. Hi Ahmed, I have checked your code with VS 2013 and It is working fine if give the fully qualified user (i.e. TestDomain\username). Anyway, I have updated my article for more clarity. and please use my new code and check it again with domain user name (TestDomain\username).


      If you still receiving error code, get the error message for corresponding return code from this Microsoft support link: https://msdn.microsoft.com/en-us/library/aa393660(v=vs.85).aspx

      Delete
    10. Thanks a million :)
      It's working now

      Delete