Variable Substitution inside a PowerShell Scriptblock run as Administrator - powershell

I'm writing code in PowerShell. I want to change the port in a configuration file as administrator, but it doesn't work.
Here's my code:
$port=8888
$ScriptBlock = {
function bar($port) {
$config = Get-Content -Path "c:\Users\Me\foo.xml"
$NewConfig = $Config -replace 'httpPort=[0-9]*\s',"httpPort=$port "
Set-Content -Path c:\ProgramFiles(x86)\foo.xml -Value $NewConfig -Force
}
};
Start-Process -FilePath powershell.exe -ArgumentList $ScriptBlock,"bar('$port')" -Verb RunAs -Wait
I think its having trouble with the second part of the -replace ("httpPort=$port ").
What am I missing?

You can try the following:
Start-Process -Verb RunAs -Wait -FilePath powershell.exe -ArgumentList '-Command',
"$($ScriptBlock -replace '"', '\"'); bar $port"
As an aside:
* The regex you're using to match the port number suggests that it is an unquoted attribute value, which, while acceptable in HTML, is not well-formed XML.
* Don't invoke your function with method syntax (bar($port)) - PowerShell functions are called like shell commands, without parentheses and with arguments separated by whitespace rather than ,: bar $port
As for what you tried:
Start-Process invariably interprets the arguments passed to -ArgumentList as strings, and while a script block conveniently stringifies to its literal string content, you need to escape the embedded " chars. as \" (sic) in order for the target PowerShell instance to recognize them properly with the (implied in Windows PowerShell) -Command option.
Since -Command simply concatenates all subsequent arguments with a space as the separator before interpreting the resulting string as a piece of PowerShell source code, it is generally preferable to pass a single string, for conceptual clarity.

Related

Start cmd from powershell with multiple arguments

Currently, I am able to call cmd from powershell using the following command:
Start-Process cmd -ArgumentList '/K "ping 192.168.1.1"'
What I am trying to do is add multiple arguments to pass onto the command, but it is not working. For example:
Start-Process cmd -ArgumentList '/K "title test" /K "ping 192.168.1.1"'
Is there a way to do this?
Edit: My goal is to have a cmd window open, pinging the address listed, but also to pass the "title" argument so the window is titled.
Since you're calling cmd.exe, use its statement-sequencing operator, &, to pass multiple commands to cmd /K:
Start-Process cmd -ArgumentList '/K title test & ping 192.168.1.1'
Note:
Start-Process's -ArgumentList (-Args) parameter technically accepts an array of arguments. While passing pass-through arguments individually may be conceptually preferable, a long-standing bug unfortunately makes it better to encode all arguments in a single string - see this answer.
cmd.exe's built-in title command inexplicably includes double quotes in the title if you enclose the argument in "..."; thus, if you want to specify a title that contains spaces, leave it unquoted and escape metacharacters other than spaces with ^; e.g., to pass test & more as the title, use title test ^& more
start-process is sometimes tricky... try to add the elements as strings to an array and pass it over to start-process.
But in your case... idk what that /K should do, but in case of ping - ping is the process to start, not cmd ;-)
start-process ping.exe -argumentlist "127.0.0.1"
start-process ping.exe -argumentlist "127.0.0.1 /t"
as you are already using PowerShell
test-connection 127.0.0.1
Here is an example where I did something simliar:
$cmdArray = #(
If ($token){
"-c"
"`"http.extraHeader=Authorization: Bearer $token`""
}
If ($clone){
'clone'
}
If ($fetch){
'fetch'
'-f'
'origin'
If ($tag){
"tags/$($tag):tags/$($tag)"
}
}
Else {
"`"$uri`""
}
If ($whatif){
'--dry-run'
}
)
$result = Start-Process $pathGitexe -ArgumentList $cmdArray -Wait -NoNewWindow -PassThru
ok, based on your comment you need this:
$ips = #("192.168.1.1","192.168.1.2")
$ips | %{
start-process ping.exe -ArgumentList $_
}

Start-Process inside start-process as different user

