Reputation: 979
I have an XML file at $DSConfigPath
that contains information as such:
<dataSources>
<add id="DataSource1" type="Fixed" distance="1">
<parameters>
<name />
<address>10.1.1.10</address>
<otherIdentifiers>DataSource1</otherIdentifiers>
<activeStatus>true</activeStatus>
<ip>10.1.1.10</ip>
<port>952</port>
</parameters>
</add>
<add id="DataSource2" type="Fixed" distance="2">
<parameters>
<name />
<address>10.1.1.11</address>
<otherIdentifiers>DataSource2</otherIdentifiers>
<activeStatus>true</activeStatus>
<ip>10.1.1.11</ip>
<port>952</port>
</parameters>
</add>
<add id="DataSource3" type="Fixed" distance="3">
<parameters>
<name />
<address>10.1.1.12</address>
<otherIdentifiers>DataSource1</otherIdentifiers>
<activeStatus>false</activeStatus>
<ip>10.1.1.12</ip>
<port>952</port>
</parameters>
</add>
</dataSources>
My goal is to do a do a port connection test to any IP/port where the <activeStatus>
is 'true.'
I've got the following function, which I've verified will give me the correct results when I put in a specific $hostname
and $port
:
function Test-Port($hostname, $port) {
# This works no matter in which form we get $host - hostname or ip address
try {
$ip = [System.Net.Dns]::GetHostAddresses($hostname) |
Select-Object IPAddressToString -ExpandProperty IPAddressToString
if ($ip.GetType().Name -eq "Object[]") {
#If we have several ip's for that address, let's take first one
$ip = $ip[0]
}
} catch {
Write-Host "Possibly $hostname is wrong hostname or IP"
return
}
$t = New-Object Net.Sockets.TcpClient
# We use Try\Catch to remove exception info from console if we can't connect
try {
$t.Connect($ip,$port)
} catch {}
if ($t.Connected) {
$t.Close()
$msg = "Port $port is operational"
} else {
$msg = "Port $port on $ip is closed, "
$msg += "You may need to contact your IT team to open it. "
}
Write-Host $msg
}
So now when I add the following variables:
[xml]$DSConfig = gc "$DSConfigPath"
$DS = $dsconfig.datasources.add.parameters
$DSName = $DS.otherIdentifiers
$DSIP = $DS.ip
$DSPort = $DS.port
$DSActive = $DS | Where-Object {$_.activeStatus -eq 'True'}
$hostname = $DSIP # yes I realize this is redundant
$port = $DSPORT # and yes, I realize this is redundant as well
Then run:
foreach ($DSActive in $DSConfig) {Test-Port $hostname $port}
I get the result:
Possibly 10.1.1.10 10.1.1.11is wrong hostname or IP
Any suggestion how I can get it to test the connection to 10.1.1.10:952, give that result, then test the connection to 10.1.1.11:952 and then give that result?
Upvotes: 3
Views: 15564
Reputation: 200493
The statement
$DS = $dsconfig.datasources.add.parameters
puts a list of all <parameter>
nodes in the variable $DS
. If you check the $DS.Count
you'll see that it has the value 3 for your sample data, and if you echo the variable you'll see something like this:
PS C:\> Write-Output $DS name : address : 10.1.1.10 otherIdentifiers : DataSource1 activeStatus : true ip : 10.1.1.10 port : 952 name : address : 10.1.1.11 otherIdentifiers : DataSource2 activeStatus : true ip : 10.1.1.11 port : 952 name : address : 10.1.1.12 otherIdentifiers : DataSource1 activeStatus : false ip : 10.1.1.12 port : 952
Next the statements
$DSIP = $DS.ip
$DSPort = $DS.port
fill the variables $DSIP
and $DSPort
with a list of all IP addresses and all ports respectively.
PS C:\> Write-Output $DSIP 10.1.1.10 10.1.1.11 10.1.1.12
On PowerShell v3 and newer, that is. Prior to PowerShell v3 the statements would throw an error, because older versions don't support member enumeration, i.e. accessing properties and methods on the elements of an array when the array object itself doesn't have that property or method.
When you're passing this list of IP addresses to Test-Port
, the statement
$ip = [System.Net.Dns]::GetHostAddresses($hostname)
fails with a MethodInvocationException
, because GetHostAddresses()
expects a single string, not a string array.
Also, your loop
foreach ($DSActive in $DSConfig) {Test-Port $hostname $port}
will terminate after one iteration because $DSConfig
has just one element: the root node (<dataSources>
).
To have your code call Test-Path
for each IP address and port change the above loop into this:
foreach ($DSActive in $DSConfig.datasources.add.parameters) {
Test-Port $DSActive.ip $DSActive.port
}
Upvotes: 1
Reputation: 36332
Alternative method using Select-Xml
. This effectively does the same thing as Mathias's answer, but uses a cmdlet instead of the method. Not particularly useful unless you want to take advantage of some of the other options built into the cmdlet that the method doesn't make available (not real useful here, but can definitely be useful in more complex uses).
# Read XML document
[xml]$DSConfig = gc "$DSConfigPath"
# Select <parameters> nodes
$ParametersNode = Select-Xml -Xml $DSConfig -XPath '//parameters'|% {
Test-Port $_.Node.ip $_.Node.port
}
Upvotes: 0
Reputation: 174920
Use SelectNodes()
to grab the <parameters>
nodes from the xml document:
# Read XML document
[xml]$DSConfig = gc "$DSConfigPath"
# Select <parameters> nodes
$ParametersNode = $DSConfig.SelectNodes('//parameters')
# Loop over selected nodes
foreach($Node in $ParametersNode){
# Test if activeStatus == 'true'
if($Node.activeStatus -eq 'true') {
# Run the Test-Port command
Test-Port $Node.ip -port $Node.port
}
}
Upvotes: 4