30

I'm writing a PowerShell script that will execute commands on a remote host using Invoke-Command and its -ScriptBlock parameter. For example,

function Foo {
    ...
    return "foo"
}
$rv = Invoke-Command --Credential $c --ComputerName $fqdn -ScriptBlock ${function:Foo}

This works fine. What I'd like to do now is the same thing, but call a function with local arguments. For example,

function Bar {
    param( [String] $a, [Int] $b )
    ...
    return "foo"
}
[String] $x = "abc"
[Int] $y = 123
$rv = Invoke-Command --Credential $c --ComputerName $fqdn -ScriptBlock ${function:Foo($x,$y)}

But this does not work:

Invoke-Command : Cannot validate argument on parameter 'ScriptBlock'. The argument is null. Supply a non-null argument and try the command again.

How can I use Invoke-Command with a -ScriptBlock that is a local function with arguments?

I realize that I can wrap the entire function and the parameters in a big code block, but that is not a clean way of doing it, in my opinion.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Christopher Neylan
  • 8,018
  • 3
  • 38
  • 51

4 Answers4

51

I think you want:

function Foo ( $a,$b) {
    $a
    $b
    return "foo"
}

$x = "abc"
$y= 123

Invoke-Command -Credential $c -ComputerName $fqdn -ScriptBlock ${function:Foo} -ArgumentList $x,$y
manojlds
  • 290,304
  • 63
  • 469
  • 417
  • thanks man, works perfectly! I was messing with combos of param() + -Arguments with no luck. – Christopher Neylan Dec 09 '11 at 17:21
  • took me several hours to find this solution ;-) Much better than exporting/importing sessions Thank You! – icnivad Jan 13 '12 at 16:50
  • 1
    I believe the correct format should be `-ScriptBlock {$function:Foo}` (note `$` position) – Mourndark Aug 06 '14 at 13:30
  • I tried it out and this is the only way that it works `${function:Foo}`. At least when you have the format `${function:Foo-Bar}`. – DarkLite1 Jan 30 '15 at 08:00
  • 2
    There is big difference between `{$function:Foo}` and `${function:Foo}`. The curly braces in the former mean scriptblock, and so the expression means: "A scriptblock that resolves to variable foo in function scope." The curly braces in the latter mean the the name of the variable is taken literarly and so it translates simply to: "A variable foo in function scope." This would be useful if the "foo" variable had spaces or other special charactes in name. – nohwnd Oct 15 '15 at 09:25
  • This trick does work for a standalone function. However it won't work for a function which references another local function. – RayLuo Nov 21 '16 at 19:20
  • I was hoping this same trick would work for runspace scriptblocks. It didn't work. It was worth a try. – MKANET Jul 13 '17 at 01:38
  • For anyone wondering what order the arguments need to be in for the -ArgumentList, this article does a good job at explaining it: http://duffney.io/RunLocalFunctionsRemotely . Basically, decorate your function parameters with the position attribute [Parameter(Position=0)] – HiTech Dec 07 '17 at 20:17
9

You can wrap the functions in a block and pass the block;

$a = {
  function foo{}
  foo($args)
}

$a.invoke() // Locally

$rv = Invoke-Command --Credential $c --ComputerName $fqdn -ScriptBlock $a //remotely

It's hardly elegant though.

reconbot
  • 5,138
  • 6
  • 45
  • 63
2

This also works:

function foo
{
    param([string]$hosts, [string]$commands)
    $scriptblock = $executioncontext.invokecommand.NewScriptBlock($commands)
    $hosts.split(",") |% { Invoke-Command -Credential $cred -ComputerName $_.trim() -Scriptblock $scriptblock }
}
8DH
  • 2,022
  • 23
  • 36
0
$Log = "PowerShellCore/Operational"

Invoke-Command -ComputerName Server01 -ScriptBlock {Get-WinEvent -LogName $Using:Log -MaxEvents 10}\
levymtmr
  • 1
  • 2