WIX, how to not replace config file - wix

We have a MSI built with wix that installs our ruby based product. When we released the first version there was a bug. Upgrades would overwrite changes to a ruby config file (gemrc), effectively breaking the product in some cases.
I've been trying to get MSI to not remove or replace the config file on upgrades, without success.
What i have now is:
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallExecuteAgain" />
</InstallExecuteSequence>
<Directory Id="embeddedDir" Name="embedded">
<Directory Id="embeddedEtcDir" Name="etc">
<Component Id="gemrcComponent" Guid="uuid..." NeverOverwrite='yes' Permanent='yes'>
<File Id='gemrc' Name='gemrc'
Source='$(var.ProjectSourceDir)\embedded\etc\gemrc.default' Vital='yes' KeyPath='yes' />
</Component>
</Directory>
</Directory>
However, going from the current version (1.0) to the new version (1.1) will leave the installation with no gemrc at all. Going forward it works, so going from 1.1 to 1.2 it leaves the existing (modified) file.
I'm assuming, that the reason it doesn't work is because it uses the old 1.0 MSI to remove the existing installation, and that version has the gemrc file marked as part of the product that needs to be removed.
This means the only way i could get around this is using custom actions (copy the file to a temp path before installation, and then move it back afterwards.
Or something similar). Is there a better/easier way?

Some of the reasons you may be seeing this are as follows, but there's not enough information to say which might apply in your cases:
If you originally had a major upgrade scheduled "early" (InstallInitialize or sooner) then the upgrade would completely uninstall the older product and then install the new one. This would look like an overwrite of the config file, but strictly speaking it's not. You do not say if you ever identified the actual root cause of this issue or if the major upgrade was scheduled differently.
In a major upgrade scheduled after InstallExecuteAgain, the upgrade installs on top of the old product and follows file replacement rules. So if the config file had really been updated after install it would not be replaced.
If the component id of the config file changed between any of the setups then you'd see it removed (even in an InstallExecuteAgain upgrade). File sharing is ref counted by component id, so if the upgrade has a component id for the config file that is not the same as the previous installs then you'd see strange behavior because the ref count of the previous file means it would be removed, but you're attempting to install another of the same name with different id.
You should do your upgrades with verbose MSI logging enabled to see what happens to the config file. If it's not clear from that, then post it somewhere accessible for others to look at.

Related

WiX 3.8 keeping config file state during Major Upgrade