I'm trying to pass arguments from an installshield setup file, what am I doing wrong ?
$STExecute = "C:\Files\setup.exe"
$STArgument = '/s /f2"c:\windows\setuplogs\inst.log"'
Start-Process Powershell.exe -Credential "Domain\userTempAdmin" `
-ArgumentList "-noprofile -command &{Start-Process $($STExecute) $($STArgument) -verb runas}"
I get the blow error, as you can see it removes the double quotes, that needs to be there, I cant even get it to pass the /s argument in the 2nd start-process:
Start-Process : A positional parameter cannot be found that accepts argument '/f2c:\windows\setuplogs\dmap110_inst.log'
This is happening because the inner instance is seeing /s and /f2"c:\windows\setuplogs\inst.log" as two separate positional parameters. You need to wrap the arguments for the inner Start-Process with quotes. I'd also suggest using splatting to make it easier to understand what is happening:
$STExecute = "C:\Files\setup.exe"
$STArgument = '/s /f2"c:\windows\setuplogs\inst.log"'
$SPArgs = #{
FilePath = 'powershell.exe'
ArgumentList = "-noprofile -command Start-Process '{0}' '{1}' -Verb runas" -f
$STExecute, $STArgument
}
Start-Process #SPArgs
I have also used the format operator here, as it allows us to inject values without using subexpressions. As long as there are no single quotes in $STArgument, or you escape them properly (four quotes '''' per quote in this case), it should work for you.

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...

Problem using a for loop and start-process to call powershell script

Could someone please help me on this. I have created two powershell script one is suppose to call the other one lets call it script2.ps1
script2.ps1 - accepts two arguments computername and the type of remediation
example:
list=get-content c:\computer.txt
foreach ($pc in $list){
start-process powershell.exe -ArgumentList "-noexit", "-file `C:\temp\client fix\script2.ps1`", "-type install", "-computer $i"
}
The intention is for each computer in the list to execute script2.ps1 on separate process. The script runs fine if start-process is not being used example:
powershell.exe -file 'C:\temp\client fix\Script2.ps1' -type install -computer $i
The Start-Process help explains:
Specifies parameters or parameter values to use when this cmdlet starts the process. If parameters or parameter values contain a space, they need surrounded with escaped double quotes.
Your file paramater does not have an escaped double quote, but a backtick that is doing nothing.
start-process powershell.exe -ArgumentList "-noexit", "-file `"C:\temp\client fix\script2.ps1`"", "-type install", "-computer $i"
Furthermore you mix arguments for powershell.exe (-noexit, -file) with arugments for your script (-type, -computer). Also your variable $i is never assigned.
Anyways, more important is to know, that there is no reason for start-process. Simplify your script by using the call operator &.
$list=get-content c:\computer.txt
foreach ($pc in $list){
& "C:\temp\client fix\script2.ps1" -type install -computer $pc
}

Start-Process and the Stop Parsing (--%) parameter

I am having trouble getting the --% parameter to work as expected. My $TaskParams variable has the character '<' which is interpreted as a redirection by powershell, therefore needs to be escaped.
However the following does not work:
$CreateTask = Start-Process PowerShell.exe "$ScriptLocation --% $TaskParams" -Wait -PassThru
Without the --%, and when I manually remove any '<' characters, it works:
$CreateTask = Start-Process PowerShell.exe "$ScriptLocation $TaskParams" -Wait -PassThru
error received:
Start-Process : A positional parameter cannot be found that accepts argument
'--%'.
note: I am using PS 5.0
Am I using the --% parameter wrong? Any help or pointers is appreciated. Thanks
The stop-parsing symbol --% only works when calling executables directly or with the call operator &; it's not for use when calling PowerShell scripts / functions / cmdlets.
You do not need to spin up a new copy of powershell.exe or use Start-Process to run a script from within another script. Just put the script command and its parameters as a line from within the other script. For example, suppose you have script2.ps1:
param(
[String] $Name
)
Write-Host "Hello, $Name"
Now suppose you also have script1.ps1:
Write-Host "This is script1.ps1"
.\Script2.ps1 -Name "Bill Stewart"
Write-Host "script1.ps1 is finished"
If you now run script1.ps1:
PS C:\> .\Script1.ps1
This is script1.ps1
Hello, Bill Stewart
script1.ps1 is finished
If you really want to use Start-Process you could encode the argument, and run it as such. I use something similar to this when elevating past UAC:
$Code = ". '$ScriptLocation' $TaskParams"
$Encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($code))
Start-Process PowerShell.exe -ArgumentList "-EncodedCommand",$Encoded -Wait -PassThru
I'm fairly certain that would accomplish what you're looking for.