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

时间:2017-01-11 16:56:23

标签: powershell exchange-server powershell-remoting

我在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相同。除了不使用可选参数之外,我无法找到任何变通方法。

1 个答案:

答案 0 :(得分:2)

我能够通过删除参数来解决它,如果它不存在,就像这样:

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

这将返回错误:

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

这使得onus重新回到验证属性。如果参数未绑定,Test-Function调用将无法运行,因此else { $true }无关紧要。但是一旦Import-PSSession运行它,它将跳过Test-Path部分。

问题在于,如果您致电Test-Function -SomePath "",验证将由Test-Function的调用运行,并且它不会失败,这不是您想要的。您必须将路径验证移回功能。

我想我也尝试添加[ValidateNotNullOrEmpty()],这会在Test-Function的调用中发现这一点,但是Import-PSSession也会运行它,而你和&{ #39;当参数未绑定时,我将回到上面提到的错误。

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

找到它

在脚本块as laid out in this answer上使用.GetNewClosure()时,看起来是同样的问题。

所以查看.GetNewClosure()的代码,它会调用a method on modules called .CaptureLocals() which is defined here。您可以在其中看到它只是复制所有各种属性,包括属性。

我不确定"修复"虽然可以在这一点上应用,因为它有点做正确的事情。它正在复制变量;它对参数一无所知。参数是否绑定不是变量的一部分。

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

我不知道该代码在哪里..(但是?)。