I'm using WiX 3.8 (the latest stable release, I think), and I can't seem to get a config file to not get uninstalled-and-reinstalled during a major upgrade.
There are lots of questions about this on SO -- a lot of answers point to this site as a good answer. However, the suggestion given doesn't work (for me).
What the site says is to place each config file in its own component and mark the file as the key path of the component. Something like this:
<Component Id="config.xml"
Guid="*"
Directory="folder_where_config_file_lives">
<File Id="config_file"
Source="$(var.Project.ProjectDir)bin\Release\configFile.xml"
KeyPath="yes"/>
</Component>
Great. Next it says to schedule RemoveExistingProduct after the InstallFiles action, like so:
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFiles"/>
</InstallExecuteSequence>
Problem is, when I compile, I get this error:
The InstallExecuteSequence table contains an action
'RemoveExistingProducts' that is declared in two different locations.
Please remove one of the actions or set the Overridable='yes'
attribute on one of their elements.
This person also had that problem, but he seems to have solved it. What fixed it for him was adding a scheduling attribute to the , which effectively got rid of the "two different locations" declaration problem (I guess):
<MajorUpgrade Schedule="afterInstallInitialize"
DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
So when I substitute the schedule change attribute (which contains a attribute itself, I guess), not only does it not work -- the config file gets removed and replaced during the upgrade -- it causes even more weirdness. My project has a bootstrapper with a lot of MSIs, and although I get log files for the installation of all of the MSIs that are after the MSI that contains the config file, they aren't installed.
Let me repeat that: the logs say that the MSIs are installed, but they aren't. There's probably a rollback somewhere that I can't find in the log files, but reading the MSI log files it looks like the installation went swimminly.
Does anyone know a way for a config file to not be removed-and-reinstalled during a Major Upgrade in Wix 3.8? What I've mentioned above is the best info from the interwebs that I could find, but I've tried pretty much everything on SO to no avail.
The MajorUpgrade element has everything you need, including where the RemoveExistingProducts action is scheduled. Don't add a RemoveExistingProducts into a sequence as well.
RemoveExistingProducts shouldn't be after InstallFiles. It's not clear where that comes from, but the documentation doesn't say that's a choice:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa371197(v=vs.85).aspx
When RemoveExistingProducts is sequenced early (such as after InstallInitialize or InstallValidate) it means that you are effectively uninstalling the old product followed by an install of the new product upgrade, and that means uninstalling the config file and installing the one in the upgrade. The way to retain the config file is to schedule REP afterInstallExecute. This results in an upgrade that is basically a version-rules install of the new product over the older installed one. The version rules mean that if you want updated binaries you must update their file versions. The good news about data files (your config file) is that updated data files won't be replaced:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
The older product then gets uninstalled, retaining the resulting set of files.
So sequencing of REP afterInstallExecute in the MajorUpgrade seems to be what you want. A caveat is that you need to follow component rules, which should happen automatically if you have auto-generated * guids in WiX.
IMO, Windows Installer was invented before XML caught on and the component rules don't handle it well. What I prefer to do is to not fight this behavior. Write your application so that one config file is owned by the installer and can always be safely overwritten and another config file that holds your user configuration data and that MSI doesn't know about. This second file should take override the first file.

Wix throws away components that are marked as NeverOverWrite="yes"

I've created an installer that upgrades our software, but for some reason the XML configuration files (those of our software) are removed when upgrading.
This appears to happen if all features of the software are upgraded.
Our software is an archive type thing. If I install just that feature and upgrade it everything is fine.
However, if I install all services accompanying the archive and upgrade those then all configuration files (and each is in a different folder!) are gone.
As an example:
<ComponentGroup Id="AutoArchiveTool" Directory="AutoArchiverFolder">
<Component Id="C_AutoArchivingTool_Gateway_exe_config" NeverOverwrite="yes" Guid="{A62D5200-FDE0-4DA1-A04A-7FBDACEA83B2}">
<File Id="F_AutoArchivingTool_Gateway_exe_config" Source="$(var.Gateway.TargetDir)Gateway.exe.config" KeyPath="yes"/>
</Component>
... more script
</ComponentGroup>
If you log the installer you can even see that it's recognized as "never overwrite":
Disallowing installation of component: {A62D5200-FDE0-4DA1-A04A-7FBDACEA83B2} since the keyfile exists and the component is marked to never overwrite existing installations
Yet it's gone after the upgrade. What am I missing?
I've see the "Permanent" property, but that's not what I want. I want the installer to leave the config files alone during an upgrade. Not leave them (config files) after removal.
NeverOverwrite does not mean "don't uninstall" so:
a) if the component ID changes between the original install and the new one the sharing won't work as you intend and the ref count will decrement and the file will be removed, this being when the upgrade is scheduled "late", such as afterInstallExecute.
b) If the upgrade is scheduled early (such as afterInstallInitialize) all the old product is uninstalled first, then the new product is installed. You haven't said where your upgrade is sequenced, but sometimes Windows Installer screws up in an "early" upgrade: it decides that the file won't be overwritten, but fails to re-evaluate this when the install turns out to be an upgrade. In this case your upgrade will complete and the file will be missing. If you have this issue then a repair of the product will restore the file from the new version of the product (because that is the current owner of the component). This won't help.
So make sure that the component ID didn't change and your upgrade is scheduled late, such as afterInstallExecute. In addition, do the upgrade with verbose logging to verify what's going on.
Having said all that, NeverOverwrite is often used to solve a problem that doesn't exist. The file overwrite rules (that are invoked by a "late" upgrade) say that modified files won't be overwritten:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
So if the first setup installs the file, then it gets updated by the app, then your upgrade runs it will not replace the modified file anyway, and there is no need to set NeverOverwrite.

