
close
close
I hope you’ve been following along in this series as we build a PowerShell tool to ping a range of IP addresses in a given subnet. In the last article, we left with a pretty decent tool that displayed the IP address for computers that responded. Although that may be all you need, lets try to get a more rich result since PowerShell is already writing an object to the pipeline. It would probably be helpful to see the computer name.
PowerShell Ping Sweep Tool Article Series
If you are running Windows 8 or later, you can easily use the Resolve-DnsName cmdlet with an IP address.
advertisment
Excellent. I can see that the NameHost property is what I want. Although I'm not a big fan of using raw .NET when there is a perfectly good cmdlet to use, this can serve as an alternative method:Using PowerShell's Resolve-DnsName cmdlet. (Image Credit: Jeff Hicks)
This approach provides us with a different property name but gives us the same result. It would be nice to be able to write an object to the pipeline with the IP address and its host name. It's possible that the IP address is not registered with DNS, so I should handle that error. Here's the relevant change to the function.Using .NET to grab the host name in Windows PowerShell. (Image Credit: Jeff Hicks)
Here's what happens when I run the new version.Another useful entry might the device's MAC address. I can't guarantee that the host will be a Windows host or that I will have permissions to access it, so I can't rely on WMI or CIM cmdlets. However, there is an old-school alternative. When I ping a remote IP address, an entry will be made in the arp table.Our result after running test-subnet. (Image Credit: Jeff Hicks)
I can also get a specific IP address.Using the ARP command in Windows PowerShell. (Image Credit: Jeff Hicks)
The physical address is the MAC address. To obtain the MAC address, I will have to parse the ARP output. I'll need to get just the line with the MAC. There are a few ways I could parse the text output. Here's one approach:Using a specific IP address with the ARP command in Windows PowerShell. (Image Credit: Jeff Hicks)
I can split this line into an array of three elements. I am splitting on the white spaces between the columns using a regular expression pattern.Parsing the text output. (Image Credit: Jeff Hicks)
I find it helpful to trim the line first so that there are no extra spaces.The MAC address is the second element.Trimming the first line. (Image Credit: Jeff Hicks)
I can use this code in my function so that if the address can be pinged, I can get the MAC and add it to the output. Here's the new version of Test-Subnet.
[cmdletbinding()] Param( [Parameter(Position=0,HelpMessage="Enter an IPv4 subnet ending in 0.")] [ValidatePattern("\d{1,3}\.\d{1,3}\.\d{1,3}\.0")] [string]$Subnet= ((Get-NetIPAddress -AddressFamily IPv4).Where({$_.InterfaceAlias -notmatch "Bluetooth|Loopback"}).IPAddress -replace "\d{1,3}$","0"), [ValidateRange(1,255)] [int]$Start = 1, [ValidateRange(1,255)] [int]$End = 254, [ValidateRange(1,10)] [Alias("count")] [int]$Ping = 1 ) Write-Verbose "Pinging $subnet from $start to $end" Write-Verbose "Testing with $ping pings(s)" #a hash table of parameter values to splat to Write-Progress $progHash = @{ Activity = "Ping Sweep" CurrentOperation = "None" Status = "Pinging IP Address" PercentComplete = 0 } #How many addresses need to be pinged? $count = ($end - $start)+1 <# take the subnet and split it into an array then join the first 3 elements back into a string separated by a period. This will be used to construct an IP address. #> $base = $subnet.split(".")[0..2] -join "." #Initialize a counter $i = 0 #loop while the value of $start is <= $end while ($start -le $end) { #increment the counter write-Verbose $start $i++ #calculate % processed for Write-Progress $progHash.PercentComplete = ($i/$count)*100 #define the IP address to be pinged by using the current value of $start $IP = "$base.$start" #Use the value in Write-Progress $proghash.currentoperation = $IP Write-Progress @proghash #get local IP $local = (Get-NetIPAddress -AddressFamily IPv4).Where({$_.InterfaceAlias -notmatch "Bluetooth|Loopback"}) #test the connection if (Test-Connection -ComputerName $IP -Count $ping -Quiet) { #if the IP is not local get the MAC if ($IP -ne $Local.IPAddress) { #get MAC entry from ARP table Try { $arp = (arp -a $IP | where {$_ -match $IP}).trim() -split "\s+" $MAC = $arp[1] } Catch { #this should never happen but just in case Write-Warning "Failed to resolve MAC for $IP" $MAC = "unknown" } } else { #get local MAC $MAC = ($local | Get-NetAdapter).MACAddress } #attempt to resolve the hostname Try { $iphost = (Resolve-DNSName -Name $IP -ErrorAction Stop).Namehost } Catch { Write-Verbose "Failed to resolve host name for $IP" #set a value $iphost = "unknown" } Finally { #create a custom object [pscustomobject]@{ IPAddress = $IP Hostname = $iphost MAC = $MAC } } } #if test ping #increment the value $start by 1 $start++ } #close while loop } #end function
After testing, I realized I needed to take the local IP address into account.
The local address won't be in the ARP table. If the address I'm pinging is the same as the local address, I'll need to take other steps to get the MAC.
I also included some error handling in the rare situation where the address can be pinged but not retrieved from the ARP cache. I don't think this is even possible, but it is only a few lines of code, so I included it to be safe. Most of my tests have been with a small subnet subset, but I should really test the entire thing end to end. I'm also a little curious about how long this will take.
Subtracting the begin and finish times shows this took about 6 minutes.Testing our function in Windows PowerShell. (Image Credit: Jeff Hicks)
You might get different results depending on the quality of your network and especially name resolution. In fact all of the extra bits could be totally optional. You could create the function so that the basic output is the IP address and if you want the additional detail, it is specified by parameter. There are some advanced things we could do to really speed up performance with runspaces or the use of background jobs, but that is definitely advanced material and beyond the scope of this series. However, there is one more feature I think we can add and I'll cover that next time.Our function took six minutes to complete. (Image Credit: Jeff Hicks)
More from Jeff Hicks
advertisment
Petri Newsletters
Whether it’s Security or Cloud Computing, we have the know-how for you. Sign up for our newsletters here.
advertisment
More in PowerShell
Microsoft’s New PowerShell Crescendo Tool Facilitates Native Command-Line Wraps
Mar 21, 2022 | Rabia Noureen
Most popular on petri
Log in to save content to your profile.
Article saved!
Access saved content from your profile page. View Saved
Join The Conversation
Create a free account today to participate in forum conversations, comment on posts and more.
Copyright ©2019 BWW Media Group