1

I'm using Invoke-Command, but this question can be relevant to any command using splat. I essentially want to pass two sets of variables that will be useful in the splat command, but I'm not sure how I can do this.

In the code below, the Invoke-Command successfully connects to both servers, but the output I get is "Server1 Info" from both servers, which makes sense since the code is reading it like I'm trying to pass two arguments to both servers and it is taking what is in the first argument and writing it to host. What I really want it to do though is only pass one argument each time and to move down the list of which argument is being passed as it connects to successive servers.

$ServerList = "Server1","Server2"
$ServerArgs = "Server1 Info","Server2 Info"

$SB = {
    param($Arg1)
    Write-Host $Arg1
}

$SplatInfo = @{
    ComputerName = $ServerList
    ArgumentList = $ServerArgs
}

Invoke-Command @SplatInfo -ScriptBlock $SB
Krenztor
  • 11
  • 2
  • It makes plenty of sense. The argumentlist isn't specific to either computer, it's towards both. You will have you use a loop against that list – Abraham Zinala Aug 24 '22 at 13:10
  • You could have the servers perform an action based on their hostname and still invoke in parallel using a hash table but that doesnt involve splatting – Santiago Squarzon Aug 24 '22 at 13:20

2 Answers2

1

You can only send one set of arguments per invocation of Invoke-Command.

If you want to pass different arguments per remote machine, you need to call Invoke-Command once per server:

$ServerList = "Server1","Server2"
$ServerArgs = "Server1 Info","Server2 Info"

$SB = {
    param($Arg1)
    Write-Host $Arg1
}

for($i = 0; $i -lt $ServerList.Count; $i++){

    $SplatInfo = @{
        ComputerName = $ServerList[$i]
        ArgumentList = $ServerArgs[$i]
    }

    Invoke-Command @SplatInfo -ScriptBlock $SB
}

... in which case you might want to organize the input data slightly differently:

$inputList = @(
  @{ ComputerName = "Server1"; ArgumentList = @("Server1 Info") }
  @{ ComputerName = "Server2"; ArgumentList = @("Server2 Info") }
)

$baseSplat = @{
  ScriptBlock = {
    param($Arg1)
    Write-Host $Arg1
  }
}

foreach($entry in $inputList){
  Invoke-Command @baseSplat @entry
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks. In that case though, I'd be best served to avoid using splat at all and just do Invoke-Command -ComputerName $ServerList[$i] -ArgumentList $ServerArgs[$i] -ScriptBlock $SB I was hoping to make the code cleaner by using Splat but sounds like that isn't possible. Thanks for that clarification – Krenztor Aug 24 '22 at 13:16
  • @Krenztor depends entirely how you format your input data (see updated example in my answer) – Mathias R. Jessen Aug 24 '22 at 13:21
  • I've fought with this all day. As soon as foreach gets added, it completely kills the efficiency of the script. It only really works if I can run it as Invoke-Command @SplatInfo -ScriptBlock $SB without a foreach anywhere. Thanks for your help though – Krenztor Aug 24 '22 at 18:07
  • @Krenztor If it makes a huge difference then you can probably also reduce the wallclock time again by using `-AsJob` - it'll start a background job and then continue immediately instead of blocking until the remote session ends, so you'll basically get the same result as when you were passing all the server names at once – Mathias R. Jessen Aug 24 '22 at 20:04
0

Using the hashtable idea. This should run in parallel still.

import-csv file.csv | 
  % { $ServerArgs = @{} } { $ServerArgs[$_.server] = $_.args }
$ServerList = $ServerArgs | % keys
$SB = {
    param($Arg1)
    [pscustomobject]@{result = $Arg1[$env:computername]}
}
$SplatInfo = @{
    ComputerName = $ServerList
    ArgumentList = $ServerArgs
    ScriptBlock = $SB
}
Invoke-Command @SplatInfo


result       PSComputerName RunspaceId
------       -------------- ----------
Server1 Info Server1        gacbbb30-2492-41df-b181-9ebf6395b8b6
Server2 Info Server2        ebca4b52-c349-4ff7-972e-e67d07d9c0c3
js2010
  • 23,033
  • 6
  • 64
  • 66
  • Appreciate that. I even considered doing something like this, but the array of args I need to pass is for about 1000 servers. It would be too much passing that along to each of them and having them find their own arguments. This would work for a small set of systems though. – Krenztor Aug 24 '22 at 18:06