Updating a package with a Windows service resets the service account and password

I am working on an MSI installer with WiX. I try to keep it as simple as possible: it is an internal product and my users are our IT professionals.

The product includes a Windows service that must be configured to run under a different account for each machine.

The workflow I planned for my users (for the initial installation) is as follows:

  • Run the installer (The installer installs the service under the default account)
  • Stop service via sc

    or local services applet
  • Update the properties of the service that will run under the appropriate account for the specific machine. (The account is different for each machine, and only the IT staff has access to passwords.)
  • Restart the service

Subsequent updates consist of installing the updated MSI files.

Testing the "small" update, I was surprised to find that the installer reset is back to working under the default account. This is a serious problem for me because it is very difficult for users to update their servers. They will have to re-enter the account information on each machine every time there is an update. I expected this to happen with a "major" update, but not a "small" one.

  • Is there a way to configure the installer so that it doesn't change the existing account / password configuration for the service during a "minor" or "minor" upgrade?

  • Will this also happen during the "repair" (I have not tried this)?

This is what my component looks like in the file .wxs

:

<Component Id="cmpService" Guid="{MYGUIDHERE}">
  <File Id="filService" KeyPath="yes" Name="ServiceApp.exe" />
  <ServiceInstall Id="ServiceInstall" Name="ServiceApp" DisplayName="My Service"
                  Type="ownProcess" Start="auto" ErrorControl="normal"
                  Account="LocalSystem">
    <util:PermissionEx ... attributes here... />
  </ServiceInstall>
  <ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall"
                  Name="ServiceApp" Wait="yes" />
</Component>

      

I expected Remove="uninstall"

