Search code examples
.netwindows-servicesinstallation

Installing a Windows service to run as virtual user


I have a ServiceProcessInstaller which installs a .NET Windows Service.

The install process works perfectly if I either:

  • Set the service to run as SYSTEM (serviceProcessInstaller1.Account = ServiceAccount.LocalSystem).
  • Set the service to run as a normal user (serviceProcessInstaller1.Account = ServiceAccount.User) by either specifying the Username and Password properties, or letting the install process prompt me.

However, I would like the service to run as a virtual user, a la NT Service\ServiceName. If you look at the some of the SQL Server services, you will see they default to log on as their own virtual user account. There is more, albeit limited, information at http://technet.microsoft.com/en-us/library/dd548356.aspx.


I have tried setting serviceProcessInstaller1.Username = @"NT Service\ServiceName", but the installer then throws the following error no matter what I give as the password (I have tried String.Empty, same as the user name, my own password, null to bring up the interactive dialog, and even random junk):

No mapping between account names and security IDs was done

However, if I install the service normally (e.g. run as SYSTEM), I can then go in to the service's properties from the services.msc snap-in, on the Log On page change the user to NT Service\ServiceName, and it works beautifully.

I have also looked into the ChangeServiceConfig2 function but I can't seem to get it to change anything either.


How can I set the log on user to the virtual user NT Service\ServiceName from code within my ServiceProcessInstaller?


Solution

  • You cannot do it directly with the ServiceProcessInstaller object. However, you can set the username after the service is installed, in the ServiceInstaller.Committed event, using the Change method in WMI. Specify the username as wmiParams[6] and leave the password null:

    void serviceInstaller1_Committed(object sender, InstallEventArgs e)
    {
        using (ManagementObject service = new ManagementObject(new ManagementPath("Win32_Service.Name='ServiceName'")))
        {
            object[] wmiParams = new object[11];
            wmiParams[6] = @"NT Service\ServiceName";
            service.InvokeMethod("Change", wmiParams);
        }
    }
    

    Finally, don't forget to give the user read/execute permission on your service exe and config files, or you will get an Access Denied error.