Redirect MSIEXEC Log Output to stdout/stderr for CI/CD Pipelines


2 views

When automating MSI package installations in CI/CD environments, developers often hit a roadblock: msiexec only writes logs to files (/l*v switch) rather than streaming to stdout/stderr. This creates cumbersome workarounds in pipeline scripts where we need immediate feedback.

Before exploring solutions, let's eliminate some common dead-ends:

:: These WON'T work
msiexec /i package.msi /qn | more
msiexec /i package.msi /qn > output.txt
start /B msiexec /i package.msi /qn

For modern Windows systems, we can leverage PowerShell's stream redirection capabilities:

$msiArgs = @(
    "/i", "package.msi",
    "/qn",
    "/l*vx", "msilog.txt"
)
Start-Process msiexec -ArgumentList $msiArgs -NoNewWindow -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt
Get-Content stdout.txt, stderr.txt, msilog.txt | Write-Host

For immediate feedback during long installations:

$logWatcher = {
    param($path)
    Get-Content $path -Wait -Tail 10
}
Start-Job -ScriptBlock $logWatcher -ArgumentList "msilog.txt"
msiexec /i package.msi /qn /l*v msilog.txt

The solution becomes more complex with remote execution:

psexec \\target -h -n 60 -accepteula cmd /c "msiexec /i package.msi /qn /l*v msilog.txt & type msilog.txt" > local_output.txt

For Jenkins, here's a declarative pipeline example:

pipeline {
    agent any
    stages {
        stage('Install MSI') {
            steps {
                bat '''
                msiexec /i package.msi /qn /l*v install.log
                type install.log
                '''
            }
            post {
                always {
                    archiveArtifacts artifacts: 'install.log'
                }
            }
        }
    }
}
  • Log file handles remain locked during installation - use the streaming approach for real-time access
  • MSI logging adds 10-20% overhead to installation time
  • Some corporate environments block simultaneous file access

When automating MSI package installations in CI/CD environments, we often hit a frustrating limitation: msiexec.exe only writes logs to files by default. This creates unnecessary complexity when we need real-time log streaming for build pipelines.

The conventional approach using /l*v creates separate log files:

msiexec /i package.msi /l*v install.log

This requires additional steps to read the file contents back into your CI system. For Jenkins or other CI tools, this means:

type install.log
# or
Get-Content install.log

Here's the PowerShell magic to capture MSIEXEC output in real-time:

$process = Start-Process msiexec.exe -ArgumentList "/i package.msi /q" -NoNewWindow -PassThru -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt
$process.WaitForExit()

Get-Content stdout.txt, stderr.txt | Write-Host

For true real-time streaming during installation:

Start-Process msiexec.exe -ArgumentList "/i package.msi" -NoNewWindow -PassThru | ForEach-Object {
    if ($_.HasExited) {
        if ($_.ExitCode -ne 0) {
            throw "Installation failed with exit code $($_.ExitCode)"
        }
    } else {
        $_.StandardOutput.ReadLine() | Write-Host
        $_.StandardError.ReadLine() | Write-Host
    }
}

Here's how to implement this in a Jenkins pipeline:

pipeline {
    agent any
    stages {
        stage('Install MSI') {
            steps {
                powershell '''
                $psi = New-Object System.Diagnostics.ProcessStartInfo
                $psi.FileName = "msiexec.exe"
                $psi.Arguments = "/i package.msi /qn"
                $psi.RedirectStandardOutput = $true
                $psi.RedirectStandardError = $true
                $psi.UseShellExecute = $false
                
                $process = [System.Diagnostics.Process]::Start($psi)
                while (!$process.HasExited) {
                    $process.StandardOutput.ReadLine() | Write-Host
                    $process.StandardError.ReadLine() | Write-Host
                }
                
                if ($process.ExitCode -ne 0) {
                    error "Installation failed"
                }
                '''
            }
        }
    }
}
  • Buffer handling - MSIEXEC output can be bursty rather than smooth
  • Exit code verification - Always check the return code (0 for success)
  • Timeout handling - Implement proper timeouts for CI environments
  • Log verbosity - Consider adding /l*v when debugging complex issues