It’s funny how I can write code and then a long time afterwards need to write something for the same goal and come up with it in an easier way. It makes sense, since I’ve likely learned more and grown in the time that has passed. Still, to look back on the old cold and see how I fumbled through it or took a very long time to accomplish it and then do it in just a few lines makes me chuckle.
Take, for instance, NetStat. Now, I know there are plenty of functions and cmdlets out there that other folks have written to get you “objectized” results for NetStat. I’m sure they work well but I wouldn’t be who I am if I didn’t write my own code to do something. Lately I’ve been guilty of using the cmdlets from Citrix and VMWare to interact with their items but for the sake of practicality, I have to. Anyway… I’m troubleshooting slowness reports with one of our Citrix PVS environments but one of the problems is I don’t get told until hours afterwards and/or no one is willing to troubleshoot on the spot. I can’t do anything if I can’t get data. So, I needed to write a script that did not require a lot of resources and could run on the servers at the same time the users were using them at the heaviest time of the day – without impacting them. At the time of this writing it hasn’t run for the first time yet out in the live environment, so after it does (and it’s successful *fingers crossed*) I may just create a post for the whole script.
Part of the script is collecting latency data. I’m using a standard ping with the default packet size, doing an average of three pings and reporting back. The piece that I wrote anew, despite having code already, was getting all of the connection data so that I could ping each connection and find latency. Here’s what I do first:
$netstat = (netstat -ano) | where { ($_ -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") -and ($_ -match "TCP") }
I’m pulling net stat and then only selecting the lines that 1) match a 4 octet IPv4 address and 2) are using TCP. You can always further qualify it by adding a “-notmatch” and putting in any IP address you don’t want like local loopback stuff or all the 0.0.0.0. I get something like this (I’m using just a sample of the data off a vm):
TCP 127.0.0.1:49160 127.0.0.1:49161 ESTABLISHED 2232 TCP 127.0.0.1:49161 127.0.0.1:49160 ESTABLISHED 2232 TCP 127.0.0.1:49162 127.0.0.1:49163 ESTABLISHED 2232 TCP 127.0.0.1:49163 127.0.0.1:49162 ESTABLISHED 2232 TCP 127.0.0.1:49164 127.0.0.1:49165 ESTABLISHED 2232 TCP 127.0.0.1:49165 127.0.0.1:49164 ESTABLISHED 2232 TCP 127.0.0.1:49172 127.0.0.1:49173 ESTABLISHED 2232 TCP 127.0.0.1:49173 127.0.0.1:49172 ESTABLISHED 2232 TCP 127.0.0.1:49174 127.0.0.1:49175 ESTABLISHED 2232 TCP 127.0.0.1:49175 127.0.0.1:49174 ESTABLISHED 2232 TCP 127.0.0.1:49176 127.0.0.1:49177 ESTABLISHED 2232 TCP 127.0.0.1:49177 127.0.0.1:49176 ESTABLISHED 2232 TCP 127.0.0.1:49178 127.0.0.1:49179 ESTABLISHED 2232 TCP 127.0.0.1:49179 127.0.0.1:49178 ESTABLISHED 2232 TCP 127.0.0.1:49185 127.0.0.1:65001 ESTABLISHED 2060 TCP 127.0.0.1:55460 127.0.0.1:27015 ESTABLISHED 11792 TCP 127.0.0.1:55462 127.0.0.1:5354 ESTABLISHED 11792 TCP 127.0.0.1:55466 127.0.0.1:5354 ESTABLISHED 11792 TCP 127.0.0.1:55467 127.0.0.1:5354 ESTABLISHED 11792 TCP 127.0.0.1:55469 127.0.0.1:55470 ESTABLISHED 13388
Then, to pull out the “Foreign” address (the remote machine’s IP) I do some splitting and replacing of each line to widdle it down to just the IP address:
ForEach ($rec in $netstat) { $foreignAddress = (($rec.split(" ") | where { $_ -match "\d{1,3}\." } )[1].Split(":"))[0]
As I go through each line (which is just a string of text) I first split it by spaces, which is the part of the code up to the first | (pipe) which ends up looking like this:
TCP 127.0.0.1:1120 127.0.0.1:59964 LISTENING 16932
It looks like a mess but it’s made it a lot simpler. Now each non-blank string of text is on it’s own line, and thus, it has an index. But, I can further widdle this down by only matching those items that begin with an octet. That’s where I pipe part one above into the “where” clause and get:
127.0.0.1:1120 127.0.0.1:59964
Awesome. So I know that the foreign address is always the second one (index “1”) but there’s also that port on there which will break the ping attempt so we need to take that off. That’s where the rest of the code at the end of the “where” clause comes in. I tell it I want index 1 ([1]), which is the line “127.0.0.1:59964” and then I split by the “:” which produces two lines, “127.0.0.1” and “59964” I only want the IP and that is index “0” and so my line ends with a [0]. Note that each step I’ve explained above is in it’s own set of parenthesis.
I know I didn’t do the best job of explaining the breakdown but hopefully it has helped make sense of nesting commands with PowerShell and been an example of how powerful piping and one line of code can be. My overall code here is 3 lines but it took a mess of a string and brought it down to the exact piece of data that I wanted. I think that’s pretty darn cool. For anyone wishing to take this a step further and make their own “objectized” version of NetStat or another text-output tool you should be able to go from here using this. Maybe a quick google search on how to eliminate blank lines from a multi-line string, too. I know I have that code somewhere…. If I were smart I’d do the same, that way I can reuse the function no matter which individual piece of data in the results I was looking for and not had to modify anything…
You must be logged in to post a comment.