SophisticatedBear
SophisticatedBear

Reputation: 69

Automate .msi installs

I'm trying to mass install a bunch of .msi's one after the other. But when I run my powershell script the msiexec /? comes up as if my arguments are wrong. What am I missing here?

$Path = Get-ChildItem -Path *my path goes here* -Recurse -Filter *.MSI
foreach ( $Installer in ( Get-ChildItem -Path $Path.DirectoryName -Filter *.MSI ) ) {
    Start-Process -Wait -FilePath C:\windows\system32\msiexec.exe -ArgumentList "/i '$Installer.FullName'"
}

Upvotes: 2

Views: 613

Answers (3)

js2010
js2010

Reputation: 27566

In Powershell 5.1, with msi's, you can also do:

install-package $installer.fullname

Upvotes: 0

Olaf
Olaf

Reputation: 5252

The syntax "/i '$Installer.FullName'" is not correct. It should be "/i", $Installer.FullName in your code.

Surrounding a PowerShell object in double quotes triggers string expansion. When that occurs, only the variable itself is substituted with its value and then the remaining characters not part of the name are treated as a string. You can see when you run the following snippet that the object performed a ToString() and then just added the string .FullName to it.

foreach ( $Installer in ( Get-ChildItem -Path $Path.DirectoryName -Filter *.MSI ) ) {
     "/i $Installer.FullName"
}

If you must have double quotes, then a way around it is to use the sub-expression operator $(). Anything inside is treated as an expression by the parser. So the more verbose "/i '$($Installer.FullName)'" would technically work.

The complete code should be:

$Path = Get-ChildItem -Path *my path goes here* -Recurse -Filter *.MSI
foreach ( $Installer in ( Get-ChildItem -Path $Path.DirectoryName -Filter *.MSI ) ) {
    Start-Process -Wait -FilePath C:\windows\system32\msiexec.exe -ArgumentList "/i", $Installer.FullName
}

Upvotes: 3

mklement0
mklement0

Reputation: 439912

Olaf's answer contains good pointers, but let me try to boil it down conceptually:

There are two unrelated problems with your attempt:

  • Inside expandable strings ("...") only simple variable references ($Installer) can be used as-is; expressions ($Installer.FullName) require $(), the subexpression operator: $($Installer.FullName) - see this answer for an overview of expandable strings (string interpolation) in PowerShell.

  • Since you're passing the arguments for msiexec via -ArgumentList as a single string, only embedded double quoting, "...", is supported, not '...' (single quoting).

Therefore, use the following (for brevity, the -FilePath and -ArgumentList arguments are passed positionally to Start-Process):

Get-ChildItem $Path.DirectoryName -Recurse -Filter *.MSI | ForEach-Object {
  Start-Process -Wait C:\windows\system32\msiexec.exe "/i `"$($_.FullName)`""
}

Note:

The -ArgumentList parameter is array-typed ([string[]]). Ideally, you'd therefore pass the arguments individually, as the elements of an array: '/i', $_.FullName, which wouldn't require you to think about embedded quoting inside a single string.

Unfortunately, Start-Process doesn't handle such individually passed arguments properly if they contain embedded spaces, so the robust solution is to use a single -ArgumentList argument comprising all arguments, with embedded double-quoting, as necessary, as shown above.

See this answer for a more detailed discussion.

Upvotes: 3

Related Questions