Line breaks invoking PowerShell from MSBuild - powershell

I'm trying to call a PowerShell script from my MSBuild script. When I collapse the following into a single line, it runs, but when I leave it like this, I get the error below. In case it matters, I'm kicking off the MSBuild script from a BAT script.
<Exec Command="powershell.exe -NonInteractive -ExecutionPolicy Unrestricted
-Command "& Invoke-Command
-ComputerName &apos;$(Server)&apos;
-ScriptBlock {
&install.ps1
-serviceName &apos;$(ServiceName)&apos;
-exePath &apos;$(ExePath)&apos;
-computerName &apos;$(Server)&apos;
} "
" />
'-Command' is not recognized as an internal or external command,
operable program or batch file.
'-ComputerName' is not recognized as an internal or external command,
operable program or batch file.
'-ScriptBlock' is not recognized as an internal or external command,
operable program or batch file.
& was unexpected at this time.
I copied the XML from Microsoft documentation as a starting point, I checked that all whitespace are plain space characters, I tried adding a space to the end of each line, I tried making sure there was only a single space at the beginning of each line with none at the end, and I tried both Windows and Unix line endings. None of those made any difference, so I'm not sure what could be happening.
After some feedback, I've tried adding ` and ^ to the end of each line except the one with />, and neither allowed the script to run.

Thanks to #PetSerAl's comments, I was able to fix it like so, by adding ^ at the end of each line, and before each double-quot and ampersand:
<Exec Command="powershell.exe -NonInteractive -ExecutionPolicy Unrestricted ^
-Command ^"^& Invoke-Command
-ComputerName &apos;$(Server)&apos; ^
-ScriptBlock { ^
^&install.ps1 ^
-serviceName &apos;$(ServiceName)&apos; ^
-exePath &apos;$(ExePath)&apos; ^
-computerName &apos;$(Server)&apos; ^
} ^"
" />

Related

Powershell / cmd - Redirecting embedded script's output streams to file

