25 September 2014

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…


There are no comments.



You must be logged in to post a comment.