question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`System.Management.Automation.SwitchParameter` is not properly passed to Remote `Invoke-Command` Sessions

See original GitHub issue

Prerequisites

Steps to reproduce

Attempt to execute the following script; you will need at least one remote machine:

function Test-SwitchRemoteBehavior {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [System.Management.Automation.SwitchParameter]$Force
    )
    process {
        # In these cases the -Force switch works as expected
        Write-Host "###########################################################"
        Write-Host "# Show how this works locally                             #"
        Write-Host "###########################################################"
        Write-Host "Force was false: $($Force -eq $false)"
        Write-Host "Force was NOT present: $($Force.IsPresent -eq $false)"

        # In these cases the -Force switch works as expected still
        Invoke-Command -ScriptBlock {
            param([System.Management.Automation.SwitchParameter]$Force)
            process {
                Write-Host "###################################################"
                Write-Host "# Show on the local Computer via Invoke-Command   #"
                Write-Host "###################################################"
                Write-Host "Force was false: $($Force -eq $false)"
                Write-Host "Force was NOT present: $($Force.IsPresent -eq $false)"
            }
        } -ArgumentList @($Force)

        # In this case the value of the -Force switch is lost for some reason
        # it appears to be a bug
        Invoke-Command -ComputerName 'SomeRemoteMachineHere' -ScriptBlock {
            param([System.Management.Automation.SwitchParameter]$Force)
            process {
                Write-Host "###################################################"
                Write-Host "# Show on a Remote Computer via Invoke-Command    #"
                Write-Host "###################################################"
                Write-Host "Force was false: $($Force -eq $false)"
                Write-Host "Force was NOT present: $($Force.IsPresent -eq $false)"
            }
        } -ArgumentList @($Force)
    }
}

Test-SwitchRemoteBehavior -Force

For what its worth this behavior is also present in PowerShell 5.1 and several others have encountered it as seen around the Internet:

StackOverflow: https://stackoverflow.com/questions/24011684/powershell-pass-a-switch-to-a-function-with-invoke-command VMWare Post: https://communities.vmware.com/t5/VMware-PowerCLI-Discussions/How-to-past-Boolean-variables/td-p/2820126

The work around is to pass this using $Force.IsPresent but it would be great to understand why this quirk exists, as far as I can tell there was never a bug report or even an explanation of why this behavior differs.

Expected behavior

###########################################################
# Show how this works locally                             #
###########################################################
Force was false: False
Force was NOT present: False
###################################################
# Show on the local Computer via Invoke-Command   #
###################################################
Force was false: False
Force was NOT present: False
###################################################
# Show on a Remote Computer via Invoke-Command    #
###################################################
Force was false: False
Force was NOT present: False

Actual behavior

###########################################################
# Show how this works locally                             #
###########################################################
Force was false: False
Force was NOT present: False
###################################################
# Show on the local Computer via Invoke-Command   #
###################################################
Force was false: False
Force was NOT present: False
###################################################
# Show on a Remote Computer via Invoke-Command    #
###################################################
Force was false: True
Force was NOT present: True

Error details

No response

Environment data

Name                           Value
----                           -----
PSVersion                      7.1.3
PSEdition                      Core
GitCommitId                    7.1.3
OS                             Microsoft Windows 6.3.9600
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0.}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:1
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
Jaykulcommented, Aug 23, 2021

Switch parameters explicitly and inherently cannot be bound positionally. The workarounds are legion.

  • use $using:force instead of passing a parameter at all
  • use a boolean parameter instead of a switch when you’re writing an Invoke-Command scriptblock, since you know all the parameters must be bound positionally…
  • prefix it with [Parameter(Position=0)] to make it positional
1reaction
Jaykulcommented, Aug 28, 2021

Hmm. That’s always a fun question: how do you know that?

I’m honestly not sure how I learned that initially. I noticed it again recently while prototyping … I was working on something recently in Pester where I needed to replicate positional parameters. I “knew” how it worked, but as usual, I prototyped in the terminal to validate my knowledge, and I wrote something like this:

function Test-Parameter {
  [CmdletBinding()]param(
    [string]$Path,
    [switch]$Force,
    [string]$Destination
  )
}

And then ran this to get positional parameters:

(Get-Command Test-Parameter).Parameters.Values.Where{ $_.Attributes.Position -ge 0 }

Name            : Path
ParameterType   : System.String
ParameterSets   : {[__AllParameterSets, System.Management.Automation.ParameterSetMetadata]}
IsDynamic       : False
Aliases         : {}
Attributes      : {, System.Management.Automation.ArgumentTypeConverterAttribute}
SwitchParameter : False

Name            : Destination
ParameterType   : System.String
ParameterSets   : {[__AllParameterSets, System.Management.Automation.ParameterSetMetadata]}
IsDynamic       : False
Aliases         : {}
Attributes      : {, System.Management.Automation.ArgumentTypeConverterAttribute}
SwitchParameter : False

If you run that without the .Where filter, you’ll see all the “built in” parameters and switch(es), but when you filter for positional parameters, it skips over the Force switch. PowerShell adds implicit [Parameter()] and [ArguymentTypeConverter()] attributes on all parameters, but the implicit ParameterAttribute does not include a Position value for switches (or rather, it’s set to [int]::MinValue). I was relieved by that (and slightly surprised by the “ExperimentName” and “ExperimentAction” fields that I had never seen before) and it stuck with me.

I also know that in C# the type documentation says it “can only be used as a switch” (emphasis mine), so that’s clearer than the PowerShell about doc.

I just opened a PR against the about topic to make it clearer there.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Powershell Question | Ars OpenForum
I've never used invoke-command to run things across all Exchange ... using Invoke to make the command run on the remote host, think...
Read more >
How objects are sent to and from remote sessions
This post explains how remote objects are serialized and which types of ... cannot be invoked in the remote session (for example, System....
Read more >
Invoke-Command with remote session: Cannot validate ...
For some reasons my Invoke-Command do not run properly, I have the following error message: Cannot validate argument on parameter 'Name'. The ...
Read more >
InvokeCommandCommand Class (Microsoft.PowerShell. ...
This cmdlet executes a specified script block on one or more remote machines. The expression or command, as they will be interchangeably called, ......
Read more >
Mocking New-PSSession and Invoke-Command
Since I'm calling Invoke-Command and passing the results of New-PSSession, I need to return a PSSession object from the Mock. Here is the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found