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

时间:2016-02-15 21:42:05

标签: powershell powershell-v3.0 powershell-v4.0 powershell-v5.0

当在对象中进行管道传输时,我遇到了New-WebBinding的问题。我有一个对象,它定义了5个属性:Name,Protocol,Port,IPAddress和HostHeader(New-WebBinding cmdlet支持所有5个作为Accept Pipeline输入:ValueByPropertyName)。但是,当您在此对象中进行管道传输时,它仍然会请求提交名称:。如果您想复制该问题,这是一个快速测试功能。如果在提示符处按Enter键,则会成功处理对象,添加绑定。但提示本身将其作为非交互式脚本打破。

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

1 个答案:

答案 0 :(得分:4)

PetSerAl在上面的评论中找到了正确的想法:

  

一种解决方法是将当前位置更改为某个网站(cd IIS:\Sites\SomeSite),这与

无关

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

要发现New-WebBinding行为方式的原因,我将包含此Microsoft.IIS.PowerShell.Provider cmdlet的WebAdministration程序集加载到dotPeek中。该程序集存在于GAC中,因此您可以告诉dotPeek从GAC"打开。

加载后,我们感兴趣的课程称为NewWebBindingCommand

粗略检查后,我们可以看到所有参数属性都使用[Parameter(ValueFromPipelineByPropertyName = true)]属性进行修饰,因此这是一个良好的开端,管道一系列具有匹配属性名称的对象应该可行:

enter image description here

NewWebBindingCommand最终继承自System.Management.Automation.Cmdlet,在此实例中覆盖了BeginProcessing方法。如果被覆盖,则PowerShell会调用BeginProcessing并且"为cmdlet提供一次性预处理功能。"

重要的是要了解在处理任何传递命名参数的管道并绑定到cmdlet的属性之前调用cmdlet的BeginProcessing覆盖(请参阅: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的直接基类HelperCommand,该基类提供了许多"帮助程序"对许多不同WebAdministration cmdlet有用的方法。

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的基类帮助方法GetSiteName()的代码。

以下是在绑定到GetSiteName的PowerShell会话管道中附加VS2015内部New-Kevulator时的屏幕截图:

enter image description here

顶部箭头表明Name支持的siteName属性尚未绑定且仍为null(如上所述导致GetSiteName被执行)。我们也刚刚走过这条线的断点:

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

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

如果我们Import-Module WebAdministrationCD IIS:然后重新运行并再次中断,您可以看到路径提供程序已更改为WebAdministration,负责处理IIS:>内的生活以及:

enter image description here

如果pathInfo名称不等于WebAdministration,即我们未处于IIS:提示符,则代码会通过,并会提示{{1}在命令行参数,正如您所经历的那样。

如果Name pathInfo,则会发生以下两种情况之一:

如果路径为WebAdministrationIIS:,则代码会落空并发出IIS:\Sites参数的提示。

如果路径为Name,则会返回IIS:\Sites\SomeSiteName并有效地成为SomeSiteName参数值,我们会从-Name功能退回到GetSiteName()

最后一个行为很有用,因为如果您当前的路径是BeginProcessing,那么您可以管道该站点的绑定数组,但不指定IIS:\Sites\MySite参数。或者,如果您在管道对象数组中提供Name属性,则这些属性将覆盖从Name上下文中选择的默认网站名称。

所以,回到我们的代码,一旦IIS:\Sites\MySite运行它的路线,我们的管道命名参数现在实际上被绑定,然后调用BeginProcessing方法,这是{和土豆的工作'{ {1}}必须执行。

<强> TLDR:

除非您将当前正在运行的目录更改为IIS网站,否则

ProcessRecord不会绑定管道化参数,例如New-WebBinding