在函数中调用时导入 PSSession 错误

Import-PSSession Error when called in a function

我在使用 Import-PSSession 时遇到了一些有趣的行为。我正在尝试为 Exchange 2013 服务器建立 PSSession 并导入所有 cmdlet。这在 运行 手动时工作正常:

$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$remoteexchserver/PowerShell/ -Authentication Kerberos -ErrorAction Stop
Import-PSSession $Session -ErrorAction Stop  -AllowClobber -DisableNameChecking

但是,如果我 运行 它在一个带有可选参数的函数中,例如:

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

我收到错误:

Import-PSSession : Cannot bind argument to parameter 'Path' because it is an empty string 

当 运行在没有为 $SomePath 指定值的情况下调用函数。指定有效值时工作正常。这似乎与报告的内容相同 here。不过,除了不使用可选参数外,我还没有找到任何解决方法。

如果参数不存在,我可以通过删除参数来解决它,如下所示:

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
    }
}

一个更好的例子可能是:

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
    }
}

差别很小。

如果可以以任何方式将变量强制为 $false,则第一个将删除变量(例如,如果提供并通过验证但仍评估为 $false),而第二个只有在根本没有提供的情况下才会将其删除。您必须决定哪一个更合适。


至于为什么会这样,我不确定 (见下文),但仔细观察错误,很明显,验证脚本正在 运行 反对参数的值,即使它没有绑定。错误来自 Test-Path.

$_ 最终为空,因此 Test-Path 抛出错误。 Import-PSSession 所做的部分工作是 运行 验证,但不区分绑定参数和未绑定参数。


我已经通过更多测试确认了此行为。即使您确保验证属性 运行 没有错误,它的结果仍将被使用:

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

这将 return 错误:

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

因此,我将其更改为:

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
    }
}

这将责任放回验证属性上。如果参数未绑定,Test-Function 调用将不会 运行 它,因此 else { $true } 无关紧要。但是一旦 Import-PSSession 运行s 它,它将跳过 Test-Path 部分。

问题是,如果您调用 Test-Function -SomePath "",验证将由 Test-Function 调用 运行,并且不会失败,这不是您想要的。您必须将路径验证移回函数中。

我想我也会尝试添加 [ValidateNotNullOrEmpty()],这将在 Test-Function 的调用中捕获它,但是 Import-PSSession 也会 运行,并且未绑定参数时,你会回到我上面提到的错误。

所以在这一点上,我认为删除变量的同时保持验证,就像您没有调用 Import-PSSession 时一样,是最直接的解决方案。


找到了

看起来这与在脚本块上使用 .GetNewClosure() 时发生的问题相同,as laid out in this answer

因此查看 .GetNewClosure() 的代码,它调用 a method on modules called .CaptureLocals() which is defined here。在那里您可以看到它只是简单地复制了所有各种属性,包括属性。

不过,我不确定此时是否可以应用 "fix",因为它是在做正确的事情。它正在复制 变量;它对参数一无所知。参数是否绑定不是变量的一部分。

所以我认为应该在参数被定义为局部变量的任何地方应用修复,这样就不会定义未绑定的参数。这将隐含地解决这个问题,并且根据我花了几分钟时间思考它,不会有其他副作用(但谁知道呢)。

虽然我不知道代码在哪里..(还是?)。