PowerShell: Parallel ping test with CSV result files

I’ve been experimenting with parallel workflows in PowerShell and this seemed like a good thing to try and do with it. No guarantees that this script is the best, but it does work.

Give it a list of computers to test and it’ll ping them all at the same time and write out to a CSV file with the response time in milliseconds, one file per computer tested. If a ping is dropped it’ll give the response time as -1.

workflow PingTest{
    $Computers = "127.0.0.1","rcmtech.co.uk","bbc.co.uk","virginmedia.com"
    foreach -parallel ($Computer in $Computers){
        $Time = Get-Date
        $TestResult = Test-Connection -ComputerName $Computer -Count 1 -ErrorAction SilentlyContinue
        inlinescript{
            if ($using:TestResult.ResponseTime -eq $null){
                $ResponseTime = -1
            } else {
                $ResponseTime = $using:TestResult.ResponseTime
            }
            $ResultObject = New-Object PSObject -Property @{Time = $using:Time; Computer = $using:Computer; ResponseTime = $ResponseTime}
            Export-Csv -InputObject $ResultObject "C:\Users\Me\Desktop\ping$using:Computer.csv" -Append
        }
    }
}
Clear-Host
while($true){
    $Now = Get-Date
    Write-Host $Now "Testing..." -NoNewline
    PingTest
    Write-Host "Sleeping..."
    Start-Sleep 5
}

Description/explanation:

I wanted to try and use the parallel job processing features of PowerShell. Clearly for pinging only a few machines this is overkill, but it can make a significant difference if there were many more machine, or the task was something slightly more involved than a simple ping. In order to process in parallel you have to use a workflow, which means that strange things happen to the processing of your code – you’re transposed into Windows Workflow Foundation, which is a .Net thing. My experience (which is not much) is that some stuff works as you’d expect in PowerShell, and other stuff definitely does not.

Within the workflow, most of the code is held within a foreach loop, to which I’ve added the -parallel option. The -parallel option is only valid within a workflow, it modifies the foreach such that it runs every option at once, rather than one at a time as it would normally.

Back to the workflow. You can see that I’m able to shove a comma separated list of text into a variable, I’m also able to use Get-Date and Test-Connection and put their outputs into variables. But then for the other stuff I had issues, so resorted to using inlinescript which allows you put a chunk of “real” PowerShell into your code – it’s fired up in a separate PowerShell session, and the output is returned back into the workflow.

But because your inlinescript code is running in a separate PowerShell session you have to do something special to get access to the variables outside of the inlinescript. That special thing is to prefix the the variable name with using:, so $Time defined outside the inlinescript code block becomes $using:Time within the inlinescript. Don’t forget, as strange things will happen.

In order to handle the error that’s generated by Test-Connection when the ping fails I’ve added -ErrorAction SilentlyContinue, and then used an if statement to check if the ResponseTime property is null, and if so set a variable called $ResponseTime to -1, otherwise this variable gets set to whatever the response time actually was.

Because your code is running in parallel you’ll have to think about how to get at the output. Output to screen is “interesting”, Write-Host won’t work. You can output by just typing text or putting variables on their own on a new line, but that makes PowerShell’s already annoying text formatting even worse, especially as the order that results appear in is “random” due to the fact that the code is not being processed sequentially. Writing the output to a file can cause problems too, if one of the parallel streams has opened the file it’ll cause another to error if it tries to write at the same time. So I’m using one output file per tested machine.

In order to select the data that goes into the file I’m creating a new PSObject and specifying the property names and data. I then pass this object to Export-Csv.

I’m using export-csv as it’s an easy way to get the data into a file, and I can double-click the file to open it in Excel and then quickly see the results and/or graph them. I combined the response times into one sheet and produced this graph of my home broadband performance last night (I’m having some issues, ugh).

So, we’ve written a workflow, now we need to call it! That’s what the small while loop does down the bottom, just loops over and over again, every five seconds.

This entry was posted in PowerShell, Scripting and tagged , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s