Powershell start-process issue when path contains R character after parenthesis - powershell

I am trying to install msi from commandline using powershell command. My msi path contains R character after parenthesis. Due to this powershell throws below error.
PowerShell.exe -ExecutionPolicy Bypass Start-Process -Wait -FilePath msiexec -ArgumentList /i, `"c:\program files\xxx (R) xxx.msi`"
If my path contain no parenthesis, it is working fine. Can someone help me resolve this issue.
Do not suggest some other method to do it. I would like to know the path issue as it works when my msi path doesnt have parenthesis.

From PowerShell:
# Note that parameter -FilePath is implied for `msiexec`, and
# -ArgumentList for `'/i ...'`
Start-Process -Wait msiexec '/i "c:\program files\xxx (R) xxx.msi"'
The most robust approach is to use a single -ArgumentList (-Args) argument that encodes all arguments, using embedded quoting, rather than passing arguments individually, due to a long-standing bug detailed in this answer.
From cmd.exe, there's no reason to involve PowerShell in this case; the built-in start command will do:
start /wait msiexec /i "c:\program files\xxx (R) xxx.msi"

Related

Powershell One Liner with spaces from CMD

I have to create a Powershell Oneliner which will be called from CMD.
The Powershell should uninstall an .MSI first and then install another one.
The Problem is the .MSI I have to install has spaces in its name.
The first part of the command works, but obviously the second part doesn't work.
How could I solve it?
powershell.exe -noprofile -command "Start-process msiexec.exe -wait -Argumentlist '/x {aaaaaaaaaaaa} /qn';Start-Process msiexec.exe -wait -ArgumentList '/i `"the msi with spaces in it.msi`" ADV_SUPRESSDATABASETEST=1 SERVER_PROP='someserver\AB' DATABASE_PROP='DATABASE' SQL_TRUSTED_CONNECTION=1'"
I have also tried with $variables, but with single quote they aren't expanded. (it's possible to use variables in a one-liner?
How could I possibly solve this?
Leaving aside that calling msiexec.exe with start /wait directly from cmd.exe (a batch file) may be the better solution, as discussed in the comments on the question, you can make this work via the Windows PowerShell CLI (powershell.exe) as follows:
powershell.exe -noprofile -command Start-process msiexec.exe -wait -Argumentlist '/x {aaaaaaaaaaaa} /qn'; Start-Process msiexec.exe -wait -ArgumentList '/i "the msi with spaces in it.msi" ADV_SUPRESSDATABASETEST=1 SERVER_PROP="someserver\AB" DATABASE_PROP="DATABASE" SQL_TRUSTED_CONNECTION=1'
The outer "..." around the -command argument have been removed to avoid nested double quotes.
In case where you do need nested "-quoting, use \"[1] with the Windows PowerShell CLI (powershell.exe) and - more robustly "" with the PowerShell [Core] 7+ CLI (pwsh.exe).
Note: If the command for PowerShell contained unquoted characters such as & and |, they would be interpreted by cmd.exe, which can be avoided by enclosing the entire command in "..." or by individually ^-escaping such characters (e.g., ^& to pass & through to PowerShell).
What is inside the '...' strings is directly passed to msiexec and must therefore use "-quoting only.
[1] Even though PowerShell-internally it is ` that serves as the escape character.

passing double quotes through PowerShell and msiexec

