Monday, April 08, 2013

Some early lessons in PowerShell

I've been working on some practical uses of PowerShell for some testing in our lab (and as an excuse to start learning PowerShell too.) Specifically, we are testing the connection limitations of some firewalls (Cisco ASA's running in multi-context mode) and needed to use a cost effective tool (free) to make this all happen. I decided I can make all this work with iperf and use PowerShell to manage it all. So the challenge was to start an iperf session that increments the TCP port number for each instance which would also us to specifically measure the connection counts and get discrete sessions through the firewall.

I started off thinking I could use the Start-Job cmdlet to manage everything and built out the following working PowerShell:

# - use a for loop to open 100 iperf server listeners
for (; $i -le 100; $i++)
{ Start-Job -ScriptBlock { C:\users\ehorley\Downloads\java\jperf-2.0.0\bin\iperf.exe -s -p (5000 + $i) }

This sort of worked however there were some pretty big flaws that became obvious when executing it. First, was the fact that the PowerShell didn't do what I wanted, it didn't increment the iperf port number at all. I had no clue why until I started reading about global variable and scopes. It turns out that my variable wasn't passing into the ScriptBlock at all. So, after a bit of digging I figured out how to fix that and came up with:

# - just name the file iperf-servers.ps1 and you can execute it from PS by:
# - .\iperf-servers.ps1
# - starts $portcount # of iperf server sessions as background jobs
# - set the
$portcount = 1
$portcountmax = 100
for (; $portcount -le $portcountmax; $portcount++)
    { Start-Job -ScriptBlock {
    cd "C:\users\ehorley\Downloads\java\jperf-2.0.0\bin"
    ./iperf.exe -s -p (5000 + $portcount)
    } -ArgumentList $portcount

This script actually worked! It incremented the TCP port number of the iperf session and I was able to verify that with a simple netstat -an -p TCP and I was able to remove all the jobs started by the script in a rather brute force as with:

Get-Job | Remove-Job -force

There were some big issues with this method though. Because I was using the start-job cmdlet PowerShell was starting a new PowerShell instance for each job that it runs. In addition, it is starting a cmd.exe process and an iperf.exe process which resulted in a lot of memory being consumed for a very small number of TCP ports being opened. In our testing on a Windows 7 client VM with 4GB of memory we could not push much past 150 to 200 jobs, really not very useful when you want to test 20,000 concurrent connections through a firewall.

So it was back to the drawing board to figure out a better way to do this. I decided that the jobs cmdlets was not the way to go and I had to figure out a more reasonable way that did not start multiple PowerShell sessions but instead just ran from the single PowerShell session but ran all the cmd.exe and iperf.exe processes.

After awhile I figured out that I could use the Start-Process cmdlet instead and came up with:

# - just name the file iperf-servers-2.ps1 and you can execute it from PS by:
# - .\iperf-servers-2.ps1
# - starts $portcount # of iperf server sessions as background jobs
# - set the $portcountmax variable to the high end of sessions
# - correct the path to iperf executable before running
# - modify the @iperfargs to match client or server role
# - to see all the jobs run:
# - get-process iperf
# - to kill all the sessions:
# - get-process iperf | stop-process
$portcount = 1
$portcountmax = 1000
for (; $portcount -le $portcountmax; $portcount++)
    $iperfargs = "-s -p " + (5000 + $portcount)
    # $iperfargs = "-c <host ip> -t -p " + (5000 + $portcount) - or something like that for the client
    Start-Process -windowstyle Hidden -WorkingDirectory "C:\users\ehorley\Downloads\java\jperf-2.0.0\bin\" .\iperf.exe $iperfargs

This script worked as expected and I was able to execute 1,000 sessions and confirm the TCP ports on my laptop without any issue. As is indicated in the script you could confirm the processes are running with:

Get-Process iperf


and you could kill all the sessions with:

Get-Process iperf | Stop-Process

Clearly there is a lot of work that could be done to automate the process to do this even more efficiently and perhaps I will do that but the scripts were functional enough for our engineers to use to do the testing. The Windows 7 VM's with 4GB of RAM were able to run over 1,750 TCP ports each which allowed for us to build out a reasonable test farm in our lab of virtual machines and test the firewalls.

Yes, I know I could likely do this easier with a simple javac or even just use linux and some perl or python but remember I was using the opportunity to learn PowerShell! I was grateful for having the incredibly useful book from Don Jones and Jeff Hicks - Learn Windows PowerShell 3 in a Month of Lunches, Second Edition by Manning. If you are at all interested in learning PowerShell I highly recommend it.
- Ed


wordpress plugins said...
This comment has been removed by a blog administrator.
D Rinker said...

Can this script be ran on a Windows 7 machine using Powershell. If so, what modifications are needed?

D. Rinker said...

I got it working! I had to first set the executionpolicy to unrestricted in order to run.