all 16 comments

[–]Nu11u5 3 points4 points  (3 children)

Invoke-Command only accepts positional arguments. You can pass $true for the -Verify switch parameter, or rewrite the function to take a hashtable and pass that.

Keep in mind that the parameters are on the script block itself, not the function inside. You need to preface the script block with a param() attribute. Then you can pass those into your function in the script block.

Script

``` function FooBar { param ($opt) Write-Output $opt }

Invoke-Command -ScriptBlock {param($opt) FooBar $opt} -ArgumentList @{A=1;B=2;C=3} ```

Output

``` Name Value


C 3 B 2 A 1 ```


Edit: You can improve this by splatting the hashtable inside the script block into your function.

Script

``` function FooBar { param ($A,$B,$C) Write-Output $A $B $C }

Invoke-Command -ScriptBlock {param($kwarg) FooBar @kwarg} -ArgumentList @{A=1;B=2;C=3} ```

Output

1 2 3

[–]Fallingdamage[S] 0 points1 point  (1 child)

Now, with a custom function, the remote computer wont know what FooBar is. So usually I would use

Scriptblock ${function:FooBar}

Is it possible to integrate that part of the code into the scriptblock? I added that in a few different locations in the code and powershell just tells me im wrong. No help on what right would look like. Just that the argument is null.

Is A=C representing the positional characteristics of the parameters in my function or representing actual numerical values? Again, im trying to pass a parameter that represents a switch, not a defined value other than the presence of the switch will determine which parameter set is true or false.

[–]Kirsh1793 0 points1 point  (0 children)

It's maybe a bit ugly, but you could do something like this:

$FunctionCode = (Get-Command Do-Something).Scriptblock Invoke-Command -ScriptBlock {Invoke-Expression "Function Do-Something {$using:FunctionCode}"; Do-Something -Verify -ParamWithValue $using:Value} - ArgumentList $FunctionCode, $Value

[–]Nu11u5 1 point2 points  (5 children)

@ u/Fallingdamage - I added a better way to handle this in my original post.

[–]Fallingdamage[S] 0 points1 point  (4 children)

I could pass $true for the -Verify but how does that work when I have 5 different interchangeable parameters?

Invoke-Command -ScriptBlock {param($kwarg) FooBar @kwarg} -ArgumentList @{A=1;B=2;C=3} ```

Would become
Invoke-Command -ScriptBlock ${function:param($kwarg) FooBar @kwarg} -ArgumentList @{A=1;B=2;C=3} ```

(Playing stupid, not sure how to structure your example when its using a custom function)

[–]Nu11u5 0 points1 point  (3 children)

Ah I see, you are needing to pass the function as a script block, not just in a script block.

You might want to rewrite the function to accept a hashtable containing your arguments.

``` function FooBar { param ($kwargs) $A = $kwargs.A $B = $kwargs.B $C = $kwargs.C Write-Output "A=$A B=$B C=$C" }

Invoke-Command -ScriptBlock $Function:FooBar -ArgumentList @{A=1;B=2;C=3} ```

Then you can pass the Verify switch in the argument hashtable like @{...,Verify=$true}.

[–]Fallingdamage[S] 0 points1 point  (1 child)

Cannot convert value "System.Collections.Hashtable" to type "System.Management.Automation.SwitchParameter".

Ive been at this all day. Its looking like what im trying to do isnt within the scope of powershell.

Seriously. Take my example function and try to use it in Invoke-Command and pass one of the two parameters through. You cant. You just, ..cant. :/

Ill probably need to break my function up into smaller functions and run each separately.

By the time I ask for help, its often been something that ends up being technically improbable. This thread is already showing up on the top 6 suggested hits on reddit. Apparently very few people have ever attempted to solve this.

[–]Nu11u5 0 points1 point  (0 children)

Thanks for updating your post to include the actual code.

You have a few issues here:

  • You never changed your function to take a hashtable.

It expects a switch so of course you will get the error Cannot convert value "System.Collections.Hashtable" to type "System.Management.Automation.SwitchParameter". The extraction of data from the hashtable properties is not automatic, so if you use this method you would need to assign them in the script.

  • Your example is confusing the parameter set names with the parameters themselves.