I am trying to install the Tenable Nessus agent via PowerShell script and am running into no end of issues because the Nessus MSI syntax requires a mix of no quotes and double quotes. The problem I am running into is that PowerShell needs me to escape the double quotes which seems to break the syntax understood by msiexec.exe. Let me show you what I am doing and what I have tried. I am not a strong PowerShell person, and the solution is escapes me.
Here is the code block where I set the variable with all the MSI arguments. The Nessus syntax requires that all its configs be surrounded by double quotes except for NESSUS_KEY. Earlier in the script I define the other variables.
$MSI_Arguments = #(
"/i"
"$Install_File"
"NESSUS_KEY=$Nessus_Key"
"NESSUS_SERVER=""cloud.tenable.com:443"""
"NESSUS_NAME=""$FQDN"""
"NESSUS_GROUPS=""$Nessus_Group"""
# The colon in front of a variable needs special treatment ({}).
"NESSUS_PROXY_SERVER=""${Proxy_Server}:${Proxy_Port}"""
"NESSUS_OFFLINE_INSTALL=""yes"""
"/qn"
"/norestart"
"/L*v ""$LogPath\Tenable_MSI.log"""
)
I then try running the command like so. This command does not work properly - the install proceeds, but because the parameters in -ArgumentList needs to be surrounded by double quotes, it does not get all the parameters, resulting in a broken installation.
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList $MSI_Arguments
When I put $MSI_Arguments in double quotes, I then break how PowerShell deals with the double quotes in the variable code block. I have tried the following, but none work.
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "$MSI_Arguments"
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "$$(MSI_Arguments)"
$MSI_Command = Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "${MSI_Arguments}"
I have even tried just running the command straight-up, no variables, just to try and figure out the syntax. I tried two double quotes (similar to what I am trying in the variable code block) and it it works.
Start-Process -Wait -NoNewWindow -PassThru -FilePath "msiexec.exe" -ArgumentList "/i C:\temp\NessusAgent-7.7.0-x64.msi NESSUS_KEY= NESSUS_SERVER=""cloud.tenable.com:443"" NESSUS_NAME=""foo"" NESSUS_GROUPS=""bar"" NESSUS_PROXY_SERVER=""proxy.company.com:80"" NESSUS_OFFLINE_INSTALL=""yes"" /qn /norestart /L*v ""C:\temp\Tenable_MSI.log"""
Now that I have a working command, I cannot figure out how to modify the variable block to work. If I just add more doubled double quotes, I get errors. If I add "" I get errors. If I add " I get errors... I am so close, yet so far.
Please, rescue me from this hell I am in. At this point I am wondering if Passing double quotes through PowerShell + WinRM has the answer, and I should base64 encode the install string, but that may be beyond my skillset given how I use variables...

Powershell Removing Quotes Argument

