Custom action with elevated privileges to run batch file - wix

I need to launch .cmd script during installation, and this script modify some files in the installation directory.
I have the following WiX code:
<Fragment>
<Property Id="WINCMD">cmd.exe</Property>
<CustomAction Id="ResignManifest"
Property="WINCMD" ExeCommand="/C [ToolDir]manifest_resign.cmd"
Execute="deferred" Impersonate="no" Return="check"/>
<InstallExecuteSequence>
<Custom Action="ResignManifest" After="InstallFinalize"/>
</InstallExecuteSequence>
</Fragment>
The problem is that during installation the script fails to do modification, because of insufficient access rigths. My CA specifies that MSI should not impersonate the user context (I launch installer with Admin rigths), but cmd.exe runs without Admin rights.
PS. I know that this isn't good practice to additionally launch bat/cmd files during installation, but I have to do it to resign application and deployment manifests for some Silverlight application (during installation user modifies application config file, so the hash of such file inside manifest become invalid).

Somewhere in my answer history I've addressed this one before. The trick is to simplify the problem by refactoring the Silverlight code to read from a settings.xml file that isn't signed. This is what I had my developer do a few years ago for an internal app we wrote. The server side has a settings webservice that reads from a settings.xml file. The Silverlight app that calls the webservice to retrieve it's settings. This eliminates the need for the custom action.
See: How to do Text file changes in a ZIP file in InstallShield Basic MSI project
Creating and Using Silverlight web.config app settings (Web.config Configuration Applicatioin Settings) or app.config application settings configuration file for Silverlight
I agree with what you already know. It's not a good design to call a batch file and resign files laid down by MSI. You are right, you should already be executing in the system context. Are you sure CMD.exe isn't running elevated? I don't know how you could determine that as it's not getting logged by MSI. I'd suggest at a minimum using the QuietExecCA pattern to get the results of the cmd script logged in the MSI log.

Related

In WiX, update a windows service appSettings.json before starting services

