在函数中调用时导入 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",因为它是在做正确的事情。它正在复制 变量;它对参数一无所知。参数是否绑定不是变量的一部分。
所以我认为应该在参数被定义为局部变量的任何地方应用修复,这样就不会定义未绑定的参数。这将隐含地解决这个问题,并且根据我花了几分钟时间思考它,不会有其他副作用(但谁知道呢)。
虽然我不知道代码在哪里..(还是?)。
我在使用 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",因为它是在做正确的事情。它正在复制 变量;它对参数一无所知。参数是否绑定不是变量的一部分。
所以我认为应该在参数被定义为局部变量的任何地方应用修复,这样就不会定义未绑定的参数。这将隐含地解决这个问题,并且根据我花了几分钟时间思考它,不会有其他副作用(但谁知道呢)。
虽然我不知道代码在哪里..(还是?)。