Powershell XML-遍历父节点
我正在编写一个Powershell脚本,它根据XML配置文件查询Active Directory,该文件如下所示:Powershell XML-遍历父节点,xml,powershell,Xml,Powershell,我正在编写一个Powershell脚本,它根据XML配置文件查询Active Directory,该文件如下所示: <domains> <domain name="DOMAIN.INTERNAL" exclude="false"> <orgunit name="OU1" exclude="false"/> <orgunit name="OU2" exclude="false"> <
<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>
我的计划是迭代父组织单元节点,以便创建OU的完整可分辨名称,例如OU=bar、OU=foo、DC=sub、DC=domain、DC=internal
然后我可以在active directory中查询OU并检索其中的主机
问题是,$node.node.ParentNode的值不返回任何内容。尝试了各种各样的方法,我确实让它返回了InputStream,但我现在无法复制它
我对Powershell还不太熟悉,我很欣赏它看起来我可能在走路之前就想跑了。在某种程度上,我已经设法回答了我自己的问题 我的示例中的xpath是错误的,因为它正在选择一个属性。更改:
$xpath = ("/domains/domain[@name=`"") + ($domain.name) + ("`"]//orgunit[@exclude=`"false`"][not (*)]/@name")
致:
如果父元素是组织单位,则表示$node.node.ParentNode.ToString返回orgunit
我现在需要做的就是找出如何在树上递归,直到得到一个组织单位'
我想这将是一个do-while或do-until循环。这有帮助吗
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
作为选择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
仅供参考,以下是我的想法,虽然我知道它没有弗罗德那么干净。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
}谢谢。这与我到目前为止的结论并没有什么不同,只是我在迭代父元素,为每个组织单元建立一个FDQN,然后将其放入一个函数中,将FDQN转换为DN。你的路看起来干净多了。
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
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