#PSTip Using XPath in PowerShell, Part 3

Filtering data with XPath works very well even if we need more complex filters that require information from different levels in the XML document. Perfect example of such document is the result of nmap stored in the XML format:

nmap.exe 192.168.200.0/24 -oX testLocal.xml

Note: You can find the code and input file here.

If we want to retrieve all IPv4 addresses from the hosts that are currently up we can ask for addresses (using //host/address path) and filter them on the information both on current level (to make sure we get correct address type) and value of attribute on node that has the same parent as node we want to retrieve (‘state’ on ‘status’ node):

$addressUp = @'
    //host/address[
        @addrtype = 'ipv4' and
        ../status/@state = 'up'
    ]
'@ 

$nodeList = Select-Xml -Path .\testLocal.xml -XPath $addressUp |
            ForEach-Object { $_.Node }

$ldapPort = @'
    /host/ports/port[
        @portid = '389' and
        state/@state = 'open'
    ]
'@

foreach ($node in $nodeList) {
    $noteproperties = [ordered]@{
        Address = $node.addr
        HostName = $node.ParentNode.hostnames.hostname.name
    }

    $partialXml = [XML]('<host>{0}</host>' -f $node.ParentNode.InnerXml)

    Select-Xml -Xml $partialXml -XPath $ldapPort  | 
        Select-Object -ExpandProperty Node | 
        Add-Member -NotePropertyMembers $noteproperties -PassThru
}

Address  : 192.168.200.1
HostName : DC
protocol : tcp
portid   : 389
state    : state
service  : service

Because ‘Node’ property will not contain markup for opening/closing node, we need to add it to get valid XML. Results are not very surprising–it’s not uncommon for domain controllers to have port 389 open.

Share on: