How to run a CMD Through Invoke-Command - powershell

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

Related

Redirecting .bat output when using Invoke-Command

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

How do I pass a local variable to remote PowerShell Script

Let me brief out what all methods I tried.
Here is the Start_TestTalk.ps1 script
$RN = $env:RName
$TestV = "Local_Variable"
Write-Host $TestV
Write-Host $RName
Write-Host $RN
Write-Host $env:RName
I've declared the below variables
$Name="myname"
$CPU= 100
First Method:
PS C:\Users\Administrator> Invoke-Command -Session $s -Scriptblock{ & "C:\Users\Administrator\Desktop\Start_TestTalk.ps1
" -RName $args[0] -RCPU $args[1]}-argumentlist $Name,$CPU
Local_Variable
Second Method
PS C:\Users\Administrator> Invoke-Command -Session $s -Scriptblock{ & "C:\Users\Administrator\Desktop\Start_TestTalk.ps1
" -RName $using:Name -RCPU $using:CPU}
Local_Variable
Third Method
PS C:\Users\Administrator> Invoke-Command -Session $s -Scriptblock{ Param($Name, $CPU) & "C:\Users\Administrator\Desktop
\Start_TestTalk.ps1" -RName $Name -RCPU $CPU}-argumentlist $Name ,$CPU
Local_Variable
All the above three methods just prints the 'Local_Variable' which is local to the remote machine and doesn't print the variable that I pass from my local machine(here $Name).
You can use the :using variable prefix:
Invoke-Command -Session $s -Scriptblock{ & "C:\Users\Administrator\Desktop\Test.ps1" -RName $using:Name -RCPU $using:CPU}
I figured out the way on how this feature works with the help of a Senior Architect here in our company. The variables sent from the local(source machine) are not actually made local on the remote machine and these just get dumped as values on the remote machine and not as variables(you can't use variables). A simple example on how the above script which I had mentioned works
Start_TestTalk.ps1 script
$RN = $args[0]
$CPU= $args[1]
Write-Host $RN
Write-Host $CPU
Now use the same old Invoke Command with slight changes removing the variables earlier used to hold the values from local machine
Invoking the remote script using argument list
I've declared the below variables
$Name="myname"
$CPU= 100
PS C:\Users\Administrator\ Invoke-Command -Session $s -Scriptblock{ &
"C:\Users\Administrator\Desktop\Start_TestTalk.ps1" $args[0] $args[1] }-
argumentlist $Name,$CPU
myname
100
Now you see that you get the required output on the remote machine, so it conveys that the values are directly getting dumped and not with the variables and hence I was earlier not able to use those variables on the remote machine.

How to Invoke-Command and pass path to command as parameter

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,?}

Calling function from remote script block

My powershell script is as below. I try to zip a folder at remote machine. I don't want to put Zip function inside ScriptBlock because it will be used in other parts of the script.
function Zip{
param([string]$sourceFolder, [string]$targetFile)
#zipping
}
$backupScript = {
param([string]$appPath,[string]$backupFile)
If (Test-Path $backupFile){ Remove-Item $backupFile }
#do other tasks
$function:Zip $appPath $backupFile
}
Invoke-Command -ComputerName $machineName -ScriptBlock $backupScript -Args $appPath,$backupFile
In $backupScript, it is giving error in $function:Zip line:
+ $function:Zip $appPath $backupFile
+ ~~~~~~~~
Unexpected token '$appPath' in expression or statement.
You have to refer to arguments in a scriptblock like:
$backupScript = {
param([string]$appPath,[string]$backupFile)
If (Test-Path $backupFile){ Remove-Item $backupFile }
#do other tasks
$function:Zip $args[0] $args[1]
}
Invoke-Command -ComputerName $machineName -ScriptBlock $backupScript -Args $appPath,$backupFile
Also, the function will not be known by the target machine, you'll have to define it within the script-block or pass it to the machine.
Here is an example:
How do I include a locally defined function when using PowerShell's Invoke-Command for remoting?
This example puts it in your perspective:
PowerShell ScriptBlock and multiple functions
I would find some way of getting your shared functions onto your server. We have a standard share on all our servers where we deploy common code. When we run code remotely, that code can then reference and use the shared code.

Include a PowerShell file inside a ScriptBlock

I have written the following code
$sb = {
. .\Myfunctions.ps1
$x = MyFunction1
$y = MyFunction2
$x + $y
}
$cred = Get-Credential "domain\user"
Invoke-Command -Computer localhost -Credentials $cred -ScriptBlock $sb
This does not work because it says The term .\MyFunctions.ps1 is not recognized as commandlet
Why can't I include a file inside a script block?
The problem is that the $pwd (current directory) in the script block is different from the actual console path casued this because you are using invoke-command with -computer parameter is like you are do it in a remoting session. Try to put full path to your script to call it or just use ( if locally) & $sb