I'm having big issues with powershell, web deploy and escaping command paths. I'm trying to run a powershell script before the deployment using the runCommand provider in Web Deploy. However, powershell seems to interpret paths with spaces as seperate parameters even when I encase in double-quotes.
This is what is output if I run
web.deploy.cmd /y
Info: Updating runCommand (powershell.exe -ExecutionPolicy Bypass invoke-command "C:/local dev/mobile/Core/Web services/wsAuthUser/mobilestability/src/wsAuthUser/obj/Debug/Package/PackageTmp/install/installboot.ps1" -computername server01 -argumentlist Mobile2/AuthUser, pre).
But the error is:
Warning: Invoke-Command : A positional parameter cannot be found that accepts a
gument '
Warning: dev/mobile/Core/Web'.
At line:1 char:15
Why is it complaining when the whole path is in quotes?
Update:
If I paste the following into Powershell ISE:
invoke-command "C:\group dev\mobile\Core\Web Services\wsAuthUser\mobilestability\src\wsAuthUser\obj\Debug\Package\PackageTmp\bin\install\installboot.ps1" -computername dev-mob2iis01 -argumentlist Mobile2/AuthUser, pre
It runs fine with no errors.
If I then open a Command prompt, past the same command but pre-pend with powershell and the execution policy parameters like so:
powershell.exe -ExecutionPolicy Bypass invoke-command "C:\localdev\mobile\Core\Web Services\wsAuthUser\mobilestability\src\wsAuthUser\obj\Debug\Package\PackageTmp\bin\install\installboot.ps1" -computername server01 -argumentlist Mobile2/AuthUser, pre
It fails again.
Because it is windows, not unix. So you would use backslashes for file paths, not regular slash.
Update:
You just gotta play around with the quotes to make it work through cmd
powershell.exe -ExecutionPolicy Bypass -command "invoke-command 'C:\localdev\mobile\Core\Web Services\wsAuthUser\mobilestability\src\wsAuthUser\obj\Debug\Package\PackageTmp\bin\install\installboot.ps1' -computername server01 -argumentlist Mobile2/AuthUser, pre"
Related
Execute the remote server parametrized batch file from PowerShell.
Doesn't throw an error nor executed command on remote machine.
$path = "D:\run\test-5.2.bat";
Invoke-Command -ComputerName testserver -Scriptblock { "$path" }
Script inside the bat file is msiexec with parameters, which shall execute through Command Prompt only.
Based on this msdn link, you can run a ps1 script file on remote computers. So if it is possible to "port" the content of the bat file in a ps1 it should work. Here is the msdn example:
Example 11: Run a script on all the computers listed in a text file
PS C:\> Invoke-Command -ComputerName (Get-Content Servers.txt) -FilePath C:\Scripts\Sample.ps1 -ArgumentList Process, Service
This example uses the Invoke-Command cmdlet to run the Sample.ps1 script on all of the computers listed in the Servers.txt file. The command uses the FilePath parameter to specify the script file. This command lets you run the script on the remote computers, even if the script file is not accessible to the remote computers.
When you submit the command, the content of the Sample.ps1 file is copied into a script block and the script block is run on each of the remote computers. This procedure is equivalent to using the ScriptBlock parameter to submit the contents of the script.
Hope that helps
$path is a string. PowerShell simply echoes bare strings instead of executing them, unlike CMD or bash. Use the call operator (&):
& "$path"
or Start-Process:
Start-Process cmd.exe -ArgumentList '/c', $path -NoNewWindow -Wait
to have PowerShell execute a string as a command. Since you say you're running msiexec.exe from the batch script using the latter may be required.
On top of that you have a scope issue. The variable $path inside the scriptblock is not the same as the one in the global scope. You can mitigate that via the using: scope qualifier:
Invoke-Command -Computer testserver -Scriptblock { & "$using:path" }
or by passing $path as an argument to the scriptblock:
Invoke-Command -Computer testserver -Scriptblock { & "$($args[0])" } -ArgumentList $path
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.
I tried to launch a long powershell script with the name "long name here.ps1" from command prompt. But I am also trying to ensure that it runs as an administrator command in powershell. I have all execution policies in powershell set accordingly I used the ss64 set-executionpolicy command guide for powershell to get powershell working. But I am trying to use the solution from another stackoverflow question that talks about running commands as administrator. I am running a batch script that needs to execute a powershell script (.ps1) as admin, and I don't mind if the user is prompted by UAC or for the password. I am currently using the following command:
powershell.exe -command "&{ Start-Process powershell -ArgumentList '-noprofile -file "C:\long name here.ps1"' -verb RunAs}"
I found this command at https://ss64.com/ps/powershell.html at the bottom where there are details on how to run a powershell command as administrator. The problem with that code is that my powershell script 1. has arguments, and 2. has a long name. I have tried many different iterations of this command with no success, and the ones that DON'T work are listed below:
powershell.exe -command "&{ Start-Process powershell -ArgumentList '-noprofile -file C:\long` name` here.ps1' -verb RunAs}"
powershell.exe -command "&{ Start-Process powershell -ArgumentList '-noprofile -file:"C:\long name here.ps1' -verb RunAs}"
Also, I am completely lost as to how to send arguments to the actual script.
If I'm reading your question correctly - powershell wont find the file as it stops reading the path name when it encounters a blank space?
The example given here specifies that; powershell commands to be run from command prompt as an administrator should have the following syntax:
powershell.exe -noprofile -command "&{ start-process powershell -ArgumentList '-noprofile -file MyScript.ps1' -verb RunAs}"
Couple of ways to achieve what you're looking for. But the easiest method would be to escape the quotes using a ` character. So something similar to;
powershell.exe -noprofile -command "&{ start-process powershell -ArgumentList '-noprofile -file `"C:\long file name.ps1`"' -verb RunAs}"
Also might be worth checking out other answers here
Use a Freeware Third Party Utility
If a freeware third-party executable is permissible, you can use a short tool I wrote called elevate32.exe (32-bit) and elevate64.exe (64-bit) to launch powershell.exe as administrator with the -File parameter and the script arguments you want to use:
elevate64 -- powershell.exe -File "<path>\<long script name>.ps1" -Arg "<long script argument>"
You can get the tool from www.westmesatech.com (copyrighted freeware, free to use anywhere, no installation needed).
Use a WSH Script
If you can't use an external executable, you can also do this (although it does not handle quoting in as robust a manner as the elevate tool's -- parameter) using a Windows Script Host (WSH) script, elevate.js:
var args = WScript.Arguments;
if ( args.Length >= 1 ) {
var exec = args.Item(0);
var cmdLine = "";
for (var i = 1; i < WScript.Arguments.Length; i++ ) {
cmdLine += cmdLine == "" ? '"' + args.Item(i) + '"' : ' "' + args.Item(i) + '"';
}
var shellApp = new ActiveXObject("Shell.Application");
shellApp.ShellExecute(exec, cmdLine, "", "runas");
}
You can call as follows:
wscript.exe "d:\path\elevate.js" powershell.exe -File "C:\long path\script name.ps1" "long script argument"
Self-Elevate your PowerShell Script
Another option is to write a self-elevating PowerShell script. You can check for elevation in the script; if not elevated, it can launch itself elevated and run any command you need. Example:
$isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ( -not $isElevated ) {
Start-Process powershell.exe "-File",('"{0}"' -f $MyInvocation.MyCommand.Path) -Verb RunAs
exit
}
& "d:\long path name\script name.ps1" "Long Argument 1" "Long Argument 2"
When you use PowerShell.exe -Command you don't need to use quotes. For example, you can run the following:
PowerShell.exe -Command Get-Service 'wuauserv'
Everything after -Command is interpreted as the command. Note also that double quotes in CMD need escaping with a backslash. Therefore:
powershell.exe -Command Start-Process PowerShell -ArgumentList '-NoProfile -File \"C:\long name here.ps1\"' -Verb RunAs
If your file has arguments:
powershell.exe -Command Start-Process PowerShell -ArgumentList '-NoProfile -File \"C:\long name here.ps1\" \"Arg1\" \"Arg2\"' -Verb RunAs
I can right click on the DEC16.bat file and it will run. I am having trouble including it in a script to run from a flash drive.
The PowerShell script essentially copies over a bunch of install files onto a client's computer.
Windows PowerShell
Copyright (C) 2013 Microsoft Corporation. All rights reserved.
PS H:\> $script = "\\xxxhsfmsl03\adap\Database\Install\AugKA\DEC16.bat"
PS H:\>
PS H:\> Start-Process powershell -Credential “xxx\xxxvis_desktop” -ArgumentList '-noprofile -command &{Start-Process $script -verb runas}'
Start-Process : This command cannot be run due to the error: The directory name is invalid.
At line:1 char:1
+ Start-Process powershell -Credential “xxx\xxxvis_desktop” -ArgumentList '-noprof ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOperationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.Commands.StartProcessCommand
PS H:\> $script
\\xxxhsfmsl03\adap\Database\Install\AugKA\DEC16.bat
PS H:\>
(I have inserted "xxx"'s to protect the innocent)
Try the following:
Start-Process powershell `
-WorkingDirectory (Split-Path $script) `
-Credential xxx\xxxvis_desktop `
-ArgumentList '-noprofile', '-command', "
& { start-process powershell -ArgumentList '-File', '$script' -Verb RunAs }
"
Your primary problem was most likely that the target user - xxx\xxxvis_desktop - lacked permission to access what happened to be the current directory at the time of invocation.
Setting the working directory explicitly to a directory the target user is allowed to access should fix that problem - -WorkingDirectory (Split-Path $script) sets the working dir. to the dir. in which the target script is located.
Your secondary problem - as pointed out in a comment on the question by Matt - is that the command string you passed to Start-Process was enclosed in '...' (single quotes), causing the embedded $script variable reference not to be expanded (interpolated).
Using "..." (double quotes) fixes that problem; note, however, that the command line to pass to the powershell executable is split into individual arguments passed via -ArgumentList - the (literal, single-quoted) options, followed by the (interpolated, double-quoted) command string, which is the preferable way to pass arguments, because it is more robust.
Note, how the $string reference inside the command string is enclosed in embedded '...' so as to ensure that when the invoked powershell instance parses the command string, the value of $string is recognized as a single argument (although this happens not to be necessary for the value at hand, \\xxxhsfmsl03\adap\Database\Install\AugKA\DEC16.bat).
If there's a chance that the value of $script has embedded ' instances, you must use the following (double the ' instances to escape them):
$($script -replace "'", "''")
The final problem is that you cannot use Start-Process directly on a script - as in the outer call, you need to call powershell and pass it the script filename as an argument.
Additional notes:
The & { ... } wrapper around the start-process call in the command string shouldn't be necessary.
Generally, you could use a single Start-Process call with -Verb RunAs to elevate the run of $script, but, unfortunately, -Verb RunAs and -Credential cannot be combined, so that means:
If the current user is an administrative account, the elevated session will invariably run as that user - you'll just get a yes/no prompt to confirm elevation.
Otherwise, the credentials will be prompted for, but you cannot pre-populate the username in that dialog.
Start-Process -Verb RunAs powershell -ArgumentList '-noprofile', '-File', $script
I need to start a windows service on the local computer through PS by directly running the PS script w.o the need to manually elevate the permissions. This code works for me:
Start-Process powershell -Verb runas -ArgumentList "-file MyFileName.ps1"
Where MyFileName.ps1 contains:
Start-Service MyServiceName
But I want to keep it simple and instead of storing the command into a separate file, I want to run a single script. The following does not work for me:
Start-Process powershell -Verb runas -ArgumentList "-command '& {Start-Service MyServiceName}'"
What am I missing?
Start-Process invokes a new process. The invocation doesn't recognize single quotes as quoting characters, so instead of passing a parameter -command with a command string '&{Start-Service MyServiceName}' you're passing 4 tokens: -command, '&, {Start-Service, and MyServiceName}.
Change this:
"-command '& {Start-Service MyServiceName}'"
into this:
"-command `"& {Start-Service MyServiceName}`""