I'm trying to write a powershell script that I am kicking off from a python script that will allow me to go through and install each .msi file that is contained within a certain folder.
This is what I have so far:
$msiFiles = Get-ChildItem -Path "***PATH TO FOLDER***" -Recurse -Include *.msi
foreach($file in $msiFiles)
{
Write-Host "$file is being installed"
Start-Process "msiexec.exe" -arg "/I $file /qb ADDLOCAL=ALL ALLUSERS=TRUE" -Wait
Write-Host "$file is finished being installed"
}
I want to install these msi's in silent mode so I don't see or need ant GUI interaction. When it goes to install the msi's I get a pop up from the installer with all the possible commands to use to install stuff, but it doesn't actually install it.
What is the proper command to install these in silent mode?
Generally, if invoking msiexec
does nothing but show a dialog describing the command-line syntax, the implication is that there's a syntax problem.
The likely source of the syntax error is that the "..."
string you're using as the -arg
argument (full name: -Args
or -ArgumentList
) has $file
embedded in it without embedded quoting:
That is, if the value of $file
has embedded spaces, for instance (e.g., C:\Msi Installers\foo.msi
), the resulting msiexec
command will be syntactically invalid, because the space-separated tokens of the path are each considered an argument.
Bill_Stewart's helpful answer shows you how to use embedded quoting around $file
to solve this problem, by enclosing it in `"
(`
is PowerShell's escape character).
If you were to stick with passing the arguments as a single string, you would use:
Start-Process msiexec.exe -Args "/I `"$file`" /qb ADDLOCAL=ALL ALLUSERS=TRUE" -Wait
Arguably, however, it's cleaner not to pass a single, command-line-like string as the only argument, but to instead pass the arguments as elements of an array, which is indeed what -ArgumentList
/ -Args
was designed to accept (its parameter type is [string[]]
):
Start-Process msiexec.exe -Args /I, `"$file`", /qb, ADDLOCAL=ALL, ALLUSERS=TRUE -Wait
Note how $file
is still passed with embedded quoting, which is unfortunately required due to a bug in Start-Process
(as of Windows PowerShell v5.1 / PowerShell Core v6.1.0)
You may alternatively build up the array of arguments in advance; note how this is done in expression mode (with syntax more like regular programming languages), so the array elements all require quoting; also note how I'm using single quotes to define literal arguments:
# Create the array of arguments to pass to msiexec
$msiArgs =
'/I',
"`"$file`"", #`# !! enclosing in `"...`" is needed due to the bug mentioned above
'/qb',
'/ADDLOCAL=ALL',
'ALLUSERS=TRUE'
Start-Process msiexec.exe -Args $msiArgs -Wait
Try it this way:
$msiFiles = Get-ChildItem -Path "***PATH TO FOLDER***" -Recurse -Include *.msi
foreach ( $file in $msiFiles ) {
$fullPath = $file.FullName
Write-Host "'$fullPath' is being installed"
Start-Process -FilePath msiexec.exe -ArgumentList "/I `"$fullPath`"","/qb","ADDLOCAL=ALL","ALLUSERS=TRUE" -Wait
Write-Host "$fullPath is finished being installed"
}