Search code examples
powershellpowershell-remotinginvoke-command

How can I pass multi args with Invoke-Command


I want create a few shortcuts on my desktop, it works locally. But when I try it on a remote PC I got only one shortcut for the first target (path1), and script ignores path2 variable.

$Servers = Get-Content D:\1.txt

function add-sc {
    param ([string[]]$Targets) 
    BEGIN {}
    PROCESS {
        foreach ($a in $Targets) {
            $WshShell = New-Object -comObject WScript.Shell
            $b = $a.Substring($a.length - 5)
            $Shortcut = $WshShell.CreateShortcut("$Home\Desktop\$b.lnk")
            $Shortcut.TargetPath = $a
            $Shortcut.Save()
        }
    }
    END {}
}

foreach ($server in $Servers) {
    Invoke-Command -ComputerName $server -ScriptBlock ${function:add-sc} -Args "path1", "path2"
}

Solution

  • For one thing, you should define your function inside the scriptblock. I'm not sure about PowerShell v5+, but in PowerShell v4 and earlier a command

    Invoke-Command -Computer bar -Scriptblock {function:foo}
    

    would throw an error because the function isn't recognized inside the scriptblock.

    Also, you need to actually pass your parameters to the function you're invoking. There are basically 2 ways how you could handle that:

    • Pass the arguments to the function via the automatic variable $args:

      Invoke-Command -Computer $server -ScriptBlock {
          function add-sc {
              Param([string[]]$Targets)
              Process {
                  ...
              }
          }
          add-sc $args
      } -ArgumentList 'path1', 'path2'
      
    • Pass the arguments to the scriptblock as a single array and splat them on the function:

      Invoke-Command -Computer $server -ScriptBlock {
          function add-sc {
              Param([string[]]$Targets)
              Process {
                  ...
              }
          }
          add-sc @args
      } -ArgumentList @('path1', 'path2')
      

    The difference between the 2 approaches is that the former takes all arguments and passes them as a single array to the function whereas the latter takes all arguments to the scriptblock and passes them as individual arguments to the function. Hence the need to pass the arguments to the scriptblock as a single array in the latter case.

    In your scenario both approaches are equivalent, but if your function expected a second parameter besides the array you'd need the second approach and pass the arguments to the scriptblock as -ArgumentList @('a','b'), 'c'.