【问题标题】:Import-PSSession Error when called in a function在函数中调用时出现 Import-PSSession 错误
【发布时间】:2017-05-26 13:47:46
【问题描述】:

我在 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 报告的相同。除了不使用可选参数之外,我还没有找到任何解决方法。

【问题讨论】:

    标签: powershell exchange-server powershell-remoting


    【解决方案1】:

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

    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

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


    我已经通过更多测试确认了这种行为。即使您确保验证属性将正确运行,其结果仍将被使用:

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

    这将返回错误:

    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 运行它,它将跳过Test-Path 部分。

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

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

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


    找到了

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

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

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

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

    我不知道那个代码在哪里..(还没有?)。

    【讨论】:

    • @sodawillow 我不知道,我目前正在查看the source code for Import-PSSession,但它很长。它并没有直接向我跳出来,我认为我没有时间浏览所有这些,但我怀疑它归结为用于在执行上下文中查找参数的任何方法。
    • @sodawillow 顺便说一下,我做了更多测试,请参阅我的编辑。
    • tyvm 为您的宝贵时间:)
    • @briantist 感谢您提供如此深入的回答。我想知道这是否会导致参数被验证两次,即。如果 $somepath 最初设置为通过验证脚本的内容,然后在 Import-PSSession 之前更改了该行,如果这会导致失败。
    • @RJDeVries 是的,我相信这正是它的工作方式。您还可以通过消除 PSSession 内容并直接调用 {}.GetNewClosure() 来简化测试。这是一个更简单的测试,不依赖于远程机器。
    猜你喜欢
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    相关资源
    最近更新 更多