I have a WiX project that installs a few Windows Services that are written in .NET 6. They each have an appSettings.json file in their directory in Program Files. During the install UI, I need to ask a question of the user and update the appSettings.json files with the answer to that question. Here's the tricky part:
I need to modify the files in Program Files (which requires elevated privs)
I need to modify them after they are copied but before the services are started
We don't want to require that the install be run as admin
We are open to the notion of locating the appSettings.json files in some other directory that the install will have write permission to, but I don't know where that would be. It shouldn't be user-specific, since these are services that run as System.
I have a CA that tries to modify the files, but it fails because I don't have write permission.
Here's what I've currently got for my CA (a C# DLL):
<CustomAction Id="UpdateConfigJson" Impersonate="yes" BinaryKey="MyCustomAction" DllEntry="UpdateConfigJson" Execute="deferred" Return="check" />
<CustomAction Id="SetCADataProp" Property="UpdateConfigJson" Value="Arg1=[VALUE1];Arg2=[VALUE2]" />
<InstallExecuteSequence>
<Custom Action="SetCADataProp" Before="UpdateConfigJson"/>
<Custom Action="UpdateConfigJson" Before="StartServices" />
</InstallExecuteSequence>
The SetCADataProp CA allows for passing installer properties into my deferred CA.
The whole thing works except that I get the permission failure when trying to write to the files.
This question is more of a conceptual thing: Is there a known procedure for doing this (as this doesn't sound an uncommon need), or is the solution to put the files somewhere else?
Thank you!
If you want to write to an elevated location (like ProgramFilesFolder) then do not have your custom action impersonate the logged-in user, i.e. remove the CustomAction/#Impersonate='yes' attribute.

WiX Toolset default logging file location

I have been using WiX for a few weeks now and still finding it hard to get good information about how things are supposed to work. One of those is logging. I have a requirement to have the installer write a log file to a specific location without the user having to add any /l command line parameters. I see there is a Log command, but it's parent is Bundle, the root element for creating bundled packages. I have a simple installer, so probably not needed for my case. This should be easy, right?
1. This is the simplest solution (If I were you I would do this) - create a batch file which executes the following command:
msiexec /i MyProduct.msi /L*V "%TEMP%\MyProduct.log"
Edit the path in the batch file to the custom path that you want.
2. You can make use of the MSILogging Property to set the logging outside of the command line. But this property is only available from Windows Installer 4.0.
Basically, you need to add a new property within your .wxs file:
<Property Id="MsiLogging" Value="voicewarmupx!"/>
v – Verbose output
o – Out-of-disk-space messages
i – Status messages
c – Initial UI parameters
e – All error messages
w – Non-fatal warnings
a – Startup of actions
r – Action-specific records
m – Out-of-memory or fatal exit information
u – User requests
p – Terminal properties
! – Flush each line to the log
x – Extra debugging information
This will ensure that the log file will be created in the %temp% folder, the name of the log file will be something like "MSI*.LOG." The full path to the log file can be read from this property and MsiLogFileLocation. But the MsiLogFileLocation property is read only and cannot be set.
Now this doesn't satisfy your requirement to create the log file in the custom location as we are not able to set the log file location. The reason we can't set the log file location inside WIX is because we need to tell MSIEXEC where to write the log file to before the windows installer engine actually starts executing the MSI.
To fix this, one thing that you can do is to add a custom action and copy the log file from the %temp% folder to the folder that you want. It would be something like this:
<CustomAction Id="CopyLogFile" Execute="immediate"
ExeCommand="cmd /c copy [MsiLogFileLocation] C:\customlocation\MyProduct.log"
Directory="TARGETDIR"
Impersonate="no"
Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="CopyLogFile" OnExit="success" />
</InstallExecuteSequence>
Look in:
C:\Users\user-id\AppData\Local\Temp\Charting_Companion_YYYYMMDDhhmmss_000_Setup.log
You could have a bundle as a wrapper around your single installer, and have the installer shows its own interface during install. This should be really easy to do, and probably the correct course of action. I don't really see a problem of having a bundle of 1 package if it makes it easy for you to do what you need.
Other than that, the installer needs to be invoked with logging enabled, another option is to have global logging enabled for windows installers via the registry - but this probably doesn't suit your needs, this needs to be set before your installer is run.

My CustomAction is not working

I have the following CustomAction:
<CustomAction Id="CopyToSystem32" ExeCommand="[INSTALLFOLDER]copy.bat"
Directory="INSTALLFOLDER" Execute="deferred" Return="asyncWait" />
<InstallExecuteSequence>
<Custom Action="CopyToSystem32" After="InstallFiles" >NOT Installed</Custom>
</InstallExecuteSequence>
The .bat itself tries to copy some files into System32 folder. But it's not copying them. The log says the following:
CustomAction CopyToSystem32 returned actual error code 1 (note this may not be 100% accurate if translation happened inside sandbox)
MSI (s) (A4:DC) [15:58:46:812]: Executing op: End(Checksum=0,ProgressTotalHDWord=0,ProgressTotalLDWord=313699) 1: CopyToSystem32 2: 1603
Why isn't my CustomAction working?
Try setting Impersonate to no on your custom action
<CustomAction Id="CopyToSystem32" ExeCommand="[INSTALLFOLDER]copy.bat"
Directory="INSTALLFOLDER" Impersonate="no" Execute="deferred"
Return="asyncWait" />
that will allow your deferred custom action to run with admin privileges.
What's in the bat file? You might be asking us to debug the contents of a bat file we know nothing about. Anyway, WiX has a copyfile element that will do this without bat files, using the underlying MSI tables.
The actual error (without seeing inside the bat file) is probably the result of an assumption that the bat file is running in the same environment as if you ran it from your desktop as interactive user, but it isn't. It's being run from an msiexec.exe process that makes no assumptions about where files are located and is running with the local system account (because it's deferred).
Very few files should be copied to system32 these days. Perhaps driver files, Active X OCX files, Control Panel Applets, maybe Console Snapins, Screen Savers - all pretty unusual stuff to deploy, and I believe many of them not really necessary to deploy there at all. Are these the types of files you intend to install to system32?
Remember that there is a whole lot of protection going on in system32 via Windows File Protection on XP (replaced files reset by Windows itself) and Windows Resource Protection on Vista upwards (locked down and protected files using Windows NTFS rights). What you do there might be undone by Windows itself, so stay out of the folder if you can. And if you try to deploy runtime files from Microsoft, they should generally be deployed via Microsoft hotfixes and / or a few "still relevant" merge module runtime packages.
Deployment is not like it used to be. It is very complicated now, and many things that used to work will never work at all. It is especially important to not deploy files that are likely to be replaced by windows hotfixes. Instead, find the version of the file you depend on and set the setup to be dependent on that version or higher. See a good discussion of this here: How can I include KB2670838 in an installer with InstallShield 2013?

Is it possible to execute a custom action in WiX that is a managed .EXE file?

I am trying to execute a custom action that was written C# (.NET 3.5) that has a dependency on a single DLL (Also C# 3.5). I have included both the .EXE and .DLL in the installation, and verified that they are being copied to the install location. However, every time I attempt to run the custom action, I get the following error:
Action start 7:21:16: InstallFinalize.
Error 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: UpgradeTo022, location: C:\Program Files\Test\, command: Upgrade-0.2.2.EXE
MSI (s) (CC:38) [07:21:19:061]: Product: Test -- Error 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: UpgradeTo022, location: C:\Program Files\Test\, command: Upgrade-0.2.2.EXE
The Custom Action is defined as follows:
<CustomAction Id="UpgradeTo022"
Directory="INSTALLLOCATION"
Impersonate="no"
Execute="deferred"
ExeCommand='Upgrade-0.2.2.EXE'>
Note: The custom action must be run with elevated privileges, thus the deferred execution and Impersonate="no" attributes.
It is scheduled as follows:
<InstallExecuteSequence>
<Custom Action="UpgradeTo022" Before="InstallFinalize">NOT Installed</Custom>
</InstallExecuteSequence>
The files are installed as follows:
<Component Id="CMP_UpgradeCommon" Guid="8ECF5076-732E-4010-A33B-BE362D818949" Win64="yes">
<File Id="FILE_UpgradeCommonDLL"
Source="..\Upgrade\UpgradeCommon\bin\Debug\UpgradeCommon.dll" Vital="yes"/>
</Component>
I've seen some allusions to running managed EXEs as being a problem with Custom Actions and MSis, but no real definitive answers.
I have also tried just running a very simple .EXE with no dependencies as a custom action, and have had no luck there.
Being somewhat new to creating installer packages, it's very likely I'm either trying to do something that isn't supported, or missing something simple.
Thanks in advance for any pointers in figuring this out.
JT.
Unfortunately this could be a number of things. Firstly are you sure .net 3.5 is installed on the target box? On from that, can you actually run the exe fine on the target machine?
One other thing to note is that deferred actions run under the context of the system account, so if the exe requires access to a profile for any reason, this will cause it to fail. I had this very issue when trying to generate a certificate as part of my install. I was scratching my head for ages on this and in the end had to spawn PowerShell with the -RunAs switch which then ran a script to call the exe. Might be worth checking to see if the app runs fine under the system account (http://www.tech-recipes.com/rx/1288/how-to-run-applications-in-the-local-system-account-lsa/)

Edit config in ProgramFiles with UAC turned on

My question is quite similar to
Launch application after installation complete, with UAC turned on
Rather than build a complex set of configuration screens into the installer, we would like to launch a config process after the installer is complete.
In all cases this will require the editing of content in ProgramFiles folder which is not editable as a standard user when UAC is enabled without elevation.
Options we know of and prefer not to use:
to elevate the whole installer with a bootstrap - we would like not to do this to support 1 action of execute the config at the end elevated.
including forced elevation in the config process - while we could in some cases work this into the app, in some cases we want to launch a simple editor on App.config where this forced elevation would not be an option.
Is there some way of getting an elevated version of
<Property Id="WixShellExecTarget" Value="[INSTALLDIR]\app.config" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" />
Or is it more appropriate to make custom UI and add the checkbox earlier in the UI and launch it as a deferred action without waiting, like
<CustomAction Id="Config.SetProperty" Property="Config" Value='"Open" app.config' />
<CustomAction Impersonate="no" Execute="deferred" Return="ignore"
Id="Config" BinaryKey="WixCA" DllEntry="CAQuietExec" />
Or do we just forget it all as a bad idea and the admin can go find the file and right click elevate to edit.
One of the options could be to launch your app non-elevated from the installer as you do now. Then when your app detects that it needs to edit config files, and it requests elevation.
Another option is to store config files in ProgramData directory rather than in Program Files. This location is writable without elevation. One caveat here: files and directories that created there will have write/modify permissions only for the user who created them; other users will have read-only access. If it's not desirable, you can change ACLs for your app config files.
I suggest a separate config tool, that requires elevation for all-user config and continues with per-user config if elevation is denied.
Launch this on completion of installer (from the UI sequence only, tie it to the 'Finish' button) so that normal installs launch and prompt for elevation on completion while silent installs require the Admin to manually launch the configuration tool or edit the config file themselves.
Note that if you're installing the config file as an MSI component, if it has been edited then an Advertised shortcut or running a Windows Installer 'repair' will overwrite this with the one included in the MSI. Our general solution has been to deploy a sample.config and require the administrator to edit copy this to an application.config post-install. If the application.config is not present, the configuration tool launches or an error message displays. This has the added benefit that the configuration is preserved on remove/upgrade.