WiX: Restart manager always schedules restart when IIS has files in use - asp.net-core

We have a msi setup built with WiX which installs the files for our application. Among these files is a website built with ASP.Net Core. We don't do any IIS configuration in the msi. Recently I found that when a website is manually created in IIS which uses the website files and the website is active when an upgrade is executed then the restart manager will try to restart/shutdown IIS but it fails with this error message:
Application 'C:\Windows\System32\inetsrv\w3wp.exe' (pid 45896) cannot be restarted - Application SID does not match Conductor SID..
This error message is somewhat confusing as we are running the msi as Administrator so we should have enough permissions to restart the application?
We want to prevent the need to reboot, so I'm trying to add a custom action to stop the website before the restart manager determines if the reboot is needed. I did some searching and found that the custom action should be scheduled before InstallValidate (found here: https://stackoverflow.com/a/3221478).
But even if I scheduled the custom action before InstallValidate I still get the popup that a reboot is required. Scheduling the custom action even earlier (tried to do it before CostInitialize even) makes no difference. From the logs I've taken it seems that the restart manager runs before any custom actions are executed.
So in my logs I find these log messages pretty early on:
Line 111: MSI (c) (78:DC) [10:06:29:463]: PROPERTY CHANGE: Adding MsiSystemRebootPending property. Its value is '1'.
Line 939: MSI (s) (28:5C) [10:06:45:645]: PROPERTY CHANGE: Adding MsiSystemRebootPending property. Its value is '1'.
and a bit later my custom action is executed:
Line 1453: MSI (s) (28:5C) [10:06:50:977]: Doing action: StopInstallationFolderSites
In the log I see the website holding the files in use is shutdown correctly, but still further in the log I see this:
Line 2800: MSI (c) (78:94) [10:06:54:178]: RESTART MANAGER: Session opened.
Line 2801: MSI (s) (28:5C) [10:06:54:257]: Note: 1: 1610
Line 2802: MSI (s) (28:5C) [10:06:54:257]: Transforming table Error.
Line 2804: Tijdens de installatie moeten bestanden of services worden bijgewerkt die niet kunnen worden bijgewerkt als het systeem actief is. Als u wilt doorgaan, dient u de computer opnieuw op te starten om de installatie te voltooien.
Last line is the localized error message which is shown in the installer UI.
So even though my custom action is shutting down the application which has the files in use, I still get the popup to reboot the machine.
Is there a way I can shutdown the website to release the files before the restart manager determines if a restart is needed?

Set MSIRESTARTMANAGERCONTROL= Disable property in Property table.

Related

How can I automate testing an MSI is installable with UAC?

