【问题标题】:Powershell XML - Iterating Through Parent NodesPowershell XML - 遍历父节点
【发布时间】:2014-10-17 21:42:04
【问题描述】:

我正在编写一个 Powershell 脚本,该脚本基于 XML 配置文件询问 Active Directory,如下所示:

<domains>
    <domain name="DOMAIN.INTERNAL" exclude="false">
        <orgunit name="OU1" exclude="false"/>
        <orgunit name="OU2" exclude="false">
            <orgunit name="OU3" exclude="false"/>
            <orgunit name="OU4" exclude="false"/>
            <orgunit name="OU5" exclude="true"/>
        </orgunit>
        <host name="HOST1" exclude="false"/>
        <host name="HOST2" exclude="true" />
        <host name="HOST3" exclude="true" />
    </domain>
    <domain name="SUB.DOMAIN.INTERNAL" exclude="false">
        <orgunit name="OU6" exclude="false">
            <orgunit name="OU7" exclude="false">
                <orgunit name="OU8" exclude="false">
                    <host name="HOST4" exclude="false" />
                </orgunit>
            </orgunit>
        </orgunit>
        <host name="HOST5" exclude="false"/>
        <orgunit name="OU7" exclude="true" />
    </domain>
</domains>

我正在加载 xml 文件,设置 xpath,然后选择没有子节点的 orgunit 节点:

$currentPath=Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path
[xml]$configFile = Get-Content "$currentPath\WindowsUpdateOMatic.xml"

foreach ($domain in $configFile.domains.domain) {

    $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//orgunit[@exclude=`"false`"][not (*)]/@name")

    foreach($node in Select-Xml -Xpath $xpath $configFile){

        # Find parent nodes
        $node.Node.ParentNode
    }
}

我的计划是遍历父组织单位节点,以便创建 OU 的完整可分辨名称,例如"OU=bar,OU=foo,DC=sub,DC=domain,DC=internal"。

然后我可以查询活动目录中的 OU 并检索其中的主机。

问题是,$node.Node.ParentNode 的值没有返回任何东西。尝试了各种方法,我确实让它返回“InputStream”,但我现在无法重现它。

我对 Powershell 还很陌生,我很欣赏它看起来我可能会在走路之前尝试跑步。

