I'm tearing my hair out trying to invoke-command but pass the path to the exe as a parameter
eg:
I want to take this command
powershell Invoke-Command -ComputerName localhost -ScriptBlock { param($command ) C:\windows\system32\getmac.exe /$command } -ArgumentList ?
and translate it into a form like this
powershell Invoke-Command -ComputerName localhost -ScriptBlock { param($path, $command ) $path\getmac.exe /$command } -ArgumentList C:\windows\system32,?
I've tried all manner of quoting, ampersands and other contortions but can't get it to work. The above attempt results in
Unexpected token '\getmac.exe' in expression or statement.
At line:1 char:97
(I don't really want to invoke getmac on localhost, this is the runnable, SO distilled version)
Try this option. It shows me help for cscript.exe.
C:\>powershell.exe Invoke-Command -ComputerName localhost -ScriptBlock { param($path, $command ) cmd /c $path $command } -args '"C:\windows\system32\cscript.exe"','"/?"'
I tried other options using & and then path and arguments and it was giving me missing } exception. Then using cmd /c instead of & inside scriptblock fixed the issue.
Powershell won't parse a string as a command that way. For e.g. if you do this:
$path="C:\Windows\System32"
$path\getmac.exe
You would get the same error. The trick to work around this is to use the invoke operator &:
&$path\getmac.exe
or in your example, like this (also note that for a command that you pass to the powershell executable, you must wrap it in scriptblock braces):
powershell -command {Invoke-Command -ComputerName localhost -ScriptBlock { param($path, $command ) &$path\getmac.exe /$command } -ArgumentList C:\windows\system32,?}
Related
I have syntax similar to the below in PowerShell:
param($1,$2,$3,$4)
$Session = New-PSSession -ComputerName $1
$scriptblock = $ExecutionContext.InvokeCommand.NewScriptBlock("cmd /c $2 $3 $4")
Invoke-Command -Session $Session -ScriptBlock $scriptblock
Where $2 is a .bat and $3 & $4 are arguments I want to pass to the executable. If I run it without arguments I can run the .bat but when I add in the arguments it fails. What am I doing wrong?
Edit: To provide further information, I am calling the above by running this in PowerShell:
-ExecutionPolicy Bypass -F "powershellscript for the above" -1 "value" -2 "value" -3 "value" -4 "value"
To use local variables inside remote session, you must prefix them with $Using: scope modifier.
Try this:
param($1,$2,$3,$4)
$Session = New-PSSession -ComputerName $1
$scriptblock = $ExecutionContext.InvokeCommand.NewScriptBlock({ cmd /c $Using:2 $Using:3 $Using:4 })
Invoke-Command -Session $Session -ScriptBlock $scriptblock
I am trying to use Power-shells Invoke to run an MSI install.
This code is not runing the MSI install.
param ($path1, $path2, $path3)
write-output "path1= $path1"
write-output "path2= $path2"
write-output "path3= $path3"
$PathToMSI = "D:\Install\$path1\'$path2$path3'"
write-output "PathToMSI= $PathToMSI"
$scriptblock = {Start-Process msiexec.exe -Argumentlist "/i $PathToMSI","/qn"}
invoke-command -scriptblock $scriptblock
I know that my PathToMSI is correct, as this is what is displayed, but it's not executing.
path1= 20191213.3
path2= X Y Z
path3= .msi
PathToMSI= D:\Install\20191213.3\'X Y Z.msi'
If I run it hard coded it works?
Invoke-Command -ScriptBlock {
D:\install\20191213.3\'X Y Z.msi' /quiet
}
It seems that the PathToMSI is not resolving to it's value.
I have reviewed a few like Error invoking command to install a Msi through Powershell
You need to pass $PathToMSI to the ScriptBlock. You can either use -ArgumentList for this:
Invoke-Command -ArgumentList $PathToMSI -ScriptBlock {
Start-Process msiexec.exe -Argumentlist '/i', $args[0], '/qn'
}
or you can use the $using: scope if invoking on a remote computer:
Invoke-Command -ComputerName server.domain.tld -ScriptBlock {
Start-Process msiexec.exe -Argumentlist '/i', $using:PathToMSI, '/qn'
}
Invoke-Command runs your ScriptBlock in a new PowerShell session, which doesn't know of any local variables you may have declared. The $using scope will look to the parent session for variable resolution, while -ArgumentList will pass literal variables that can be referenced using the $args variable within your ScriptBlock.
$remoteinst = "\Windows\Temp\MyFolder"
$remotecomp = ComputerName
$remotesess = New-PSSession $remotecomp
$remotedir = "C:" + $remoteinst + "\Install.cmd"
Invoke-Command -Session $remotesess -ScriptBlock {$remotedir}
I'm trying to run the Install.cmd file on a remote computer. I realised I can't pass commands through Enter-PSSession but I'm struggling to solve this issue.
There's no need for creating an explicit session: you can pass the target computer name directly to Invoke-Command -ComputerName <computerName>.
Invoking a command whose name / path is stored in a variable requires &, the call operator.
The script block passed to Invoke-Command -ComputerName ... is executed remotely, so you cannot directly use local variables in it; in PSv3+, the simplest way to solve this problem is to use the using scope: $using:<localVarName>
Keeping all these points in mind, we get:
$remoteinst = "\Windows\Temp\MyFolder"
$remotecomp = ComputerName # Note: This syntax assumes that `ComputerName` is a *command*
$remotedir = "C:" + $remoteinst + "\Install.cmd"
Invoke-Command -ComputerName $remoteComp -ScriptBlock { & $using:remotedir }
Add cmd /c to the front of the path to the batch file.
I am trying to redirect the output of a .bat script to a file. The script is run on another machine.
The commented line works. The t.txt file is produced in the expected location. I cannot convince PowerShell to produce the output file when the ScriptBlock is used.
The current result is that the $sb text is printed to the PowerShell console running this script. No file is produced on SERVER2. What do I need to get the output written to the file specified in the scriptblock?
$cn = 'SERVER2'
$Logfile = "D:\DBA\Scripts\monlogs\monlog_$(Get-Date -Format 'yyyy-MM-ddTHH-mm-ss').txt"
$sb = [scriptblock]::Create("{ & cmd.exe /C D:\DBA\Scripts\mon_test_001.bat >`"$Logfile`" }")
### Invoke-Command -ComputerName $cn -ScriptBlock { & D:\DBA\Scripts\mon_test_001.bat >D:\DBA\Scripts\monlogs\t.txt 2>&1 }
Invoke-Command -ComputerName $cn -ScriptBlock $sb
EDIT
After BenH's comment, I found the following to work as expected. Note that the parameter needed to have the $ escaped.
$sb = [scriptblock]::Create("param(`$Logfile) & cmd.exe /C D:\DBA\Scripts\mon_test_001.bat >`"$Logfile`"")
Rather than class create method, maybe casting would work? Then because you're running the scriptblock on a remote machine, use the "$using:" scope on the local variable. (PSv3+ onwards)
$cn = 'SERVER2'
$Logfile = "c:\temp\$(Get-Date -Format 'yyyy-MM-ddTHH-mm-ss').txt"
[scriptblock]$sb = { & cmd.exe /C c:\temp\test.bat > "$using:Logfile" }
Invoke-Command -ComputerName $cn -ScriptBlock $sb
Otherwise for earlier versions, you will need to use a param block and -ArgumentList:
[scriptblock]$sb = {param($logpath) & cmd.exe /C c:\temp\test.bat > "$logpath" }
Invoke-Command -ComputerName $cn -ScriptBlock $sb -ArgumentList $Logfile
Okay, so I've got a bit of code which calls another PS script from within a PS script, and passes in a couple of parameters: Invoke-Command -ScriptBlock { param($script,$a1,$a2) &$script #($a1,$a2) } -ArgumentList #($scriptToRun,$p1,$p2) -ComputerName localhost -Credential $cred
The problem I'm experiencing however, is the receiving script is getting both $a1 & $a2 combined together in $args[0]. What I can't figure out as yet, is how do I split out the two array elements again?
Alternatively, how can I get them to pass correctly without the #()?
Splatting isn't required here, so simply remove the #():
Invoke-Command -ScriptBlock {
param($script,$a1,$a2)
& $script $a1 $a2
} -ArgumentList $scriptToRun,$p1,$p2 ...
The default behavior is to assign each argument from the argument list to the corresponding positional parameter in the parameter definition of the script block.
Or, if you want to use splatting, you need to do it like this:
Invoke-Command -ScriptBlock {
param($script,$params)
& $script #params
} -ArgumentList $scriptToRun,#($p1,$p2) ...