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