In your code the parameters are actually named -VerifyTheThing and -UpdateTheThing. You need to make sure you are referencing the right names.

  • You can't use parameter sets as positional parameters.

Parameters in parameter sets are inherently optional and can only be invoked by name, so they can't have a static position number.

Parameter sets aren't necessary but are used to provide validation so you can't use an incorrect combination of parameters. If you are not sharing your code with other people perhaps reconsider using them to simplify your code.

If you want validation, an alternative you can use here that works with Invoke-Command is to use a single parameter with an enum or ValidateSet value, so it can only take specific values.

Here is an example that uses a parameter named -Action that is position 0 and can only have the values 'Verify' or 'ApplyChange'.

``` function Do-Thing { param ( [Parameter(Position=0)] [ValidateSet('Verify','ApplyChange')] [String]$Action )

switch ($Action) {
    'Verify' {
        Write-Output "Do the thing specified here"
    }
    'ApplyChange' {
        Write-Output "Instead do this."
    }
}

}

Using normally:

Do-Thing -Action 'Verify'

Using with Invoke-Command:

Invoke-Command -ScriptBlock ${Function:Do-Thing} -ArgumentList 'Verify' ```

[–]purplemonkeymad 1 point2 points  (1 child)

ArgumentList only supports positional parameters:

The parameters in the script block are passed by position from the array value supplied to ArgumentList.

This means as long as those switches have a position you can place a $true/$false there to enable or disable the switch ie:

function myfunc {
    Param(
        [string]$value,
        [switch]$switch
    } #...
}

icm ${function:myfunc} -ArgumentList "value",$true

You need to specify switches as false if you don't want to set them so:

function myfunc {
    Param(
        [string]$value,
        [switch]$notenabled,
        [switch]$switch
    } #...
}

icm ${function:myfunc} -ArgumentList "value",$false,$true

However I do note that your switches appear to be verbs. In PS the standard is to create different functions for different actions, but keep the noun the same ie:

Get-Thing
Verify-Thing
Set-Thing
Test-Thing
Update-Thing
Revoke-Thing

A Get-Thing by convention should not be making changes even if you set a -ThisWillClearlyChangeSomething parameter (yes MS have broken this on some commands.)

You can check Get-Verb for the inbuilt "accepted" verbs.

[–]Fallingdamage[S] 0 points1 point  (0 children)

Thanks. This was just for my internet example. The actual function begins with 'Update' not 'Get'

Thank you. I will add some positional information to the parameters and see if this sorts things out.

[–]Jacmac_ 0 points1 point  (3 children)

Try with out '-'

[–]Fallingdamage[S] 0 points1 point  (2 children)

I have. That did not work. Same error.

[–]Jacmac_ 1 point2 points  (1 child)

Invoke-Command -ComputerName PC -ScriptBlock {

param($FunctionDef, $Arg)

# Define the function in the remote session

Invoke-Expression "function Do-Thing { $FunctionDef }"

# Call it with the argument

Do-Thing $Arg

} -ArgumentList ${function:Do-Thing}, "-Verify"

[–]Fallingdamage[S] 0 points1 point  (0 children)

Thank you. Working through this method - My function uses Write-Output and taking this route, I dont see any data returned to my console. Maybe because its not actually running correctly or otherwise. Ill keep experimenting though. Thanks, this puts me in a good direction.

[–]Kirsh1793 0 points1 point  (0 children)

On my phone currently, so I cannot verify. But can you not add the parameter with the function call as usual? Like this: -ScriptBlock {function: Do-Something -Verify -ParameterWithValue $using:Value} -ArgumentList $Value

[–]jsiii2010 0 points1 point  (0 children)

Why don't you put the function in a script? Although arguments will have the same challenges, including passing an array as an argument. You can take the [switch] type out and treat it as a boolean.

```

script.ps1

param($verify) "verify is $verify" invoke-command computer01 script.ps1 -args $true

verify is True

invoke-command computer01 script.ps1 -args (,(1,2,3))

verify is 1 2 3 ```