I'm using Start-Process to start another instance of Powershell as an administrator but when I try to pass the argument list, whether as a variable or as a plain string, Powershell removes the quotes. Below is the command I'm using:
$argu = '-noexit "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat"';
powershell Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
This is the error I get:
x86 : The term 'x86' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
At line:1 char:88
+ ... Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\v ...
+ ~~~
+ CategoryInfo : ObjectNotFound: (x86:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Thank you in advance for any help.
Update:
$argu = '''-noexit ""C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat""''';
powershell Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
This almost fixes it but now I'm getting the error above in the second window instead of the first.
(A) From inside PowerShell:
$argu = '-noexit -command & \"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat\"'
Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
Note: I'm not calling Start-Process via powershell.exe, as there is generally no need for that.
The embedded " are \-escaped, which is what PowerShell requires when you call its CLI (perhaps surprisingly, given that PowerShell-internally it is ` that acts as the escape character).
That said given that the " are embedded inside '...' here, they shouldn't require extra escaping - see below.
The file path to execute is prefixed with call operator &, because you need it in order to execute files that are specified in quoted form.
Note that I've added -Command, which is not strictly necessary in Windows PowerShell, but would be if you ran your command from PowerShell Core (which now defaults to -File).
Alternatively, you could also specify your arguments individually, as part of an array, which is arguably the cleaner solution:
$argu = '-noexit', '-command', '&', 'de',
'\"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat\"'
Start-Process -Verb RunAs -FilePath powershell -ArgumentList $argu
Sadly, even in this case you need the extra, embedded quoting for arguments that contain spaces, which is a known Start-Process problem being tracked on GitHub.
PowerShell's handling of quoting when calling external programs is generally problematic; the current issues are summarized in this GitHub issue.
(B) From outside PowerShell (cmd.exe, a custom File Explorer context menu):
powershell -command Start-Process -Verb RunAs -FilePath powershell -ArgumentList '-noexit -command . ''C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat'''
single-quoting is now employed (with nested single quotes escaped as ''), because double-quoting would substantially complicate the escaping.
. is used instead of & to execute the .bat file, which avoids a problem with how the & is parsed; while . generally serves a different purpose than &, the two operators behave the same when calling external programs.
If you also want to set the working directory for the PowerShell session that ultimately opens elevated, you need to incorporate an explicit Set-Location (cd) call into the command string, because Start-Process -Verb RunAs always defaults to the SYSTEM32 folder (even the -WorkingDirectory parameter doesn't help in that case).
For that to work safely, however, you must quote the directory path using double-quoting, given that file names may contain single quotes; with %V as the directory path (which File Explorer supplies to commands invoked via custom context menus), the properly escaped Set-Location call looks like this (I wish I were kidding):
Set-Location \"\"\"%V%\"\"\"
Integrated into the full command (using Set-Location's built-in alias cd for brevity):
powershell -command Start-Process -Verb RunAs -FilePath powershell -ArgumentList '-noexit -command cd \"\"\"%V%\"\"\"; . ''C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvars64.bat'''
As an aside: PowerShell Core now has a -WorkingDirectory (-wd) CLI parameter that allows you to control the startup directory more robustly (pwsh -wd "c:\path\to\dir" ...); in fact, it was precisely the File Explorer custom context-menu use case that prompted the introduction of this parameter.

Power Shell Invoking an MSI

I am trying to run an msi installer file using powershell. Below is my power shell code:-
$argumentlist = "/i D:\FolderTest\InstallerTest 1.9.0.39621 Setup.msi /qn /l*v D:\FolderTest\InstallLog.log"
Start-Process -FilePath "C:\Windows\System32\msiexec.exe" -ArgumentList $argumentlist
Every time I try to run this code though The Windows installer appears telling me that the argumentList variable isnt set correctly. Can anybody tell me what the problem is with this code?
I think that spaces in the msi filename are what's preventing the msiexec to work properly. Try something like:
$argumentlist = "/i 'D:\FolderTest\InstallerTest 1.9.0.39621 Setup.msi' /qn /l*v D:\FolderTest\InstallLog.log"
PowerShell is a shell. It's designed to run commands you type. You don't need to use Start-Process. Just type the command and press Enter.
PS C:\> msiexec /i "D:\FolderTest\InstallerTest 1.9.0.39621 Setup.msi" /qn /l*v "D:\FolderTest\InstallLog.log"
As with any command, if a parameter contains spaces, enclose it in quotes.

Debugging a Powershell start-process line

I'm using Powershell's standard ISE to develop a script that, among other things, calls start-process using a pre-defined filepath. Here is the code:
$MSTEST ="C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\MSTEST.EXE"
$TESTSETTING="D:\Source\Test\DEV\FIREBIRD\QA\LoadTesting\WebTests\perfvsctlr2.testsettings"
$TESTCONTAINER1="D:\Source\Infinity\DEV\FIREBIRD\QA\LoadTesting\WebTests\Test.AppFx.LoadTesting.Test\LoadTestDefs\Test_Interactive_Peak_Workload.loadtest"
start-process $MSTEST -ArgumentList "/Testsetting: $TESTSETTING /Testcontainer: $TESTCONTAINER1 /resultsfile: $RESULTSFILE"
When I pass the variables and then try to manually execute the start-process line from the Powershell prompt it simply opens a window and closes it without displaying the error. So far I've used the -NoNewWindow argument and tried calling Powershell from the Run line with the -noexit argument. So far, no luck.
How is $RESULTSFILE defined?
You shouldn't have a space after the colon (and you generally have to look out for spaces in any of your arguments. You don't seem to have any, but spaces in arguments require proper quoting).
You can get the error message from Start-Process by calling it with some extra arguments (the line below produces an error on purpose):
Start-Process $MSTEST -ArgumentList "\testcontainer: file" -wait -NoNewWindow -RedirectStandardError err -RedirectStandardOutput out