Run PowerShell script from WiX installer

24,525

Solution 1

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

Solution 2

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="&quot;[POWERSHELLEXE]&quot; -Version 3.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command &quot;&amp; '[#MONGODB_INSTALL.PS1]' ; exit $$($Error.Count)&quot;" />

        <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>

Solution 3

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="&quot;C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe&quot; -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.

Share:
24,525
Greg Prosch
Author by

Greg Prosch

I am a Software Development Manager for A-T Solutions, Inc.

Updated on July 09, 2022

Comments

  • Greg Prosch
    Greg Prosch almost 2 years

    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 ="&quot;[POWERSHELLEXE]&quot; -Version 2.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command &quot;&amp; '[#ShutdownIExplore.ps1]' ; exit $$($Error.Count)&quot;" />
    </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.

  • Greg Prosch
    Greg Prosch over 11 years
    That did the trick! That was the piece I was missing and which was not evident in the other examples I looked at. Thank you very much.