Thursday, November 20, 2014

The perfect storm: Windows 2012, UAC, and silent [un]installs with MSI

Over the past few months I've done a lot of work with the Microsoft Installer (MSI) platform and in that time spent many sleepless nights trying to figure out how to get valuable debugging information from rather cryptic error messages.  As such I've been labeled as the go-to resource for solving such obscure issues.

Recently one of my colleagues approached me asking for assistance with an issue she was having when trying to uninstall and subsequently re-install an MSI package via Release Management.  When they would run msiexec from the command line as the same account as the deployment agent, the command was successful, however when the agent attempted to do so, it came back with this vague error:

Error 1601: Windows Installer is not accessible

Not the most useful error message, so we started doing some debugging looking for the usual suspects including group membership, local security policy, and even if there was a pending reboot. Everything was in order.  Next step, grab some more debugging/logging information from the uninstall process using the built in logging mechanisms.

msiexec /x {79C635ED-150B-4DE7-92D2-522E1EDC1BC5} /quiet /lvx* c:\logfiles\uninstall-ffi.log

NOTE: I specify a directory called logfiles on the root drive because the msiexec.exe program is being executed from the working directory c:\windows\syswow64 and would have attempted to write the log file to that directory which would fail because of rights assignment (and it's a bad practice). You may need to assign additional permissions to this directory so all users (or at least your deployment agent account) can write to it.

The log file produced more information but still somewhat cryptic.

=== Verbose logging started: 11/20/2014  12:25:40  Build type: SHIP UNICODE 5.00.9600.00  Calling process: C:\Windows\SysWOW64\msiexec.exe ===
MSI (c) (2C:F8) [12:25:40:388]: Resetting cached policy values
MSI (c) (2C:F8) [12:25:40:388]: Machine policy value 'Debug' is 0
MSI (c) (2C:F8) [12:25:40:388]: ******* RunEngine:
           ******* Product: {79C635ED-150B-4DE7-92D2-522E1EDC1BC5}
           ******* Action: 
           ******* CommandLine: **********
MSI (c) (2C:F8) [12:25:40:388]: Client-side and UI is none or basic: Running entire install on the server.
MSI (c) (2C:F8) [12:25:40:388]: Grabbed execution mutex.
MSI (c) (2C:F8) [12:25:40:403]: Failed to connect to server. Error: 0x80070005

MSI (c) (2C:F8) [12:25:40:403]: Note: 1: 2774 2: 0x80070005 
1: 2774 2: 0x80070005 
MSI (c) (2C:F8) [12:25:40:403]: Failed to connect to server.
MSI (c) (2C:F8) [12:25:40:403]: MainEngineThread is returning 1601
=== Verbose logging stopped: 11/20/2014  12:25:40 ===

Notice the line that says Failed to connect to server. Error: 0x80070005. Some research shows us this is often an access denied error across many Microsoft products but as to what access is unclear. If you look deeper into the recesses of how the Windows Installer works you will see there is a service that is invoked and runs under the highest privileged account it can [SYSTEM]. In order to connect to the service the user must have elevated access rights, which our user did, so what gives?

However, with all the modern security standards in place to make sure you are actually doing something you intend to do, Windows 2012 will enforce UAC no matter if you are an administrator or not.  When running a silent install/uninstall you do not see the prompt and in turn, the engine fails with the cryptic 1601 - 0x80070005 error.

While likely not the most elegant of fixes, disabling UAC [via registry] on these servers seems to do the trick. TechNet provides a download to make this even easier.

Once the key was modified, we attempted to publish a release through RM again and the error was gone and deployment moved forward!