2

I have a function that takes a single string array as a parameter in my PowerShell .pm1 that I want to be able to call on a remote server using a second function in my .pm1 (I do not want to rely on the server having a copy of the function). I found this Using Invoke-Command -ScriptBlock on a function with arguments but it only seems to work for 'non-arrays' or for multiple parameters (where array variable is not last)

function Hello_Worlds { param([string[]]$persons)
    foreach($person in $persons){
        write-host ("hello "+$person)
    }
}

$people = "bob","joe"
Invoke-Command -ComputerName "s1" -ScriptBlock ${function:Hello_Worlds} -ArgumentList $people
#output => "hello bob" only

Invoke-Command -ComputerName "s1" -ScriptBlock ${function:Hello_Worlds} -ArgumentList $people, ""
#output => "hello bob hello joe"

I can modify my argument list like -ArgumentList $people, "" (above) to make it work by forcing the function to see the $persons variable as a single parameter and not an array of parameters, but that seems like bad practice and I sure that I am just missing something simple.

EDIT: I was directed here ArgumentList parameter in Invoke-Command don't send all array and while it works for this exact example, it requires that I KNOW which parameters require an array. Is there a generic way to pass an any arguments that would prevent this issue? I.E. I build my argument list as an array of parameters and there could be 0 or more of them and any number of them could be arrays - or am I stuck with putting this in front of calls?

foreach($parg in $myCustomGeneratedArguments) {
   if($parg -is [array]) {$paramArgs += ,$parg} 
   else {$paramArgs += $parg} 
}
4
  • 1
    Pass the argument like this -ArgumentList (, $people) Commented Jan 20, 2023 at 15:53
  • Same purpose to this^, use it in your assignment: $people = ,@("bob","joe") Commented Jan 20, 2023 at 15:55
  • Isn't using the ``` , ``` in front the same as using it in the back? This seems as kludgy as ,"" or am I missing something? Commented Jan 20, 2023 at 16:57
  • Pass the function to the remote host then you can use it inside the scriptblock without any problems. See this answer stackoverflow.com/a/61273544/15339544. Even tho the answer is for ForEach-Object -Parallel it will work the same for Invoke-Command Commented Jan 20, 2023 at 18:13

2 Answers 2

1

Looking at your edit I'm afraid the linked answer doesn't lead you to the easier path, which is to not use -ArgumentList at all, instead, refer to your Hello_Worlds function and to your $people array with the $using: scope modifier:

function Hello_Worlds { param([string[]]$persons)
    foreach($person in $persons){
        write-host ("hello "+$person)
    }
}

# store the function definition locally
$func   = ${function:Hello_Worlds}.ToString()
$people = "bob","joe"

Invoke-Command -ComputerName "s1" -ScriptBlock {
    # define the function in the remote scope
    ${function:Hello_Worlds} = $using:func
    # now you can use it normally
    Hello_Worlds -persons $using:people
}
Sign up to request clarification or add additional context in comments.

Comments

0

I sure that I am just missing something simple.

Unfortunately, you're not (but Santiago's helpful answer offers an approach that bypasses the problem).

The bottom line is this:

  • If you're passing two or more - individually specified - arguments to -ArgumentList, everything works as expected, such as -ArgumentList $people, "" in your example.

    • The reason is that the use of , implicitly creates an enclosing array whose elements are the now nested $people array, and the empty string ("").
  • If you're passing just one argument and that one argument happens to be an array (or similar list type) that must be passed as a whole, as a single argument, you need the -ArgumentList (, $arrayOrList) workaround, i.e. you need to explicitly create an enclosing array, using the unary form of , the array constructor operator, which signals to -ArgumentList that $arrayOrList doesn't represent individual arguments, but a single argument that happens to be array-like.
    That is, you're then passing a single-element array whose only element happens to be array-like itself.

Therefore, you invariably do need to know ahead of time if a given array represents individual arguments or is a single, array-valued argument.

You can assemble an array of all arguments ahead of time in a single array variable, whose elements may or may not be nested arrays that should be passed as a whole, but there too you need know which elements are themselves array-like and therefore require the use of , :

$people = "bob", "joe"

# Assembly the list of *all* arguments in a single array variable.
$myArguments = @(
  # Add the $people array as an element, which 
  # requires the unary form of "," *here*.
  , $people
  # Add more elements as situationally needed.
  # ...  
)

# Now you can use $myArguments as-is.
Invoke-Command -ScriptBlock $function:Hello_Worlds -ArgumentList $myArguments

If you're given a value that you know represents a single argument to pass on, it is safe to always use -ArgumentList (, $someSingleArgument), whether that single argument happens to be array-like or not.

# Assume that $mySingleArgument is a value representing a *single*
# argument, which may or may not be array-like.

Invoke-Command $function:Hello_Worlds -ArgumentList (, $mySingleArgument)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.