I have a situation in which a cmd script must launch a powershell script (install.ps1), elevating to admin if the cmd is not already. The line that launches the powershell looks like this:
powershell -WindowStyle Hidden "Start-Process powershell \"-NoP -Exec Bypass -File `\"%~dp0install.ps1`\" %args%\" -Verb runAs -Wait"
Or this also works:
powershell -WindowStyle Hidden "Start-Process powershell \"-NoP -Exec Bypass invoke-command { %~dp0install.ps1 %args% } \" -Verb runAs -Wait"
I would like to redirect the output from the install.ps1 script to a file for logging purposes, but having trouble doing this. Something like the following will generate the log.txt file, but output will still be shown in the console and the resulting log.txt file will be empty:
powershell -WindowStyle Hidden "Start-Process powershell \"-NoP -Exec Bypass invoke-command { %~dp0install.ps1 %args% } \" *> log.txt -Verb runAs -Wait"
Moving the *> log.txt portion to inside the Start-Process block (just after the invoke-command block), which I thought would be the key, seems to not even run the script at all (or it's flashing an error in the console too quick to see because it closes immediately).
Is it possible to achieve this logging behavior when the data I want is buried in a couple layers of powershell, executed by a cmd file?
We've technically gotten this to work by creating a powershell wrapper script that is called/elevated by the cmd, then within the wrapper calling the install.ps1 script and assigning logging in that call. Unfortunately the extra script layer causes a bunch of other tricky / more critical problems regarding getting arguments passed at the command line all the way through to the actual install script correctly, so we're really trying to avoid that route.
EDIT
Thanks to #mklement0 for the pointer that the redirect needed to be escaped, which was my problem. Follow-up question - The following command works great to log to file, but is there any way to get this same behavior using -File rather than -Command when invoking the PS script ("-Command %~dp0pg.ps1")?
powershell -Command "Start-Process -WindowStyle Hidden -Verb RunAs -Wait powershell \"-NoProfile -ExecutionPolicy Bypass -Command %~dp0pg.ps1 *^> %CD%\log.txt\""
Moving the *>log.txt redirection into the Invoke-Command block works in principle, but your problem is that in Windows PowerShell (as opposed to PowerShell Core) a process invoked with elevation (as admin), via -Verb RunAs, defaults to C:\Windows\System32 as the working directory, not the caller's working dir.
Aside from the fact that you probably didn't mean to create a log file in C:\Windows\System32, the command will fail, because writing to that location requires the caller to already be elevated.
The simplest solution is to make *> redirect to a file specified with a full path instead:
powershell -Command "Start-Process -WindowStyle Hidden -Verb RunAs -Wait powershell \"-NoProfile -ExecutionPolicy Bypass -Command %~dp0pg.ps1 *^> %CD%\log.txt\""
Note:
There is no need for Invoke-Command - just invoke the *.ps1 file directly; however, I've added -Command to make it more obvious that the remainder of the command line is to be interpreted as PowerShell code (not a script-file path with arguments only).
Because > is a cmd.exe metacharacter, it must be escaped as ^> in order to be passed through to PowerShell - perhaps surprisingly, cmd.exe considers the > to be unquoted, because it doesn't recognize the \" sequences as embedded double quotes - only PowerShell does.
As in your original command, the assumption is that neither %~dp0 - the batch file's folder dir. path - nor %CD% - the caller's working dir. path - contain spaces or other special chars. that would need additional quoting / escaping.

Powershell script cannot recognize absolute file path

I have a windows command script that needs to run another powershell script in another location. The windows command script contains the following code:
powershell -NoProfile -Command "Set-ExecutionPolicy Bypass -Scope Process -Force; & "C:\Users\Tommy\AppData\bootstrap\bootstrap.ps1" -Update"
When I run the windows command script, it always show the following error:
& : The term 'C:\Users\Tommy' 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:53
I have tried adding dot to the front of the absolute file path and it did not work.
Try updating "C:\Users\Tommy\AppData\bootstrap\bootstrap.ps1" to 'C:\Users\Tommy\AppData\bootstrap\bootstrap.ps1'. You are ending your quotes early by using the double quotes again.
powershell -NoProfile -Command "Set-ExecutionPolicy Bypass -Scope Process -Force; & 'C:\Users\Tommy\AppData\bootstrap\bootstrap.ps1' -Update"

Powershell opening file path with whitespaces

I'm my PS script I want to be able to run another script in another PS instance by doing the following:
$filepath = Resolve-Path "destruct.ps1"
start-process powershell.exe "$filepath"
destruct.ps1 is in the same folder as this script.
However when running this script in a location which includes spaces ("C:\My Scripts\") I will get the following error:
The term 'C:\My' is not recognized as a cmdlet, function, operable program, or script file. Verify the term and try again.
I know by using a '&' with the Invoke-Expression method solves this problem, how can I do the same but by using the start-process method?
try this:
start-process -FilePath powershell.exe -ArgumentList "-file `"$filepath`""
edit after comments:
start-process -FilePath powershell.exe -ArgumentList "-file `"$($filepath.path)`""
side note:
$filepath is a [pathinfo] type and not a [string] type.
You can add escaped double quotes so that you pass a quoted argument:
"`"$filepath`""
I am answering here for a general scenario.
If you need to navigate to a folder for example C:\Program Files from the Powerhsell, the following command won't work as it has white space in between the path.
cd C:\Program Files
Instead embed the path with double quotes as like the below.
cd "C:\Program Files"
File name might contain spaces, so preserve spaces in full path:
Notepad++ exec command:
"C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" "& \"$(FULL_CURRENT_PATH)\""
same from command prompt:
"C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe" "& \"C:\a_work\Systems\name with spaces.ps1\""
Just in case [string]$shipno (which is path & file name) comes in including spaces the following allows it to be passed to -FilePath successfully:
if ($shipno.contains(" ") -eq $true) {
$shipno = """" + $shipno + """"
}

powershell web.deploy.cmd positional parameter

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"

Run PowerShell command from command prompt (no ps1 script)

I'm looking for a way to run just a couple PowerShell commands from the command prompt. I don't want to create a script for this since it's just a couple commands I need to run and since I don't really know how to script with PowerShell.
Here is the command I'm trying to use to start with:
Get-AppLockerFileInformation -Directory <folderpath> -Recurse -FileType <type>
I don't really want to create a script for this as it would be much easier if I can just run one or two commands from a batch file with the rest of the stuff.
EDIT:
Here is what I've tried so far.
1)
powershell -Command "Get-AppLockerFileInformation....."
Error: The term 'Get-AppLockerFileInformation is not recognized as the name of a cmdlet, function, script file, or operable program....
2)
powershell -Command {Get-AppLockerFileInformation.....}
No error with this way but I don't get anything back. If I use the Set-AppLockerPolicy... nothing happens.
3)
powershell -Command "{Get-AppLockerFileInformation.....}"
Error: The term 'Get-AppLockerFileInformation is not recognized as the name of a cmdlet, function, script file, or operable program....
4)
powershell -Command "& {Get-AppLockerFileInformation.....}"
Error: The term 'Get-AppLockerFileInformation is not recognized as the name of a cmdlet, function, script file, or operable program....
5)
powershell "& {Get-AppLockerFileInformation.....}"
Error: The term 'Get-AppLockerFileInformation is not recognized as the name of a cmdlet, function, script file, or operable program....
6)
powershell -ExecutionPolicy Bypass -NoLogo -NoProfile -Command {Get-AppLockerFileInformation....}
No error but nothing happens.
7)
powershell -ExecutionPolicy Bypass -NoLogo -NoProfile -Command "Get-AppLockerFileInformation...."
No error but nothing happens.
Here is the only answer that managed to work for my problem, got it figured out with the help of this webpage (nice reference).
powershell -command "& {&'some-command' someParam}"
Also, here is a neat way to do multiple commands:
powershell -command "& {&'some-command' someParam}"; "& {&'some-command' -SpecificArg someParam}"
For example, this is how I ran my 2 commands:
powershell -command "& {&'Import-Module' AppLocker}"; "& {&'Set-AppLockerPolicy' -XmlPolicy myXmlFilePath.xml}"
Run it on a single command line like so:
powershell.exe -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile
-WindowStyle Hidden -Command "Get-AppLockerFileInformation -Directory <folderpath>
-Recurse -FileType <type>"
Maybe powershell -Command "Get-AppLockerFileInformation....."
Take a look at powershell /?
This works from my Windows 10's cmd.exe prompt
powershell -ExecutionPolicy Bypass -Command "Import-Module C:\Users\william\ps1\TravelBook; Get-TravelBook Hawaii"
This example shows
how to chain multiple commands
how to import module with module path
how to run a function defined in the module
No need for those fancy "&".