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

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.

Related

CustomAction run as administrator

I have created a custom action:
<CustomAction Id='AddEventLog' BinaryKey='CustomActionEventLog.dll' DllEntry='AddEventLog' Return="check" Execute="immediate"/>
Install sequence
<Custom Action="AddEventLog" Before="InstallFinalize" />
My installer does pop up and say that it needs admin rights to run. Which I grant it.
InstallPrivileges='elevated' InstallScope='perMachine' AdminImage='yes'
However when it runs the custom action it doesn't work because its not running as administrator.
I even tried adding the following to app.manifest on my custom action project dll. It didn't help.
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
I have scoured all the tutorials and forum posts I can find on this subject. Noting has worked yet.
If anyone has any better tags for this please feel free to add them I have been struggling with this all day.
Update for clarification:
While my customAction does work with EventLog I am not using http://schemas.microsoft.com/wix/UtilExtension Util:EventSource. It is a genral question can you even force a customAction to run as administrator?
To run any custom action with administrator privileges you must run the custom action during the Server portion of the installation. ie: it must be a deferred custom action. Otherwise, I think you get a consent.exe message box asking for administrator privileges.
All msi installs work in two parts, Client and Server portions of the install. The Client portion is where you see the UI and set properties that may determine where things get installed and what gets installed (they must be marked Secure for the Server portion of the install to have access to them). The Server portion is what actually puts the files on your system. This always (??) requires administrator privileges because it can be writing stuff into Program Files and other protected file locations.
These custom actions must be marked "Execute='deferred'" and must also be scheduled between the InstallaInitialize and InstallFinalize.
Also to note, if you want to use values of any properties from your installation within the custom action you need to use a separate custom action which sets a specially named property with a special format. You then get the property values in your custom action querying the CustomActionData of the session object. There are lots of examples out there you can find.
Disclaimer, I've not done this previously, and personally I'd take the other approach of using the wix extensions, but you should be able to give elevated permission to your custom actions by using deferred execution and not impersonating the current user. So in your case, Execute="immediate" is what's standing in your way.
<CustomAction Id="MyCustomAction" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="no"/>
<!-- -or- -->
<CustomAction Id="MyCustomAction" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="commit" Return="check" Impersonate="no"/>
Source

Custom action with elevated privileges to run batch file

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.

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?

WiX v3.7 - How to remove file in uninstall that was created after installation

I'm creating an installer for a program using WiX. The program creates a log file and a .dat file in the program directory during the configuration process (after complete installation). How do I tell WiX to remove these files during the uninstallation process if they are not present during the installation process?
Any suggestions would be greatly appreciated.
One way is to define the problem out of existence by installing those files. That is, create and install an empty log file and a dat file—initialized to whatever empty or default is for it.
Another way is to put RemoveFile elements under the Component element that is most closely associated with the use those files (e.g., the program exe).
Are you sure you want writable files in the same directory as the program? Usually, programs are installed so that only administrators can change them. If the program uses data that any user can change to affect all users, the data would be stored under the ALLUSERS profile. And, data that a user can change that affects only that user would be stored under that user's profile.
When data is stored that way, it is a question as to when or even whether such data should be deleted. A user might want that data preserved for the next installation of the program even if the user doesn't know how the data is persisted.
This worked for me - use custom command and delete using "del" or "rmdir"
http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Forced-remove-of-folder-and-files-within-td698425.html
<Product>
<CustomAction Id="Cleanup_logfile" Directory="INSTALLFOLDER"
ExeCommand="cmd /C "del install.log""
Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="Cleanup_logfile" After="RemoveFiles" >
REMOVE="ALL"
</Custom>
</InstallExecuteSequence>
</Product>

Deleting files and directories before installation of MSI

Our product was installed via InstallShield Setup over the years. I changed the installation to MSI (WiX) this year. Now the MSI should clean up the directory, which remains the same.
One custom action in the MSI I implemented to start the uninstallation of the old product:
<CustomAction Id="UninstallIS" Property="QtExecCA" Value=""[WindowsFolder]IsUn0407.exe" -f "[ProgramFilesFolder]\company\product\Uninst.isu"" Execute="deferred" />
<CustomAction Id="QtExecCA" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="ignore" Impersonate="no" />
After the removal of the old product there are temporary files and some subdirectories that are different from client to client and are unknown to the InstallShield Setup, so I would try to delete them via the MSI.
Keeping the UAC in mind, I think that I can't use command-line commands to do this also the 'Remove File Table' is not useful here (to much unknown files and many directories).
What is a possible way to do this?
Thank You for any help!
You can use a Deferred custom action which has Impersonate flag set to "no". This way it will run under the local system account with full privileges.
The custom action can use custom code (for example an EXE or DLL) or a command line.
Please note that deferred custom actions can be scheduled only after InstallInitialize action in InstallExecuteSequence.
As a side-note, make sure you thoroughly test it. Deleting files from the target machine is very dangerous. You never know what you may end up deleting.