WiX MajorUpgrade of Windows Service, preserving .config, and avoiding a reboot

I am struggling to get MajorUpgrade, ServiceControl, .config files to work nicely together. After my other question, I'm now kinda having the opposite problem again.
Before, the files weren't being overwritten because the AssemblyFileVersions were static so I fixed that. 1) Now, even with Schedule="afterInstallExecute" my KeyPath='yes' .config file is still being overwritten even though the existing file modified date is different than the file creation date and it is set as a KeyPath. I'm currently having to overwrite the .config file and restart the service after the install.
2) And even if I fix that, I still have a problem of avoiding a reboot. If I say Schedule="afterInstallInitialize" then I believe the .config file will certainly be removed along with the service too early. If I say Schedule="afterInstallExecute" then the service isn't stopped and after the install a reboot is necessary. (That's right, right?) Stopping the service manually prior to the install let's me avoid the reboot. Adding a net stop custom action could work to replace the ServiceControl I guess, but getting all the conditions right seems complex.
3) As a bonus, I'd like to NOT remove the service at all during an upgrade. Can I just stop the service, replace the binary, and start the service again? That will avoid re-entering the service account credentials for an upgrade. But of course it still needs to install on a first install and uninstall on a feature removal.
Here's the meat of it (which is also bundled later, in case that somehow matters):
<MajorUpgrade DowngradeErrorMessage="A newer version is already installed."
Schedule="afterInstallExecute" />
<ComponentGroup Id="ServiceCG">
<Component Id="Service" Guid='*' Win64='yes' Directory='INSTALLDIR'>
<File Id='ServiceEXE' Source='$(var.root)Service.exe' />
<ServiceInstall Id="ServiceInstall"
Name="MyService"
DisplayName="My Server"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Description="My Server Service"
Interactive="no"
Account="[...]"
Password="[...]" />
<ServiceControl Id="StopService" Name="MyService" Start="install"
Stop="uninstall" Wait="yes" Remove="both" />
<util:User Id="UpdateServiceAccountLogonAsService" UpdateIfExists="yes"
CreateUser="no" Name="[SERVICEACCOUNTFULL]"
LogonAsService="yes"/>
</Component>
<Component Id="ServiceConfig" Guid='*' Win64='yes' Directory='INSTALLDIR'>
<File Id='FileServiceConfig' KeyPath='yes'
Source='$(var.root)Service.exe.config' />
</Component>
</ComponentGroup>
Related but unanswered:
Prevent service removal/install during WiX major upgrade - service not stopping
WiX version 3.8.1128.0
EDIT: it seems this explanation of the same issue, or on the same topic at least, might be easier to understand: Msiexec: automatic rollback to previous version on installation failure
You are banging your head against several core MSI usage problems here.
File versioning: during installation the default file overwrite mode (defined by the REINSTALLMODE property) won't replace files that are equal version by default. This can be changed by setting REINSTALLMODE = "emus". This will replace files with equal version for versioned files. Unversioned files will be preserved if modify and create dates are different.
Upgrade behavior: like Chris says, files that appear to be reverted to default are actually uninstalled and reinstalled due to major upgrade configuration. File preservation is only possible in major upgrades if RemoveExistingProducts is placed late in the InstallExecuteSequence. Then shared files between releases are never uninstalled and the file versioning rules described in point 1 apply for overwriting.
Service configuration preservation: avoiding re-entry of service credential information is a common problem for major upgrades that uninstall early in the InstallExecuteSequence. In other words the product is uninstalled and then reinstalled wiping out changed files. I don't recommend it, but some people argue for this solution: How to only stop and not uninstall windows services when major upgrade in wix? (Rob Mensching is the WIX and Orca author - I think he is suggesting this solution as an option, and not necessarily a recommendation. Please ask in the linked post to be sure). With correct component referencing and uninstall placed late in the InstallExecuteSequence this problem is normally avoided altogether, and this is then a preferred approach (normal component referencing prevent the component from uninstalling altogether leaving service settings and changed config files intact - if, and only if, component referencing is correct - see description of this concept below). However, my preferred approach is still to use a separate MSI for the service install and configuration if you are using a user account to run the service - then it is a self contained deployment unit and can be included in a bootstrapper and updated on its own, and best of all: it is undisturbed by any other application changes or hotfixes. Finally I would like to point out that a service running with a user account isn't a recommended approach to begin with - for security and deployment reasons alike.
Component referencing: refers to the GUIDs assigned to MSI components and how they must match one, and only one (absolute) path at all times through all upgrades. See a better discussion of this with a couple of examples here: Change my component GUID in wix?
I didn't mention the option of setting the MSI components installing services to permanent to prevent their removal on uninstall for the simple reason that this isn't good practice at all. Then files and registration will remain on final uninstall, and you need a custom action to clean up. Very bad practice and bound to cause lots of extra work and problems with dangling component references.
The file create/mod rule only applies to install/reinstall a component. It doesn't prevent the component from being uninstalled. Your major upgrade is scheduled very early which means the previous version is completely uninstalled and then then the new version is installed. This is why your file is overwriting when you don't expect it to. Schedule RemoveExistingProducts later to avoid this problem.
Set your Stop attribute to both install and uninstall. Those two changes should solve your problems.