to keep the service in place if there were no changes to it. Obviously not. (I'm not too worried if this happens on "major" updates).

I also noticed that the element ServiceConfig

has attributes ( OnReinstall

) that seem to match a vector, but from the candle error messages it is pretty clear that it is OnReinstall

intended to only affect configuration items the element ( PreShutdownDelay

etc.) and not a service setting in the whole.

I looked them over:

Curiously, this answer assumes that this is a problem for "major" updates only. This was not my experience. Was my experience random?

During the installation process, it would be correct to specify the account and password, but storing the password in the registry or elsewhere is not really an option in this case, and having to re-enter the credentials with every update is as destructive as manually reconfiguring the service.

+6


source to share


2 answers


I had a phone call with FireGiant about this problem and we came up with a solution.

Background:

  • When installing our application, MSI first installs Windows Service using LocalService

    , however, our real desktop software changes it to NetworkService

    or even a custom user account, which may be necessary in certain network environments.
  • Our element <Component> <ServiceInstall>

    had Account="NT AUTHORITY\LocalService"

    and looked like this:

    <Component Id="Comp_File_OurServiceExe" Guid="*">
    
        <File Source="$(var.TargetDir)OurService.exe" id="File_OurServiceExe" KeyPath="yes" />
    
        <ServiceInstall
            Id           = "ServiceInstall_OurServiceExe"
            Vital        = "yes"
    
            Name         = "RussianSpyingService"
            DisplayName  = "Russian Spying Service"
            Description  = "Crawls your network for incriminating files to send to the FSB"
            Account      = "NT AUTHORITY\LocalService"
            Type         = "ownProcess"
            Arguments    = "-mode service"
            Interactive  = "no"
            Start        = "auto"
            ErrorControl = "normal"
        >
    
            <ServiceConfig DelayedAutoStart="yes" OnInstall="yes" OnUninstall="no"  OnReinstall="yes" />
            <util:ServiceConfig FirstFailureActionType="restart" SecondFailureActionType="restart" ThirdFailureActionType="none" ResetPeriodInDays="1" />
        </ServiceInstall>
    </Component>
    
          

  • Performing these repeated steps will unintentionally reset the service registration / configuration:

    1. Complete the installation using MSI version 1.0.0
    2. Open Services.msc

      and change RussianSpyingService

      to use NT AUTHORITY\NetworkService

      (instead of NT AUTHORITY\LocalService

      )
    3. Create a new MSI using the same files *.wxs

      but with higher file versions and give it a higher version like 1.0.1 (remember MSI only uses the first 3 components of the version number and ignores the 4th version)
    4. After completing this installation, make sure it has RussianSpyingService

      been reset for use NT AUTHORITY\LocalService

      .

Also, I asked FireGiant (their consultants previously worked at Microsoft and helped other company teams use MSI) who other software, such as SQL Server, can use MSI to install Windows services that work fine despite configuration changes between updates- They told me that products like SQL Server often use custom actions to configure a Windows service, and despite the general advice to avoid custom actions, it is acceptable because the SQL Server team at Microsoft is large enough to allocate resources for development and testing to make them work.

Decision

  • In short: "Use MSI properties!"
  • Specifically, define the MSI property that represents the attribute value Account

    , and load that value from the registry at MSI startup time, and if no value is present, use the default NT AUTHORITY\LocalService

    .
  • Ideally, the property value should be stored in its own application registry key, and the application should ensure that this value matches the current service configuration.
    • This can be done by creating a new registry key in HKLM

      which allows LocalService

      or NetworkService

      (or any other service account) to write to it, so when the service starts, it writes the name of its user account there. - But it is difficult.
    • Don't use HKCU

      values ​​for storing because that won't work: HKCU

      Allows completely different registry hives (which may not even be loaded or accessible) for different users.
  • The other option is not technically supported by Microsoft because it uses its own services

    Windows registry key value ObjectName

    (account name), which is usually in the same format that the attribute uses AccountName=""

    . This is also the most pragmatic, and this is what is described below:


Here's what worked for us:

  1. In your element <Wix> ... <Product>...

    add this declaration <Property>

    and element <RegistrySearch />

    :

    <?xml version="1.0" encoding="UTF-8"?>
    <Wix
        xmlns       = "http://schemas.microsoft.com/wix/2006/wi"
        xmlns:netfx = "http://schemas.microsoft.com/wix/NetFxExtension"
        xmlns:util  = "http://schemas.microsoft.com/wix/UtilExtension"
    >
    
        <Product
            Id="*"
            UpgradeCode="{your_const_GUID}"
            otherAttributes="goHere"
        >
    
            <!-- [...] -->
    
            <Property Id="SERVICE_ACCOUNT_NAME" Value="NT AUTHORITY\LocalService">
                <!-- Properties used in <RegistrySearch /> must be public (ALL_UPPERCASE), not private (AT_LEAST_1_lowercase_CHARACTER) -->
                <RegistrySearch Id="DetermineExistingServiceAccountName" Type="raw" Root="HKLM" Key="SYSTEM\CurrentControlSet\Services\RussianSpyingService" Name="ObjectName" />
            </Property>
    
            <!-- [...] -->
    
        </Product>
    </Wix>
    
          

  2. Update the item <ServiceInstall

    to use the new MSI property SERVICE_ACCOUNT_NAME

    for Account=""

    instead of the previous hardcoded one NT AUTHORITY\LocalService

    :

    <ServiceInstall
        Id           = "ServiceInstall_OurServiceExe"
        Vital        = "yes"
    
        Name         = "RussianSpyingService"
        DisplayName  = "Russian Spying Service"
        Description  = "Crawls your network for incriminating files to send to the FSB"
        Account      = "[SERVICE_ACCOUNT_NAME]"
        Type         = "ownProcess"
        Arguments    = "-mode service"
        Interactive  = "no"
        Start        = "auto"
        ErrorControl = "normal"
    >
    
        <ServiceConfig DelayedAutoStart="yes" OnInstall="yes" OnUninstall="no"  OnReinstall="yes" />
        <util:ServiceConfig FirstFailureActionType="restart" SecondFailureActionType="restart" ThirdFailureActionType="none" ResetPeriodInDays="1" />
    
    </ServiceInstall>
    
          

  3. Build and run Setup, run the update script, and you will see that the account username of any configured service will persist between update installations.

You can generalize this approach to other properties as well.

Denial of responsibility:

  • Microsoft does not officially endorse user-space programs that directly interact with a registry key HKLM\SYSTEM\CurrentControlSet\Services\

    . All operations in Windows Services are designed to execute the documented and supported Win32 Service Control Manager API: https://docs.microsoft.com/en-us/windows/desktop/services/service-control-manager
    • This means that Microsoft can, at its discretion, change the configuration of the Windows service so that it no longer uses the key HKLM\SYSTEM\CurrentControlSet\Services\

      .
    • (This can break a lot of third-party software; if Microsoft did, they would probably add some sort of virtualization or remapping system to it, as they do with SysWow6432Node

      ).
  • I've only tested with LocalService

    and NetworkService

    . I haven't seen what happens if you change the service configuration to use a custom user account after installation before running the update. I expect it to preserve the configuration in this case as well, as it will perform string comparison for the value ObjectName

    in SCM and it has no access to passwords.
0


source


In the end I got it

  <DeleteServices><![CDATA[REMOVE ~= "ALL" AND (NOT UPGRADINGPRODUCTCODE)]]> </DeleteServices>
  <InstallServices><![CDATA[NOT Installed]]> </InstallServices>

      



I arrived at this answer through a series of trial and error and a combination of several other topics with similar answers.

One of the possible reasons why it doesn't work is that WIX also removes the service upon reinstallation. We only want to install the service once, during the initial installation. We also want to make sure that the service is removed after deletion. This is the only combination of conditions that worked for me, allowing the service to save its settings and user account.

0


source







All Articles