【问题讨论】:

    标签: xml powershell


    【解决方案1】:

    我已经部分回答了我自己的问题。

    我的示例中的 xpath 是错误的,因为它正在选择一个属性。改变:

    $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//orgunit[@exclude=`"false`"][not (*)]/@name")
    

    收件人:

    $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//orgunit[@exclude=`"false`"][not (*)]")
    

    表示如果父元素是组织单位,$node.Node.ParentNode.ToString() 返回 orgunit

    我现在需要做的就是弄清楚如何递归树,直到我得到一个 != 'orgunit' 的父级。

    我认为这将是一个 do whiledo until 循环。

    【讨论】:

      【解决方案2】:

      这有帮助吗?

      function ConvertFrom-ADXmlToDN ($Element) {
      
          $DNparent = ""
      
          #Get parent DN - Recursion
          if(($Element.ParentNode -ne $null) -and ($Element.ParentNode.LocalName -ne 'domains')) { 
              $DNparent = ",$(ConvertFrom-ADXmlToDN -Element $Element.ParentNode)"
          }
      
          #Convert to LDAP path  
          switch($Element.LocalName) {
              "host" { "CN=$($Element.GetAttribute("name"))$DNparent" }
              "orgunit" { "OU=$($Element.GetAttribute("name"))$DNparent" }
              "domain" { 
                  "DC=$($Element.GetAttribute("name") -split '\.' -join ',DC=')$DNparent"
              }
          }
      }
      
      #Sampledata
      $ou = $xml.domains.domain[1].orgunit[0]
      $machine = $xml.domains.domain[1].orgunit[0].orgunit.orgunit.host
      ConvertFrom-ADXmlToDN -Element $ou
      ConvertFrom-ADXmlToDN -Element $machine
      
      OU=OU6,DC=SUB,DC=DOMAIN,DC=INTERNAL
      CN=HOST4,OU=OU8,OU=OU7,OU=OU6,DC=SUB,DC=DOMAIN,DC=INTERNAL
      

      用法:

      ConvertFrom-ADXmlToDN -Element $node.Node
      

      小建议。您首先遍历 xml 文件以查找域,然后再次遍历它以查找 xpath(指定域名)。

      作为Select-XML 的替代品,您可以使用$domain.SelectNodes($xpath)。但是您必须重写 xpath 以匹配您从域元素而不是根 xml 元素开始的事实。喜欢:

      foreach ($domain in $xml.domains.domain) {
      
          $xpath = ".//orgunit[@exclude='false'][not (*)]"
      
          foreach($node in $domain.SelectNodes($xpath)) {
      
              # Get DN
              ConvertFrom-ADXmlToDN -Element $node
          }
      
      }
      
      OU=OU1,DC=DOMAIN,DC=INTERNAL
      OU=OU3,OU=OU2,DC=DOMAIN,DC=INTERNAL
      OU=OU4,OU=OU2,DC=DOMAIN,DC=INTERNAL
      

      【讨论】:

      • 谢谢。这与我到目前为止所做的并没有什么不同——除了我遍历父元素并为每个组织单位建立一个“FDQN”,然后将其投入到一个函数中以将 FDQN 转换为 DN。你的方式看起来更干净。
      【解决方案3】:

      仅供参考,这是我想出的,虽然我很欣赏它不像弗罗德那样干净。 F的回答:

      clear-host
      Import-Module ActiveDirectory
      Import-Module D:\Code\infrastructure\Scripts\PowershellModules\PSWindowsUpdate 
      
      function FDQNtoDN {
          param([string]$FDQN)
      
          [string]$dn = $null
          [int]$index = 0         
          foreach($part in $FDQN.Split(".")) {
      
              $dn = $dn + "DC=$part"
              $index++
      
              if( $index -ne $FDQN.Split(".").Count) {
                  $dn = $dn + ","
              }
          }    
          return $dn
      }
      
      $currentPath=Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path
      [xml]$configFile = Get-Content "$currentPath\WindowsUpdateOMatic.xml"
      
      # Process each domain specified in the configuration file
      foreach ($domain in $configFile.domains.domain) {
      
          # Retrieve excluded hosts from configuration file
          $excludedHosts = @()
          $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//host[@exclude=`"true`"]/@name")
          foreach($node in Select-Xml -Xpath $xpath $configFile){        
              $excludedHosts += ,@($node.ToString())
          }
      
          # Retrieve excluded org units from the configuration file
          $excludedOrgUnits = @()
          $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//orgunit[@exclude=`"true`"]/@name")    
          foreach($node in Select-Xml -Xpath $xpath $configFile){      
              $excludedOrgUnits += ,@($node.ToString())
          }
      
          $hostsToUpdate = @()
      
          # Retrieve org units within the current domain in the 
          # configuration file, ignoring org units with child 
          # elements
      
          # Process childless org units
          $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//orgunit[@exclude=`"false`"][not (*)]")
          foreach($orgUnit in (Select-Xml -Xpath $xpath $configFile)){      
              $distinguishedName = "OU=" + $orgUnit.Node.name + "," + $distinguishedName
      
              # Ignore excluded org units
              if(-not ($excludedOrgUnits -contains $orgUnit.Node.name)) {
      
                  # Get parent org units ready to interrogate AD                
                  $parent = $orgUnit.Node.ParentNode      
                  while($parent.localname -eq "orgunit") {
                      $distinguishedName = $distinguishedName + "OU=" +  $parent.name + ","
      
                      if(-not ($parent.ParentNode -eq $null)) {
                          $parent = $parent.ParentNode
                      }
                      else {
                          break
                      } #ENDIF
                  } #ENDWHILE
              $distinguishedName = $distinguishedName + (FDQNtoDN($domain.name).ToString())
              $hostsToUpdate += (Get-ADComputer -server $domain.name -Filter 'ObjectClass -eq "Computer"' -SearchBase "$distinguishedName"  -SearchScope OneLevel | Select -expand DNSHostName)
          } #ENDIF
      Remove-Variable distinguishedName
      } #ENDFOREACH $orgUnit
      
      # Retrieve individually specified hosts
      $xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//host[@exclude=`"false`"]")
      foreach($hostToUpdate in (Select-Xml -Xpath $xpath $configFile)) {
      
          # Ignore excluded hosts
          if(-not ($excludedHosts -contains $hostToUpdate.Node.name)) {
              $hostsToUpdate += ($hostToUpdate.Node.name) + (".") + ($domain.name)
          }
      } #ENDFOREACH $host
      
      # Apply updates and reboot each host
      foreach($hostToUpdate in $hostsToUpdate) {
      
          # Attempt to find host in Nagios. If exists, put host into downtime
      
          # If IIS box, drain stop from Apache2 Load Balancer
      
          # Apply Updates
      
          # Reboot & Wait a short period of time
      
          # Check whether host is alive
      
          # If IIS box: (N.B. NAnt scripts exist for some of this)
              # start IIS gracefully
              # warm up
              # run rudimentary checks # apache readd probably does a calc
              # re-add to Apache2 Load Balancer
      
          # If applicable, Remove Nagios Downtime
      }
      
      $hostsToUpdate
      Remove-Variable hostsToUpdate
      

      } #

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-10
        • 2011-08-19
        • 2013-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-01
        • 1970-01-01
        相关资源
        最近更新 更多