I want to verify that an MSI is installable by normal users. Is there any way to emulate the permissions flow of an install using UAC without having the graphical prompts?
Background:
I work with an application that is distributed on Windows via an MSI. We do automated testing on it by running msiexec and then testing the resulting application. This works, but we recently ran into an edge case it doesn't cover.
We recently added a CustomAction in our wix setup that is run at the end of the install and which requires elevated permissions to run. The problem is CustomActions, by default, run with Impersonate="yes" which means they run with the running user's permissions and not the elevated permissions granted by UAC.
In our testing, we're running from an administrator context so the install succeeds. However typical users will require a UAC prompt to grant the MSI temporary admin rights to install. Since the custom action is not running with that admin context, the install fails.
So what I want to do is set up an automated test environment that emulates the user experience more closely. UAC is generally designed to not be scripted for security reasons, which complicates the issue. So what I'm wondering is if there's any way to exercise the permissions flow in a way that is automateable, given I have full control over the environment.
Elevated Rights: A per-machine setup can run with temporary admin rights - as you put it - without being a per-user install. This is done via Group Policy / Active Directory and distribution systems that handle this kind of scenario properly.
Emulate Elevated Rights: To emulate the elevated rights on a regular PC (without a distribution system), you can use a group policy / registry hack called AlwaysInstallElevated. Once applied, it essentially means that all users can run with elevated rights just launching an MSI. Sizable security hole, so don't use it for fun. You can, however, kick off any install and install with elevated rights - if that is what you need.
Deferred Mode Custom Action: Only deferred mode custom actions have elevated rights. Immediate mode custom actions can not elevate and if they try to do something to the system that requires admin rights you will get a runtime error. If you run such a failing MSI with "real" admin rights, then you will see the MSI appear to successfully install (though it will fail with elevated rights for a standard user).
Implementing a deferred mode custom action generally (for non-trivial purposes) requires some involved steps. Perhaps you are familiar with them? You can't access properties directly, you need to "send them to deferred mode" via a mechanism called CustomActionData (SO). And adding a marginal WiX sample for deferred mode.
UPDATE: New link to samples for deferred mode custom actions: How to hide the value of customactiondata in logs of MSI? - you will find links to this github sample (and other samples): https://github.com/glytzhkof/WiXDeferredModeSample
Lobbing you some links for now (too many, but just thought I'd hand you the bunch - I think the first link will do, 5 for classic technical article, 6 for a modern approach using JSON):
1: Tip: MSI Properties and Deferred Execution
2: How to Access Windows Installer Property in Deferred Execution
3: It Wants Me to do What? Some Notes on CustomActionData in InstallShield
4: Just Be Yourself: Understanding Windows Installer (MSI) Custom Action Contexts
5: Installation Phases and In-Script Execution Options for Custom Actions in Windows Installer
6: Beam Me Up: Using JSON to serialize CustomActionData
Privileged: If you want to ensure that the MSI package installs per-machine properly for a standard user, I would add a launch condition to the MSI to abort if privileged rights are not available (no chance of success). Note that this does not check for admin rights, but for privileged rights (elevated rights).
<Condition Message="Elevated rights required to install package.">Privileged</Condition>
msiexec.exe: And just for the record, adding a little batch to check for msiexec.exe error codes (not tested recently, but here goes - note that a number or error codes indicate success - for example "reboot initiated" and stuff like that):
1603 = Fatal error. See link above for more.
start /wait msiexec.exe /i Setup.msi /l*v Setup.log
if "%errorlevel%" == "0" goto OK
if "%errorlevel%" == "1603" goto err
if not "%errorlevel%" == "0" goto err
:OK
GOTO END
:err
rem print message and return errorlevel
echo "Error: Msiexec failed with errorlevel = %errorlevel%"
exit /b %errorlevel%
:END
Note that a number of exit codes indicate success (beyond just 0):
1641: ERROR_SUCCESS_REBOOT_INITIATED
3010: ERROR_SUCCESS_REBOOT_REQUIRED
There are probably others I have forgotten. Sample only.

How can I stop an .exe on repair, update and delete in wix?

In my wix I use the following declaration:
<ComponentGroup Id="BinComponents" Directory="BIN">
<Component Id="BinComponent" Guid="23D229D0-06EE-49f4-80B4-6D7136500721">
<File Id="MyProjectOutput" Name="MyProject.exe" Source="MyProject\bin\MyProject.exe"/>
<ServiceControl Id="RemoveService"
Stop="both"
Remove="both"
Name="MyProject.exe"
Wait="yes" /> <!-- Stop running MyProject instances -->
</Component>
</ComponentGroup>
My Repro:
At first, I run my installation as usual. After the installation, I start my web application. An .exe appears in the task manager as usual:
I want to end this .exe on a repair, update or uninstall. So I start my .msi again and choose repair:
Now my problem: After pressing 'Repair', I expect the following dialog because of the declared ServiceControl:
But it doesn´t. Instead, the following dialog appears:
When I log the setup, the log shows the following lines:
MSI (s) (A8:DC) [10:16:28:227]: Executing op: ActionStart(Name=StopServices,Description=Stopping services,Template=Service: [1])
Action 10:16:28: StopServices. Stopping services
MSI (s) (A8:DC) [10:16:28:228]: Executing op: ProgressTotal(Total=1,Type=1,ByteEquivalent=1300000)
MSI (s) (A8:DC) [10:16:28:228]: Executing op: ServiceControl(,Name=MyProject.exe,Action=2,Wait=1,)
MSI (s) (A8:DC) [10:16:28:228]: Executing op: ActionStart(Name=DeleteServices,Description=Deleting services,Template=Service: [1])
Action 10:16:28: DeleteServices. Deleting services
MSI (s) (A8:DC) [10:16:28:228]: Executing op: ProgressTotal(Total=1,Type=1,ByteEquivalent=1300000)
MSI (s) (A8:DC) [10:16:28:229]: Executing op: ServiceControl(,Name=MyProject.exe,Action=8,Wait=1,)
MSI (s) (A8:DC) [10:16:28:229]: Executing op: ActionStart(Name=InstallFiles,Description=Copying new files,Template=File:
[1], Directory: [9], Size: [6])
[...]
MSI (s) (7C:28) [09:06:21:950]: RESTART MANAGER: Did detect that a critical application holds file[s] in use, so a reboot will be necessary.
MSI (s) (7C:28) [09:06:21:950]: Note: 1: 1610
MSI (s) (7C:28) [09:06:21:950]: Note: 1: 2205 2: 3: Error
MSI (s) (7C:28) [09:06:21:950]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1610
Next to a Repair I also have tried an Update with the same results.
Perhaps any declaration missing?
Note: When I close the MyApp.exe in the task manager, the message does not appear, so the MyApp.exe is definitely responsible for the problem.
You should post the entire log somewhere. The root cause is that a repair shouldn't routinely require files to be replaced. So if you literally installed your product, ran the exe, and then a repair needs to replace files, then the issue is not that files-in-use dialog - it's that the installed product is broken, so required files or registry entries have been removed. The application event log should have MsiInstaller entries that describe a missing component. So look at that root cause first.
After fixing that it should be very rare for a repair to need to replace files, so it may not be worth worrying about. But you could look at integrating your app with Restart Manager or using the WiX util CloseApplication.
The warning dialog that you are seeing is from "InstallValidate" standard action.
I have run into a similar issue in the past . I fixed it by making use of a single service control element instead of multiple service control elements, for the same service id.
In my case, i had multiple service control elements for the same service id.
This is as per the link at
http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Upgrade-uninstall-restart-issue-td7586315.html
This did the trick for me. Numerous users have reported the same behavior, though its not officially documented.
Having a single service control element makes Restart Manager take note of the entry in the Service Control table and will prevent Restart Manager from listing the service in the RMFilesInUse dialog box or will prevent Restart Manager
from throwing up warning messages informing the user that a restart might be required.
Here is one more link
Can't start windows service with WiX
My experiments have shown me that there is definitley a link between the number of service control elements and Restart Manager
http://microsoft.public.windows.msi.narkive.com/OOuQQAsw/controlling-restart-manager-behaviour
The other option was to totally disable Restart Manager using the property RESTARTMANAGERCONTROL, In case, you disable the RestartManager, you might be prompted for reboots(you might want to test it once) and the legacy "Files in Use" mechanism kicks off. Disabling Restart Manager is a conscious decision by the concerned msi developer and at times becomes necessary.
I am not sure about how your Service Control table looks like. Just wanted to share my experience with you.
Hopefully it helps.
Regards,
Kiran Hegde

Error repairing setup created in WiX 3.8

I have created a Web Server setup in WiX (3.8), It gets installed correctly, but when i Repair it, I get an error 'Fatal error during Installation' and the process is rolled back. below is what i see in logs
MSI (s) (F8:C4) [12:39:26:183]: Executing op: CustomActionSchedule(Action=WriteIIS7ConfigChanges,ActionType=11265,Source=BinaryData,Target=**********,CustomActionData=**********)
MSI (s) (F8:F8) [12:39:26:188]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI7B14.tmp, Entrypoint: WriteIIS7ConfigChanges
WriteIIS7ConfigChanges: Error 0x80070002: Site not found for create application
WriteIIS7ConfigChanges: Error 0x80070002: Failed to configure IIS application.
WriteIIS7ConfigChanges: Error 0x80070002: WriteIIS7ConfigChanges Failed.
CustomAction WriteIIS7ConfigChanges returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 12:39:26: InstallFinalize. Return value 3.
Am i missing out something. Any help is appreciated, Thanks in Advance :)
If it's a repair and the site name is missing, chances are that it was entered in a UI dialog in the original install. In a repair there is no UI to re-enter the site name and properties aren't automatically saved. It may be trying to configure a site with no name. The best way to deal with this type of thing in general is to use the WiX remember property pattern on the site name so that it is preserved and is available for a repair. I can't tell if that would be a good thing in this particular case because I don't know if writing the config changes again on top of the existing config changes will work or be handled correctly by the code. A verbose log of the original install should tell you what property names are being used for site names etc.
If you think it's unlikely that the IIS config changes will break and that they won't need repair, you could consider adding "Not Installed" to the condition on the CA so that a repair won't call it.

