Powershell XML - 通过父节点迭代

时间:2014-10-16 12:03:40

标签: xml powershell

我正在编写一个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的完整可分辨名称,例如&#34; OU =巴,OU = FOO,DC =子,DC =域,DC =内部&#34;

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

麻烦的是,$ node.Node.ParentNode的值不会返回任何内容。尝试各种各样的事情,我确实让它返回&#34; InputStream&#34;但我现在无法复制。

我对Powershell来说相当新鲜,我很欣赏它看起来好像我可以在我走路之前跑步。

3 个答案:

答案 0 :(得分:0)

我已经设法回答了我自己的问题。

我的示例中的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

现在我需要做的就是弄清楚如何在我得到父母之前递归树!=&#39; orgunit&#39;。

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

答案 1 :(得分:0)

这有帮助吗?

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以匹配你从domain-element而不是root xml-element开始的事实。像:

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

答案 2 :(得分:0)

仅供参考,这是我想出来的,虽然我很欣赏它并不像Frode那么干净。 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

}#