Import-PSSession Error when called in a function Import-PSSession Error when called in a function powershell powershell

Import-PSSession Error when called in a function


I was able to work around it by removing the parameter if it doesn't exist, like so:

FUNCTION Test-Function {    [CmdletBinding()]    Param(        [Parameter(Mandatory=$false)]        [ValidateScript({Test-Path $_ -PathType Container})]        [string]$SomePath    )    begin {            if (!$SomePath) {                Remove-Variable SomePath            }            $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$remoteexchserver/PowerShell/ -Authentication Kerberos -ErrorAction Stop            Import-PSSession $Session -ErrorAction Stop  -AllowClobber -DisableNameChecking    }}

A better example might be:

FUNCTION Test-Function {    [CmdletBinding()]    Param(        [Parameter(Mandatory=$false)]        [ValidateScript({Test-Path $_ -PathType Container})]        [string]$SomePath    )    begin {            $remoteexchserver = 'prdex10ny06'            if (-not $PSBoundParameters.ContainsKey('SomePath')) {                Remove-Variable SomePath            }            $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$remoteexchserver/PowerShell/ -Authentication Kerberos -ErrorAction Stop            Import-PSSession $Session -ErrorAction Stop  -AllowClobber -DisableNameChecking    }}

The difference is subtle.

The first one will remove the variable if it can be coerced to $false in any way (for example if it was supplied and passed validation but still evals to $false), while the second will only remove it if it wasn't supplied at all. You'll have to decide which one is more appropriate.


As for why this is happening, I'm not sure (See below), but looking more closely at the error, it's clear that the validation script is being run against the value of the parameter even when it's not bound. The error is coming from Test-Path.

$_ ends up being null and so Test-Path throws the error. Some part of what Import-PSSession is doing is running the validation but isn't differentiating between parameters that were bound vs unbound.


I've confirmed this behavior with more testing. Even if you ensure that the validation attribute will run without error, its result will still be used:

    [ValidateScript({         if ($_) {            Test-Path $_ -PathType Container        }    })]

This will return the error:

Import-PSSession : The attribute cannot be added because variable SomePath with value  would no longer be valid.At line:21 char:13+             Import-PSSession $Session -ErrorAction Stop  -AllowClobbe ...+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    + CategoryInfo          : MetadataError: (:) [Import-PSSession], ValidationMetadataException    + FullyQualifiedErrorId : ValidateSetFailure,Microsoft.PowerShell.Commands.ImportPSSessionCommand

So instead, I've changed it to this:

FUNCTION Test-Function {    [CmdletBinding()]    Param(        [Parameter(Mandatory=$false)]        [ValidateScript({             if ($_) {                Test-Path $_ -PathType Container            } else {                $true            }        })]        [string]$SomePath    )    begin {            $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$remoteexchserver/PowerShell/ -Authentication Kerberos -ErrorAction Stop            Import-PSSession $Session -ErrorAction Stop  -AllowClobber -DisableNameChecking    }}

This puts the onus back on the validation attribute. If the parameter is unbound, the Test-Function invocation won't run it, so the else { $true } won't matter. But once Import-PSSession runs it, it will skip the Test-Path part.

The problem is that if you call Test-Function -SomePath "" the validation will be run by Test-Function's invocation, and it won't fail, which is not what you want. You'd have to move the path validation back into the function.

I figured I'd try also adding [ValidateNotNullOrEmpty()], which will catch this on Test-Function's invocation, but then Import-PSSession will also run that, and you'll be back to the error I mentioned above when the parameter is not bound.

So at this point I think removing the variable while keeping the validation as you would if you weren't calling Import-PSSession, is the most straightforward solution.


Found It

It looks as though it's the same issue that happens when using .GetNewClosure() on a scriptblock, as laid out in this answer.

So looking at the code for .GetNewClosure(), it calls a method on modules called .CaptureLocals() which is defined here. That's where you can see that it simply copies over all the various properties including the attributes.

I'm not sure the "fix" can be applied at this point though, because it's sort of doing the right thing. It's copying variables; it doesn't know anything about parameters. Whether the parameter is bound or not is not part of the variable.

So instead I think the fix should be applied wherever parameters are defined as local variables, so that unbound parameters are not defined. That would implicitly fix this and based on the few minutes I've spent thinking about it, wouldn't have other side effects (but who knows).

I don't know where that code is though.. (yet?).