Forcing an upgrade of a file that is modified during its initial installation

I'm working on the upgrade feature for my WiX-based installer.
As part of the instalation, we are installing a web.config file and then using a custom action to update the connection strings inside the file.
But this causes a problem when we run our upgrade. We would like to have the RemoveExistingProducts scheduled for after InstallFinalize since this is most efficient in terms of not removing and reinstalling files that have not changed. But this leaves the original web.config file in place at the time when Windows Installer is trying to determine whether it should update it or not. Since it's last modified date is more recent than its creation date, Windows Installer decides not to update it (see versioning rules that Windows Installer uses). But we need it to be updated.
One obvious solution is to change the scheduling of RemoveExistingProducts to after InstallValidate - but this is inefficient, and also, I don't think it would give us the opportunity to migrate settings from existing files, should we need to do that.
Any other ideas?
Newer answers: 1) Companion files, 2) file version hack using Visual Studio, 3) moving the file to another installation path, 4) variations of REINSTALLMODE, 5) "version lying", etc... All kind of options, most of which are not ideal:
File of a new component isn't installed because there was an old component with the same file
How to Explicitly Remove dll During Majorupgrade Using Wix Toolset
Below is an older answer. I don't think option 2 works properly anymore:
There are many ways - none are ideal.
1: You can use a companion file to force update of the file in question. Provided the companion file specified always gets updated, this may be the way to go. Essentially this means that you link the non-versioned file to the version update logic of its companion file (files are updated together). I have never used this in WIX, but I think it's as easy as adding the CompanionFile attribute to a File element and point to the ID of the file you want to "version follow". Inside the MSI file it will look something like this:
2: You can use a custom action to delete the file before file costing (or better yet, rename it to a backup format). The problem is that if the setup fails the file will be missing. If you rename the file instead of deleting you can put it back in case the setup fails via a rollback custom action. Sometimes I use the RemoveFile table to remove files on install, but depending on the sequencing specified in InstallExecuteSequence this may not work (deletion must happen before msi does file costing).
3: Then there is the sledgehammer approach: set REINSTALLMODE = amus to force overwrite all files regardless of version. I shouldn't even mention this since it is horribly dangerous (you can end up overwriting system files, or on newer Windows versions trigger a nasty runtime error as files are protected). Use it only for dev testing, and don't think it is a quick fix. It causes more problems than it solves.
As a variation, an acceptable approach may be to set the REINSTALLMODE to emus (replace older and same version files). This can help if you don't want to increment the version numbers but keep rebuilding your binaries - as is the case in a lot of .NET. My guess is this will cause a whole new range of problems though - most significantly binary different but version identical files in the wild if you use it for public releases - a deployment smell if ever there was one. As a QA/DEV only approach it could work though. But seriously, why bother? Just auto-increment the build version of the binaries and the problem is solved reliably.
Links:
How to Explicitly Remove dll During Majorupgrade Using Wix Toolset
Only iffy ones. You could remove the specific file early with a custom action, but be sure to condition this right! Or you could specify a version for the file so upgrade rules will treat it like replacing a non-versioned file with a versioned one, but then patches can get antsy about having the wrong version of this file.
Don't use a custom action to update your config file is the other obvious idea. Instead get WIX to do the update via the XML extensions. E.g.
<Component Id="web.config" Guid="f12ff575-ad5f-47bc-a5c9-40b1e3a7f9f5" >
<File Source="$(var.SrcPath)\web.config.config" KeyPath="yes" />
<util:XmlConfig Id="AppSqlInstanceName"
File="[#web.config]"
Action="create"
ElementPath="//configuration/connectionStrings/add[\[]#name='YourStringKey'[\]]"
Name="connectionString"
Node="value"
Value="metadata=res://*/YourModel.csdl|res://*/YourModel.ssdl|res://*/YourModel.msl;provider=System.Data.SqlClient;provider connection string="data source=[SQLSERVERANDINSTANCE];initial catalog=DatabaseName;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework""
On="install"/>
</Component>
This is using a [SQLSERVERANDINSTANCE] variable which needs to be setup before hand.

