名称为 属性 的 Powershell New-WebBinding 管道问题

Powershell New-WebBinding Pipeline Issue with Name Property

我在对象中使用管道时遇到 New-WebBinding 问题。我有一个定义 5 个属性的对象:名称、协议、端口、IP 地址和 HostHeader(所有 5 个在 New-WebBinding cmdlet 中都支持作为接受管道输入:ValueByPropertyName)。但是,当您传入此对象时,它仍会请求提交一个 Name:。如果你想重现这个问题,这里有一个快速测试功能。如果您在提示符下按回车键,它会成功处理对象,添加绑定。但是提示本身将其分解为非交互式脚本。

我已经用 PS v3 和 PS v4 测试过了。

我很确定我做的一切都是正确的,但我想确保没有我可能忽略的东西。现在我只是在 foreach 循环中遍历我的对象集合,它没有这个问题,但想看看这是否是我应该报告的错误。

function Test-WebBinding{
   [CmdletBinding()]
   Param()

   $testBindingCol = @()

   $testBinding1 = New-Object System.Object
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'https'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name Port -Value '4000'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
   $testBinding1 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4000'
   $testBindingCol += $testBinding1

   $testBinding2 = New-Object System.Object
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Name -Value 'Default Web Site'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Protocol -Value 'http'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name Port -Value '4001'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name IPAddress -Value '*'
   $testBinding2 | Add-Member -MemberType NoteProperty -Name HostHeader -Value 'Test4001'
   $testBindingCol += $testBinding2

   $testBindingCol | New-WebBinding
}

PetSerAl 的上述评论是正确的:

One workaround would be to change current location to some site (cd IIS:\Sites\SomeSite), it does not really matter to which

这确实有效,但为什么在正常的文件系统提示下不起作用?

为了发现 New-WebBinding 为什么会这样,我将包含此命令和其他 WebAdministration cmdlet 的 Microsoft.IIS.PowerShell.Provider 程序集加载到 dotPeek 中。程序集位于 GAC 中,因此您将 dotPeek 告诉 "Open from GAC"。

加载时,我们感兴趣的 class 称为 NewWebBindingCommand

粗略检查后,我们可以看到所有参数属性都用 [Parameter(ValueFromPipelineByPropertyName = true)] 属性修饰,所以这是一个好的开始,通过管道传递具有匹配 属性 名称的对象数组应该可以工作:

NewWebBindingCommand最终继承自System.Management.Automation.Cmdlet and in this instance is overriding the BeginProcessing方法。如果被覆盖,PowerShell 会调用 BeginProcessing 并且 "Provides a one-time, preprocessing functionality for the cmdlet."

重要的是要了解 cmdlet 的 BeginProcessing 覆盖在处理任何管道馈送的命名参数并绑定到 cmdlet 的属性之前被调用(参见:Cmdlet Processing Lifecycle (MSDN))。

我们的 New-WebBinding cmdlet 的 BeginProcessing 实现如下所示:

protected override void BeginProcessing()
{
  base.BeginProcessing();
  if (!string.IsNullOrEmpty(this.siteName))
    return;
  this.siteName = this.GetSiteName("Name");
}

this.siteNameName 属性 的私有成员值,它将绑定到 -Name 参数。当我们到达上面的 if(...) 语句时,`this.siteName 尚未绑定(它为空),因此落入:

this.siteName = this.GetSiteName("Name");

GetSiteName() 的调用会调用 cmdlet 的直接基础 class HelperCommand,它提供了许多 "helper" 方法,这些方法对许多不同的 WebAdministration cmdlets.

HelperCommand.GetSiteName(string prompt) 看起来像这样:

protected string GetSiteName(string prompt)
{
  PathInfo pathInfo = this.SessionState.PSVariable.Get("PWD").Value as PathInfo;
  if (pathInfo != null && pathInfo.Provider.Name.Equals("WebAdministration", StringComparison.OrdinalIgnoreCase))
  {
    string[] strArray = pathInfo.Path.Split('\');
    if (strArray.Length == 3 && strArray[1].Equals("sites", StringComparison.OrdinalIgnoreCase))
      return strArray[2];
  }
  if (!string.IsNullOrEmpty(prompt))
    return this.PromptForParameter<string>(prompt);
  return (string) null;
}

为了了解这个问题,我创建了自己的 PowerShell cmdlet(称为 Kevulator,抱歉)并引入了 New-WebBinding cmdlet 的 BeginProcessing() 代码和来自New-WebBinding 的基础 class 辅助方法 GetSiteName().

这是在绑定到 New-Kevulator:

的 PowerShell 会话管道中附加到 PowerShell 会话管道时在 VS2015 中突破 GetSiteName 的屏幕截图

顶部的箭头表明 siteName 支持的 Name 属性 尚未绑定,仍然是 null(如上所述导致 GetSiteName 待执行)。我们也刚刚跨过了这一行的断点:

PathInfo pathInfo = 
        this.SessionState.PSVariable.Get("PWD").Value as PathInfo;

...这决定了我们所处的路径提供者类型。在这种情况下,我们处于普通文件系统 C:\> 提示符下,因此提供程序名称将为 FileSystem。我用第二个箭头突出显示了这一点。

如果我们Import-Module WebAdministrationCD IIS:然后重新运行再break你可以看到path provider变成了WebAdministration负责处理生命IIS:> 内及以后:

如果 pathInfo 名称不等于 WebAdministration,即我们不在 IIS: 提示符下,则代码失败并提示输入 Name 命令行参数,就像您所经历的那样。

如果 pathInfo WebAdministration 那么将发生以下两种情况之一:

如果路径是 IIS:IIS:\Sites,代码将失败并发出 Name 参数的提示。

如果路径是 IIS:\Sites\SomeSiteName 则返回 SomeSiteName 并有效地成为 -Name 参数值,我们从 GetSiteName() 函数中退出返回到 BeginProcessing.

最后一个行为很有用,因为如果您当前的路径是 IIS:\Sites\MySite,那么您可以通过管道输入该站点的绑定数组,但无需指定 Name 参数。或者,如果您确实在对象的管道数组中提供了 Name 属性,那么它们将覆盖从 IIS:\Sites\MySite 上下文中获取的默认站点名称。

所以,回到我们的代码,一旦 BeginProcessing 有 运行 它的过程,我们的管道命名参数现在实际上被绑定,然后 ProcessRecord 方法被调用,这是New-WebBinding 必须执行的肉类和土豆工作。

TLDR:

New-WebBinding 不会绑定管道参数,除非您将当前工作的 目录 更改为 IIS 网站,例如 cd iis:\Sites\MySite.