I'm creating an installer with Wix 3.10. This installer will need to execute a PowerShell script after the installation of the files.
To execute the PowerShell script I use the following:
<Component Id="AddUserInstallScript" Guid="{87DB934A-5ECF-4073-81F1-BA139F30A686}" Directory="PHONEMANAGER_FOLDER" >
<File Id="CreateADUserScript" Name="CreateADUser.ps1" Source="CreateADUser.ps1" KeyPath="yes"/>
</Component>
<Property Id="POWERSHELLEXE" Value="c:\Windows\System32\WindowsPowerShell\v1.0\powershell">
</Property>
<Condition Message="This application requires Windows PowerShell.">
<![CDATA[Installed OR POWERSHELLEXE]]>
</Condition>
<SetProperty Id="RunPSscriptCommand"
Before="RunPSscriptCommand"
Sequence="execute"
Value=""[POWERSHELLEXE]" -Version 3.0 -NonInteractive -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "&'[#CreateADUserScript]' -domainname '[SERVICE_USER_NETBIOSDOMAIN]' -password '[service_user_pwd]' -domainadminname '[DOMAIN_ADMINISTRATOR]' -domainadminpassword '[domain_administrator_pwd]' ; exit $$($Error.Count)" "
/>
<CustomAction Id="RunPSscriptCommand"
BinaryKey="WixCA"
DllEntry="WixQuietExec64"
Execute="deferred"
Return="check"
Impersonate="no"/>
<InstallExecuteSequence>
<Custom Action="RunPSscriptCommand" After="InstallFiles"><![CDATA[NOT Installed]]></Custom>
</InstallExecuteSequence>
When I run the installer I'm getting the following error in the log file:
MSI (s) (10:F8) [09:54:54:515]: Invoking remote custom action.
DLL: C:\Windows\Installer\MSI80E7.tmp, Entrypoint: WixQuietExec64
WixQuietExec64: Error 0x80070001: Command line returned an error.
WixQuietExec64: Error 0x80070001: QuietExec64 Failed
WixQuietExec64: Error 0x80070001: Failed in ExecCommon method
CustomAction RunPSscriptCommand returned actual error code 1603
(note this may not be 100% accurate if translation happened inside sandbox)
On the destination machines is PowerShell 3 installed. The script also uses PowerShell 3 modules.
I have included the option -InputFormat None but this makes no difference for PowerShell 3.
Any thoughts on this issue?
Try to run the script stand alone, in order to exclude command errors.
I have encountered same problem and what solved it was the command length (in your situation is RunPSscriptCommand's value), I have decreased the length to be less than 255 characters.
You can check your powershell version automatically using WixPSExtension.dll
<PropertyRef Id="POWERSHELLVERSION" />
<Condition Message="You must have PowerShell 1.0 or higher.">
<![CDATA[Installed OR POWERSHELLVERSION >= "1.0"]]>
</Condition>
I hope it will help.
Related
I am creating a Microsoft Installer (.msi file) using the Wixtoolset (Windows Installer XML). This installer must automate the installation of an existing .exe program (named installer.exe below) and copy a custom configuration file (named settings.conf below) to the target directory. In addition the installer must modify the configuration file using the InstallFiles command below. But the timing of events is critical. If the executable installer runs too early, it fails or exhibits strange behavior. And if the executable installer run too late in the install sequence, it overwrites my modified configuration file with the generic values. I believe this can be done by assigning a string to the Before or After property value. What Before or After property assignment will allow the executable to run properly but not overwrite the configuration file I moved by the CopyFile element? Here is my Wixtoolset XML code.
<Property Id="CONFIGFOLDER" Value="C:\acme\config" >
<Feature
Id="ConfigurationFile"
Title="Configuration File"
Level="1"
<ComponentRef Id="CMP_ACME_Config_File" />
</Feature>
<DirectoryRef Id="TARGETDIR">
<Component Id="CMP_ACME_Config_File" Guid="">
<File
Id="ACME_Config"
Source="MySettings.conf"
KeyPath="yes"
<CopyFile Id="Copy_ACME_Config"
DestinationProperty="CONFIGFOLDER"
DestinationName="settings.conf" />
</File>
</Component>
</DirectoryRef>
<Binary
Id="InstallerEXE"
SourceFile="installer.exe" />
<CustomAction
Id="Launch_Installer"
BinaryKey="InstallerEXE"
Impersonate="yes"
Execute="deferred"
ExeCommand=""
Return="check" />
<InstallExecuteSequence>
<Custom Action="Launch_Installer"
Before="InstallFiles">
</Custom>
</InstallExecuteSequence>
</Property>
I can't explain exactly why this works but assigning "InstallFiles" to the "After" property in the "Custom" element seems to do the trick.
<InstallExecuteSequence>
<Custom Action="Launch_Installer"
After="InstallFiles">
</Custom>
</InstallExecuteSequence>
New to Wix, but trying to learn!
I'm using heat to generate a 'directory.wxs' file that contains all of the files I need for my application. One of these files is a Powershell script that I need to run as a custom action. I'm trying to use the -suid flag so the File Id is consistent across runs. I don't like this solution as I may be in a bad way if I ever have two files with the same name... suggestions welcome on that one.
In the customaction.wxs file, I'm trying to use what is generated, "install-service-filebeat.ps1" as my Property value in my custom action. I have looked at a quite a few examples / problems similar to this across here and other sites. I may just be an idiot and missing something, but was wondering if my issue is with referencing the ID from my directory.wxs file, or if it elsewhere in my syntax.
Below are my scripts, I included them at the end as they are long.
Thanks in advance!
My PS script:
## Powershell Script to Create MSI ##
## Variables ##
# WIX Source Dir #
$WIX_DIR="C:\Program Files (x86)\WiX Toolset v3.11\bin"
# Source Dir for files to be enumerated into MSI #
$SRC_DIR="C:\application-directory"
# Name of our Application #
$APP_NAME="Filebeat"
# Where to stage the various .wxs files #
$STG_DIR="C:\wix-project\${APP_NAME}\stage"
# Where to deliver the Final Product #
$TGT_DIR="C:\wix-project\${APP_NAME}\output"
## Create MSI ##
# Compile the source files into a Fragment to be referenced by main builder Product.wxs #
& ${WIX_DIR}\heat.exe dir $SRC_DIR -srd -dr INSTALLDIR -cg MainComponentGroup -out ${STG_DIR}\directory.wxs -ke -sfrag -gg -ssuid -var var.SourceDir -sreg -scom
# Convert the .wxs files into .wxobj for consumption by light.exe #
& ${WIX_DIR}\candle.exe -dSourceDir="${SRC_DIR}" ${STG_DIR}\*.wxs -o ${STG_DIR}\ -ext WixUtilExtension -arch x64
# Create the final MSI package --CURRENTLY REFERENCING THE FILES EXPLICITY. NEED TO FIND WAY TO WILDCARD! #
& ${WIX_DIR}\light.exe -o ${TGT_DIR}\${APP_NAME}_installer.msi ${STG_DIR}\customaction.wixobj ${STG_DIR}\directory.wixobj ${STG_DIR}\product.wixobj -cultures:en-US -ext WixUIExtension.dll -ext WiXUtilExtension.dll
This generates my files just fine, and compiles them. The contents of my customaction.wxs:
<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Fragment>
<!--Define the CustomAction for running the PowerShell script-->
<CustomAction Id="RunPowerShellScript_set"
Property="install_service.ps1"
Value=""[\[]POWERSHELLEXE[\]]" -Version 2.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "&'4(var.SourceDir)\[\[]#install_service.ps1[\]]'; exit $$($Error.Count)"" />
<CustomAction Id="RunPowerShellScript"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="check"
Impersonate="yes" />
<!-- Ensure PowerShell is installed and obtain the PowerShell executable location -->
<Property Id="POWERSHELLEXE">
<RegistrySearch Id="POWERSHELLEXE"
Type="raw"
Root="HKLM"
Key="SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
Name="Path" />
</Property>
<Condition Message="This application requires Windows PowerShell.">
<![CDATA[Installed OR POWERSHELLEXE]]>
</Condition>
</Fragment>
</Wix>
And the relevant portion of my product.wxs:
<!-- Execute Custom Action -->
<InstallExecuteSequence>
<Custom Action="RunPowerShellScript_set" After="InstallFiles" />
<Custom Action="RunPowerShellScript" After="InstallFiles">
<![CDATA[NOT Installed]]>
</Custom>
</InstallExecuteSequence>
I think that your Property="install_service.ps1" is incorrect. Try to follow the pattern from my blog post. I hope it helps you, just tested my script again and it works. Have a nice day!
I have a custom action that requires elevated privileges. The purpose of this custom action is to run sc.exe and remove the service triggers for a service that ships with Windows (w32time).
Here are the snippets of significance:
<Property
Id="removeW32TimeTrigger"
Value=""[SystemFolder]sc.exe" triggerinfo w32time delete"
/>
<CustomAction
Id="removeW32TimeTrigger"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="ignore"
Impersonate="no"
/>
<InstallExecuteSequence>
<Custom Action="removeW32TimeTrigger" After="InstallInitialize" />
</InstallExecuteSequence>
I followed the example for deferred execution here:
http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html
The error from the log appears to be having trouble with my syntax for where to find sc.exe.
Action 11:36:48: removeW32TimeTrigger.
CAQuietExec: Command string must begin with quoted application name.
CAQuietExec: Error 0x80070057: invalid command line property value
CAQuietExec: Error 0x80070057: failed to get Command Line
I'm clearly doing something wrong. Any help would be appreciated.
Since you are running the CA in deferred you need to send CustomActionData with a type 51 custom action instead of using Property.
Try this and see if it works:
<CustomAction Id='removeW32TimeTrigger_set'
Property='removeW32TimeTrigger'
Value='"[SystemFolder]sc.exe" triggerinfo w32time delete'
Execute='immediate'/>
<CustomAction
Id="removeW32TimeTrigger"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="ignore"
Impersonate="no"
/>
<InstallExecuteSequence>
<Custom Action="removeW32TimeTrigger_set" After="CostFinalize" />
<Custom Action="removeW32TimeTrigger" After="InstallInitialize" />
</InstallExecuteSequence>
I have found a couple of examples showing how to run a PowerShell script from WiX but have not been successful running either of them. So, I'd like to post what I have with the hope that someone can point out what I am doing wrong.
<!--Install the PowerShell script-->
<DirectoryRef Id="INSTALLFOLDER">
<Component Id="cmp_ShutdownIExplore" Guid="{4AFAACBC-97BB-416f-9946-68E2A795EA20}" KeyPath="yes">
<File Id="ShutdownIExplore" Name="ShutdownIExplore.ps1" Source="$(var.ProjectDir)Source\PowerShell\ShutdownIExplore.ps1" Vital="yes" />
</Component>
</DirectoryRef>
<!--Define the CustomAction for running the PowerShell script-->
<CustomAction Id="RunPowerShellScript" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="yes" />
<InstallExecuteSequence>
<!--Invoke PowerShell script -->
<Custom Action="RunPowerShellScript" After="InstallFiles"><![CDATA[NOT Installed]]></Custom>
</InstallExecuteSequence>
<!-- Define custom action to run a PowerShell script-->
<Fragment>
<!-- Ensure PowerShell is installed and obtain the PowerShell executable location -->
<Property Id="POWERSHELLEXE">
<RegistrySearch Id="POWERSHELLEXE"
Type="raw"
Root="HKLM"
Key="SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
Name="Path" />
</Property>
<Condition Message="This application requires Windows PowerShell.">
<![CDATA[Installed OR POWERSHELLEXE]]>
</Condition>
<!-- Define the PowerShell command invocation -->
<SetProperty Id="RunPowerShellScript"
Before ="InstallFiles"
Sequence="execute"
Value =""[POWERSHELLEXE]" -Version 2.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "& '[#ShutdownIExplore.ps1]' ; exit $$($Error.Count)"" />
</Fragment>
When I run the installer I have created I get the following error (from log):
MSI (s) (DC:F8) [11:21:46:424]: Executing op: ActionStart(Name=RunPowerShellScript,,)
Action 11:21:46: RunPowerShellScript.
MSI (s) (DC:F8) [11:21:46:425]: Executing op: CustomActionSchedule(Action=RunPowerShellScript,ActionType=1025,Source=BinaryData,Target=CAQuietExec,)
MSI (s) (DC:9C) [11:21:46:459]: Invoking remote custom action. DLL: C:\Windows\Installer\MSI8228.tmp, Entrypoint: CAQuietExec
CAQuietExec: Error 0x80070057: failed to get command line data
CAQuietExec: Error 0x80070057: failed to get Command Line
CustomAction RunPowerShellScript returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 11:21:46: InstallFinalize. Return value 3.
I am not at all clear what this error is trying to say. Are my internal references bad? Is the command to execute the script bad? Something else?
Any help is most appreciated and thanks in advance.
Looks like you have scheduled the CAQuietExec action as deferred. In this case you have to pass the command line to be executed via a CustomActionData property called QtExecDeferred which is written to the execution script. The deferred action can then access the property from the script.
More details at http://wixtoolset.org/documentation/manual/v3/customactions/qtexec.html
I didn't understand Stephen's answer, however I eventually got it working with the help of this blog post.
Here's a summary of the change I made to Greg's code to get it to work:
I changed CAQuietExec to WixQuietExec (I'm not sure if this was necessary).
In SetProperty I changed the value of the Before attribute from InstallFiles to the Id of the custom action; in Greg's case it would be RunPowerShellScript.
Although unrelated to the question, I ended up needing to change the -Version of powershell to 3.0 from 2.0 to prevent an error when running my script.
Here was my actual working code:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" Name="..." Language="1033" Version="..." Manufacturer="..." UpgradeCode="...">
<Property Id="POWERSHELLEXE">
<RegistrySearch Id="POWERSHELLEXE"
Type="raw"
Root="HKLM"
Key="SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"
Name="Path" />
</Property>
<Condition Message="This application requires Windows PowerShell.">
<![CDATA[Installed OR POWERSHELLEXE]]>
</Condition>
<SetProperty Id="InstallMongoDB"
Before ="InstallMongoDB"
Sequence="execute"
Value=""[POWERSHELLEXE]" -Version 3.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "& '[#MONGODB_INSTALL.PS1]' ; exit $$($Error.Count)"" />
<CustomAction Id="InstallMongoDB" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Return="check" Impersonate="yes" />
<InstallExecuteSequence>
<Custom Action="InstallMongoDB" Before="InstallFinalize"><![CDATA[NOT Installed]]></Custom>
</InstallExecuteSequence>
<Component Id="MONGODB_INSTALL.PS1" Guid="..." DiskId="1">
<File Id="MONGODB_INSTALL.PS1" Name="mongodb-install.ps1" Source="mongodb-install.ps1"/>
</Component>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="APPLICATIONFOLDER" Name="...">
<Directory Id="InstallScripts" Name="InstallScripts">
<Component Id="MONGODB_INSTALL.PS1" Guid="..." DiskId="1">
<File Id="MONGODB_INSTALL.PS1" Name="mongodb-install.ps1" Source="mongodb-install.ps1"/>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
</Wix>
Only the following example helped me
https://github.com/damienbod/WiXPowerShellExample/blob/master/SetupWithPowerShellScripts/Product.wxs
you need to add smth similar into your 'Product.wxs'. the 'Value' property of the first 'CustomAction' contains a ps script (create and run a windows service in my case).
<!-- assign the string (ps command) to RegisterPowerShellProperty -->
<CustomAction Id="RegisterWindowsService"
Property="RegisterPowerShellProperty"
Value=""C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NonInteractive -InputFormat None -NoProfile sc.exe create MyService binpath= 'C:\Program Files (x86)\My service\MyService.exe';sc.exe start MyService"
Execute="immediate" />
<!-- Deferred execution of the above script -->
<CustomAction Id="RegisterPowerShellProperty"
BinaryKey="WixCA"
DllEntry="CAQuietExec64"
Execute="deferred"
Return="check"
Impersonate="no" />
<InstallExecuteSequence>
<!-- On installation we register and start a windows service -->
<Custom Action="RegisterWindowsService" After="CostFinalize">NOT Installed</Custom>
<Custom Action="RegisterPowerShellProperty" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>
you will need to add a reference to 'WixUtilExtension' in order to run the script.
Does Wix 3 have a built in way to just check whether a service exists? The closest guess I can come up with is using ServiceConfig and trying to detect a failure.
The AppSecInc. Community MSI Extensions has a Service_Exists custom action.
http://msiext.codeplex.com
Online Documentation:
http://code.dblock.org/Source/msiext/1.2/Docs/_custom_actions_2_system_tools_2_service_impl_8h.html#a6fdcddc7b04a310a368c08726d3be6b3
<Binary Id="SystemTools" SourceFile="$(var.BinDir)\SystemTools.dll" />
<CustomAction Id="SetServiceName" Property="SERVICE_NAME" Value="Service1" />
<CustomAction Id="ServiceExists" BinaryKey="SystemTools" DllEntry="Service_Exists" Execute="immediate" Return="check" />
<InstallExecuteSequence>
<Custom Action="SetServiceName" After="InstallFiles">NOT Installed</Custom>
<Custom Action="ServiceExists" After="SetServiceName">NOT Installed</Custom>
</InstallExecuteSequence>
SERVICE_EXISTS is set to "1" if service exists, "0" otherwise.