Manage configuration files with WiX

I have an application with several files that contain configuration parameters and other data that changes as the user uses the application. These files can change with newer versions of my software, but the user can also modify them (or they may be changed by the application itself). Basically, I'm looking for a solution to prevent the users' changes to these files from being overwritten but also a way to install the potentially updated files when the user upgrades my software.
With RPM on *NIX you could use the %config function to define a file as a configuration file and RPM would then rename the existing file (if it existed) and install the new one on an upgrade (maybe not ideal, but I could live with something like this for WiX).
I'd like to install my config files to a subdirectory or even a different name (e.g. default.cfg) and then use the <CopyFile> element in WiX to copy the files to their correct locations. This way, the default files would get removed on install and overwritten on an upgrade, but the actual user files would stay the same. Unfortunately with <CopyFile>, Windows Installer still wants to manage (and remove) the destination file.
I've also considered using the QtExec action in WixUtilExtension to basically do "copy default.cfg reallocation.cfg" but this wouldn't quite work and it is a bit of a hack.
What is the correct way to handle this?
My recommendation is usually to have the user editable content in a separate file and manage that via the application instead of the install. That also means the separate file is "user content" and should be left out of the install.
I've found trying to do migration of user data declaratively to be deceptively difficult. Trying to do it at setup time when you need to think through install, uninstall, repair, patching and rollback for all of those cases only makes it worse.
For example, what does the RPM behavior do on "repair". Copy the user data out of the way and replace it with a good file? That's probably correct 60% - 80% of the time. And uninstall, should the file be removed? That's tricky if the user is going to just upgrade to the next version.
Again, better to let them decide what to do with their tweaks to the configuration. IMHO.
I think there is no "clean" way to do this, because a msi project must be able to uninstall itself completely by design. I think the best way to solve this, is by using a custom action which executes a batch file and put your configfile update logic in that batch file. The custom action looks like this (only relevant parts):
<Directory Id="MYDIR" Name="MyDir">
<Component Id="update.cmd" Guid="YOUR-GUID">
<File Id="update.cmd" Name="update.cmd" KeyPath="yes"
Source="source\update.cmd" />
</Component>
</Directory>
<CustomAction Id='RunUpdate' Directory='MYDIR'
ExeCommand='[SystemFolder]cmd.exe /c update.cmd' Return='ignore'/>
<InstallExecuteSequence>
<Custom Action='RunUpdate' After='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>