calls to cmd from powershell not parsing correctly? - powershell

I am trying to run cURL.exe from powershell as part of a larger script to create various user accounts as new individuals are onboarded.
(I cannot use invoke-method or invoke-webrequest because the header parameter wont accept a string)
The issue is that I can run it from a cmd window perfectly but I cannot seem to make the same thing run from powershell:
curl.exe -H "Authorization: {\"apiKey\":\"ResetAPIKey,\"username\":\"admin\"}" -d "username=someUserName&email=somebody#domain.com&firstName=Joe&lastName=Friday" "http://URL:8080/v1/users/create"
This runs just fine from cmd but when attempting to run from powershell, I get errors like 'url not found' or 'That path/method is not supported'
Additional format attempts:
encasing the whole thing in single quotes
replacing each /" (forward slash + double quote) with `" (backtick + double quote)
adding all the arguments to a here-string variable
variations of above
I would assume I can write it to a .bat file and then run that from powershell but I'd rather not be forced to do that if possible

The stop parsing parameter is a good choice for this type of thing.
The stop-parsing symbol (--%), introduced in Windows PowerShell 3.0,
directs Windows PowerShell to refrain from interpreting input as
Windows PowerShell commands or expressions.
In your case though you need something a little different as you want variables in the mix that need to be resolved.
I figured there would be a dupe for this. Most of those answer are about converting curl.exe to something like Invoke-WebRequest which is something you should look into. In this case, if you have dynamic content use the call operator like this.
$arguments = "-H ""Authorization: {\""apiKey\"":\""ResetAPIKey,\""username\"":\""admin\""}"" -d ""username=someUserName&email=somebody#domain.com&firstName=Joe&lastName=Friday"" ""http://URL:8080/v1/users/create"""
&"curl.exe" $arguments
Noticed I doubled up the inner quotes on the string. If you look at the variable after it would display properly. Now you should be able to make changes.

Passing complex command line to native application from PowerShell can be tricky. PowerShell can add extra quotes in some cases. Although, rules for this are not that complex, but them are not well (if at all) documented, and also there is some substantial changes in that rules in v2-v4 vs v5. IMHO, using stop parsing operator --%, as #Matt offered, is the best approach, in case you targeting on v3+.
Stop parsing operator does not limit you to use literal strings only. You still can use variables with it. That operator expand environment variables in CMD syntax: %VariableName%. So you can assign required values to some environment variables and use them in command line:
$Env:Env_UserName='someUserName'
$Env:Env_Email='somebody#domain.com'
$Env:Env_FirstName='Joe'
$Env:Env_LastName='Friday'
curl.exe --% -H "Authorization: {\"apiKey\":\"ResetAPIKey,\"username\":\"admin\"}" -d "username=%Env_UserName%&email=%Env_Email%&firstName=%Env_FirstName%&lastName=%Env_LastName%" "http://URL:8080/v1/users/create"
Other variant will be to generate full arguments line, you want to pass to native application, and than pass it in single environment variable:
$Arguments=...
$Env:Env_Arguments=$Arguments
curl.exe --% %Env_Arguments%

Matt's answer with the call operator looks like it should work, but if it's not there's a similar alternative I've had good luck with before. Instead of passing your arguments as a string, you can pass them as an array and PowerShell will handle them correctly. In your case, it'd look something like this:
$arguments = #("-H", 'Authorization: {\"apiKey\":\"ResetAPIKey,\"username\":\"admin\"}', "-d", '"username=someUserName&email=somebody#domain.com&firstName=Joe&lastName=Friday"', 'http://URL:8080/v1/users/create')
& "curl.exe" $arguments

Related

PowerShell - Merge two variables into one

