I've written a PowerShell script (see below) to install a very large number of MSP updates (files with .msp extensions, deployed via Windows Installer) one after the other. Now, I'd like this script to also tell me when the installation of an MSP update failed.

Things that I have tried: Querying the error code. There are two approaches:

  • One is to get the error code using $LASTEXITCODE after running MSIEXEC.EXE directly. It is tedious.
  • The other involved adding -PassThru switch to Start-Process, storing its result into an object, say $a and read the error code using $a.ExitCode. Like this:

    $a=Start-Process msiexec.exe -ArgumentList "/p `"$MspRelPath`" /log `"$LogRelPath`" /passive /norestart" -Wait -PassThru
    Write-Host $a.ExitCode

Neither prove useful. It seems msiexec.exe always returns zero as the exit code.

In case anyone is interested, here is the script:

param (

  # Get script name
  $ScriptFileObject=(Get-Item $PSCommandPath)

  # Load Windows Forms and initialize visual styles

  # Is the script holding administrative privileges?
  $prp=new-object System.Security.Principal.WindowsPrincipal($wid)
  if ($IsAdmin -eq $false) {
    if (!$BypassAdminPrompt) {
      Start-Process powershell.exe -ArgumentList "-ExecutionPolicy $env:PSExecutionPolicyPreference -File `"$PSCommandPath`" -BypassAdminPrompt" -Verb RunAs
    } else {
      $result=[System.Windows.Forms.MessageBox]::Show("This script requires administrative privileges, which are absent.", $ScriptName, "OK", "Error");

  # Install...
  Set-Location $ScriptPath
  $MSP_list = Get-ChildItem *.msp -Recurse
  if ($MSP_list -eq $null) {
    $result=[System.Windows.Forms.MessageBox]::Show("Nothing found to install.`rSearch path was "+$ScriptPath, $ScriptName, "OK", "Error");
    $MSP_list | ForEach-Object {
      # Ordinarily, I'd pass the path in the form of ".\foldername\filename.msp" but Windows Installer does not accept that.
      # It must be in "foldername\filename.msp" form.
      $MspRelPath = $_.FullName.Substring($ScriptPath.Length+1)
      $LogRelPath = $MspRelPath+".log"
      Write-Host $MspRelPath
      Start-Process msiexec.exe -ArgumentList "/p `"$MspRelPath`" /log `"$LogRelPath`" /passive /norestart" -Wait
    Remove-Variable MspRelPath
    Remove-Variable LogRelPath
  Remove-Variable MSP_list
  $result=[System.Windows.Forms.MessageBox]::Show("Error!`r`r"+$Error[0], $ScriptName, "OK", "Error");
  • You can easily find installed updates in Control panel/Settings app. – Biswapriyo May 28 '17 at 12:46

either check Windows Events for MSIExec events or get the log output contents once done and check for failure indications.

I have a script that installs an MSI remotely using MSIExec, waits for MSIExec to close out (or the process/service to start)... if nothing happens after a typical install time, I check the log path included in the MSIExec call and check for failure or success messages

  • I don't find no Windows Event. I can ask msiexec to generate a log, but how do I interpret success or failure from that log programmatically? Hell, how do I interpret anything from that log? I am sure Microsoft intended this log to torment admins, not help them. – user477799 Jun 3 '17 at 18:13

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.