Re-cached msi still running old custom action

I have made a change to a custom action in my msi file to not attempt to stop a windows service if the service is stopped or stop pending. However, after re-caching the msi using msiexec /fv mymsi.msi, when running the major upgrade to the next version (which also has the corrected service stopping custom action) the verbose logging is showing that the old custom action code is still being run even though the cached msi was updated. I even ran a binary comparison of the cached msi against the one that was used in the re-caching.
What am I doing wrong here.
If the old custom action is still running it means one of two things:
The new MSI logic is not correct and is still running the custom action.
The recache/reinstall did not work.
To re-cache/reinstall make sure your MSI is basically identical (same ProductCode/Upgradecode etc) to the old MSI except with the updated custom action. Then do:
msiexec /fv new.msi /l*v log.txt
That will overwrite the old MSI and do a repair using the new MSI (and give you a log file in case anything goes wrong).
In my case it was a cached copy of my MSI in %windir%\Installer and corresponding keys in the system registry (found them by GUID in HKLM and HKCR).

RestartManager fails to restart application during update

I'm using c#, .net 4, WIX 3.5, Windows Vista.
I have made my application compatible with RestartManager by p/invoking the RegisterApplicationRestart method and by handling the WM_QUERYENDSESSION and WM_ENDSESSION window messages (I return new IntPtr(1);).
If I try to update my application manually, then everything works as it should:
Launch application;
Launch msi file containing new app version;
During the installation/update, I'm prompted to close the running application;
Upon continuing the running app is closed, install completes, and the app is restarted;
If I try to update my application from the application itself, then I run into problems:
1) Launch application;
2) Download the new msi file;
3) Launch msi file with:
using (System.Diagnostics.Process p = new System.Diagnostics.Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "msiexec";
p.StartInfo.Arguments = "/i \"" + downloadPath + "\" /passive";
p.StartInfo.UserName = "Administrator";
p.StartInfo.Password = securePassword;
p.Start();
}
4) Because I'm using passive mode, the application is closed automatically;
5) After the installation, my application is not restarted and under Event Viewer I have an
Event 10007 - Application or service 'MyApp' could not be restarted.
I have tried:
Not to use passive mode for msiexec;
Launch msiexec via cmd.exe (cmd.exe /C "msiexec /i ....") - in the hopes that launching msiexec from another process would solve the problem;
Wait for 60+ seconds before launching the msi update (shouldn't be relevant in my scenario, but MSDN documentation has something about it...)
But none of the above has worked (always the same result).
Having to launch the setup with elevated permissions might have something to do with the issue, because during the manual update I get a warning in the Event Viewer - Application MyApp (pid 3220) cannot be restarted - Application SID does not match Conductor SID.
Despite this, restarting the app still works. Googleing the warning yields no good/specific results, only that this warning is probably caused by running the msi in an elevated prompt.
How do I fix (or workaround) this issue, so that I can update my application from the application itself and restart my application afterwards?
Edit - extra testing:
There doesn't seem to be a need to respond to WM_QUERYENDSESSION and WM_ENDSESSION messages, because application restart during a manual upgrade works without them, so we can rule them out;
If I don't provide administrator credentials to the application initiated upgrade and instead I type them in during the upgrade, then app restarting works;
If I run an elevated command prompt and initiate an application upgrade from there (manually), then app restarting still works;
In order for application upgrade to work at all under Standard user accounts (so far I tested under an Administrator account with UAC), then I also have to set p.StartInfo.LoadUserProfile = true;. Otherwise nothing happens. (application restart still doesn't work though);
I tried all other process StartInfo parameters that I could set - WorkingDirectory, Redirect, Verb
(= "runas") - no change in results;
I installed Vista SP2 onto the virtual machine that I have been testing on (so far ran SP1), but no change;
I performed an "automatic" application upgrade with verbose logging. In the end there was an error message - RESTART MANAGER: Failed while restarting applications. Error: 352. That error code is very generic (http://msdn.microsoft.com/cs-cz/library/aa373665), inorder to get more detailed info I would have to write my own installer that would call RmGetList after the error, then I might get more details (this though is something I'm not willing to do);
Edit 2 - msi log file:
http://mommi.planet.ee/muu/log.txt
Assuming that the manual process indeed works without any problem it seems that your need for Administrator privileges in combination with the "updating itself" leads to these problems. I see the following options:
create a batch file to execute the update
When you want to update call this batch file (with elevated privileges), make the app close itself... the batch file should wait some seconds, then check whether the app is still running (and close it in case) and then run the commandline you need to run msiexec - don't restart the app from within msiexec but after a successfull run of msiexec from the batch file.
create a batch file which is always used to start the app
When the time comes to update you just end the app. Either the batch file check for an available update and applies it, starting the app after successfull update OR the app set some environment variable which is then accordingly processed by the rest of the batch file.