I'm learning PowerShell so please forgive (what I'm sure is) a simple question.
I'm used to coding BATCH scripts and if I wanted to merge %USERDOMAIN% and %USERNAME% I would:
set zFullUsername=%USERDOMAIN%\%USERNAME%
echo %zFullUsername%
How can I do the same in PowerShell?
Thank you for your time.
On a supported Operating System, I wouldn't even bother with environment variables for this:
$zFullUsername = whoami
Then just access it as required:
$zFullUsername
In PowerShell, you can access environment variables in a few different ways. The way I recommend is to use the $env:VAR variable to access them.
$user = $env:USERNAME
$domain = $env:USERDOMAIN
echo "$domain\$user"
Note: \ is not an escape character in the PowerShell parser, ` is.
Similarly to rendering the echo command (echo is an alias of Write-Output btw) you can create a username variable like so:
$fullUserName = "$domain\$user"
Or you can skip right to creating $fullUserName straight from the environment variables:
$fullUserName = "${env:USERDOMAIN}\${env:USERNAME}"
Note: When variables have non-alphanumeric characters in them, the ${} sequence tells PowerShell everything between the ${} is part of the variable name to expand.
It seems the : in $env:VAR is actually an exception to this rule, as"Username: $env:USERNAME" does render correctly. So the ${} sequence above is optional.
To avoid confusion when trying to apply this answer in other areas, if you needed to insert the value of an object property or some other expression within a string itself, you would use a sub-expression within the string instead, which is the $() sequence:
$someVar = "Name: $($someObject.Name)"
When using either ${} or $(), whitespace is not allowed to pad the outer {} or ().

How do I run this in Powershell?

Here is the command that works in command prompt.
C:\Temp\Agent.exe CustomerId={9c0-4ab1-123-102423a} ActivationId={9c0-4ab1-123-102423a} WebServiceUri=https://Agent/
Here is the error. (I have tried invoke-command and arguments but I think the { is causing issues.
Error:
Agent.exe: The command parameter was already specified.
You are certainly not required to use Start-Process (although it may "work," with some limitations, in some scenarios). The simplest and most straightforward answer is to quote the arguments:
C:\Temp\Agent.exe 'CustomerId={9c0-4ab1-123-102423a}' 'ActivationId={9c0-4ab1-123-102423a}' 'WebServiceUri=https://Agent/'
If the executable you want to run is in a path that contains spaces (or the executable filename itself contains spaces), quote the command and use the & (call/invocation) operator; e.g.:
& 'C:\Temp Dir\Agent.exe' 'CustomerId={9c0-4ab1-123-102423a}' 'ActivationId={9c0-4ab1-123-102423a}' 'WebServiceUri=https://Agent/'
Remarks:
If you need string interpolation (i.e., automatic expansion of $variable names inside strings), then use " instead of ' as your quote character. Use ' instead of " (as in the examples above) to prevent string interpolation.
Parameter quoting in this case is required because the { and } symbols have special meaning in PowerShell.
The proper way to run external programs is to use Start-Process. It gives you a couple of additional options like a separate ArgumentList parameter, running-as another user, or redirecting outputs:
Start-Process -FilePath 'C:\Temp\Agent.exe' -ArgumentList #(
# Arguments are space-separated when run. You could also just use one big string.
'CustomerId={9c0-4ab1-123-102423a}',
'ActivationId={9c0-4ab1-123-102423a}',
'WebServiceUri=https://Agent/'
)

Call an executable with quotes at specific positions

I want to call an executable from a PowerShell script that requires quotes at specific positions in the argument list. Although I found similar questions I did not find a solution at all.
This is what the command must look like on the command line:
reptool.exe --profile="C:\My profile"
The parameter value ("C:\Profiles...") is supposed to be generated dynamically using a variable:
$repToolProfile = "C:\My profile"
This is what I have already tried:
&"reptool.exe" --profile=$repToolProfile
Fails as the argument is given as "--profile=C:\My profile" (quotes around the whole argument).
&"reptool.exe" --profile="$repToolProfile"
Fails as the argument is given as "--profile=C:\My profile" (quotes around the whole argument, same as above).
&"reptool.exe" "--profile=`"$repToolProfile`"
Fails as the argument is given as "--profile="C:\My profile"" (quotes around the whole argument and the value).
I cannot use single quotes or the "verbatim operator" (--%) as I have to use a PowerShell variable, neither I can use Start-Process as it is called asynchroneously (even when I use the -Wait parameter. Also I want to check the exit code. I don't want to convert my arguments to Base64.
This worked for me:
$command = '& "reptool.exe" --% ' + "--profile=`"$repToolProfile`"
Invoke-Expression $command

Build up a string to be passed to call operator

I need to build a string that is actually a command-line, and then execute the contents of that command-line. I'd hoped the call operator (&) would help, but it appears not. Here is a simple contrived example. The following works as expected, it pings a website:
$command = "ping"
$website = "www.bbc.co.uk"
& $command $website
however if I change it to this:
$command = "ping"
$website = "www.bbc.co.uk"
$cmd = "$command $website"
& $cmd
I get an error:
The term 'ping www.bbc.co.uk' is not recognized as the name of a
cmdlet, function, script file, or operable program.
Is there a way to dynamically build up a command-line as a string, and then execute it?
Yes, but you need to use Invoke-Expression (which is just like eval), instead of the call operator. Note that you also need to ensure that all your quoting is correct in that case. E.g.
$cmd = "'$command' '$website'"
would work in your trivial example, unless $command or $website contained single quotes. The problem here is essentially that everything you put into the string is subject to the usual parsing rules of PowerShell.
Generally, if you can, stay as far away from Invoke-Expression as you can. There are a few problems that need it, but invoking external programs ... not so much.
A much better alternative, especially if you have an arbitrary number of arguments, is to just collect the arguments in an array and use the splat operator (note the # in the code example below):
$command = 'ping'
$arguments = '-t','www.bbc.co.uk'
&$command #arguments
This ensures that arguments are properly quoted when necessary and generally avoids a lot of headaches you're going to get with Invoke-Expression.
(Side note: Whenever you have a problem in PowerShell and think »Oh, I'm just going to use a string«, it's often time to rethink that. This includes handling file names, or command lines. PowerShell has objects, reducing it to the capabilities of earlier shells just yields the same pain you have elsewhere too, e.g. multiple levels of escaping, sometimes with different syntaxes, etc. And most of the time there are better ways of solving the problem.)

Powershell remove quotes when start process

Let's look at the code below
$SBK="0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98"
./windows/nvflash/nvflash.exe --sbk 0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98
./windows/nvflash/nvflash.exe --sbk "0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98"
./windows/nvflash/nvflash.exe --sbk $SBK
I have define a string var $SBK and then I'm going to pass it for some app. The first process call is working properly. The second one fails and therefore application doesn't accepts quotes. But the third call is failed too with the same error. It seems that powershell passes quotes, those are causing errors. But how to eliminate them? Thanks beforehand.
Try doing
iex "./windows/nvflash/nvflash.exe --sbk $SBK"
Also, get echoargs.exe from PowerShell Community Extensions to see how args are passed from Powershell to commands etc.
You should probably not use Invoke-Expression (see This Post from the PowerShell Team)
Instead, you can create an array of arguments, and then pass them using the call operator. See this post of mine on the subject for more details.
In your case, it would look something like this:
$SBKArgs="--sbk", "0x1682CCD8", "0x8A1A43EA", "0xA532EEB6", "0xECFE1D98"
$cmd = Get-Command ./windows/nvflash/nvflash.exe
& $cmd $sbkargs
Hope this Helps