CodeVMware

Speed up your PowerShell scripts

PowerShell is a necessary part of your toolkit for anyone working on IT infrastructure. Over time your IT environment increases in size, scripts get more dependent upon and consequently need to run much faster.

How can I  speed up my PowerShell Scripts? This is a common question, fortunately, with some simple changes to your scripts, your PowerShell execution time can be significantly improved.

Use Measure-Command

If you wrap a section of your code (or all of it) with Measure-Command, PowerShell will output the time the code block took to execute. You can also save the response to a variable and access all the timing data like so:

$Timer = Measure-Command { Get-Process }
$Timer

This will return something similar to:

Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 72
Ticks : 724272
TotalDays : 8.38277777777778E-07
TotalHours : 2.01186666666667E-05
TotalMinutes : 0.00120712
TotalSeconds : 0.0724272
TotalMilliseconds : 72.4272

Or you could simple do:

$Timer.TotalSeconds

0.0724272

Don’t get confused between $Timer.Seconds and $Timer.TotalSeconds though, the first is just the seconds element from the time. If the total time was 5 minutes and 1 second, then $Timer. Seconds would return 1. You will most likely need to use the TotalXXX options.

If you want to find out which section of your code is running slow, simply wrap those few lines with Measure-Command {} and you can see the execution time of those lines easily.

Inspect your loops

Once we have identified slow sections of code, what can we do about them?

Obviously when you loop through data, every time the loop executes, the code within it will run. If you have commands that take a long time to return, see if it’s possible to move them outside of the loop.

For example, if you are running something like the below within every iteration of a loop, it will add significant processing time, even if the loop is only running a few times.

Connect-VIServer -Server vcsa01.lab.local -Credential $MyCredential

In this example, you could connect to the vCenter server before the loop and disconnect after the loop

It may seem an obvious one, but it’s always worth inspecting your loops for time-consuming code before anything else. This is because that code is running, usually, a large number of times and causes the most delay to your script.

Avoid using Sleep Wait and Pause

Sometimes there is a reason you need to suspend code execution for exactly a given time period, but more often than not, it is unnecessary.

Take for example a scenario where you start a service and want to wait for it to have started before continuing your code.

Start-Service VSS

In this or other similar examples, you may be tempted to wait a few seconds before continuing in case the code takes a while to finish and the corresponding action to complete.

I realise that Start-Service might be a bad example here but you get the idea!

So a good thing to do, is instead of using something like:

Start-Sleep 30

which will always slow your script down by 30 seconds, it might be better to run a while loop and check if the service is started like below:

Start-Service VSS
while ((get-service VSS).Status -ne "Running"){}

This means that the script is not sleeping for any more time than required.

VMware PowerCLI Get-* commands are slow

When using 3rd party modules, if you’ve identified that those are running slow, you may need to deep-dive into why this is happening.

In my experience, the VMware PowerCLI Get-VM & Get-Host commands are actually quite slow to run, especially if your environment is large or you are running the commands many times (Within a loop for example)

When we look specifically at Get-VM, I find that it usually takes an order of magnitude or longer more time to execute than a corresponding Get-View command. This is because Get-VM is really a wrapper for:

Get-View –Viewtype VirtualMachine

Now I should say here that using the Get-View option is much faster because it’s not doing as much as Get-VM is under the hood. Get-VM will gather some information, and make it “look nice” than Get-View but if really need to speed things up, using Get-View is usually the way to go. Get-View can be made even faster by using a filter for only a particular VM or other VM attribute.

Get-View -Viewtype VirtualMachine –Filter @{Name="VM-001"}

Be aware though that using Get-View can get complex as it’s more aligned with the vSphere Web Services API than PowerCLI. For instance, determining the cluster a VM belongs to, requires several Web Services type calls rather than a simpler Get-Cluster command. But if speed is key, this extra work is what’s required.

Get-View doesn’t return your traditional PowerCLI objects, so some reading may be necessary to use it to its full extent: https://developer.vmware.com/apis/1192/vsphere

If you’re following along and have just run Get-VM on your environment, you may notice that in later PowerCLI releases, the command returns quite quickly.

But Wait!

Try actually selecting some data from Get-VM:

Get-VM | Select-Object Name, PowerState, NumCPU

Not so fast now right?

This is because Get-VM has to run first, and then the Select-Object command needs to run after.
Consider the same thing but using Get-View:

Get-View -Viewtype VirtualMachine -property Name, Summary.Runtime.PowerState, Config.NumCPU

Note: You can find all the properties for the VirtualMachine object under the vSphere Web Services API guide here

Now run this, wrapped in Measure-Command { #CodeHere } and compare its execution time to your Get-VM code sample: Get-VM | Select-Object Name, PowerState,  NumCPU

$TimerGetView = Measure-Command {Get-View -Viewtype VirtualMachine -property Name, Summary.Runtime.PowerState, Config.NumCPU}
$TimerGetVM = Measure-Command {Get-VM | Select-Object Name, PowerState, NumCPU}

$TimerGetView.TotalSeconds
$TimerGetVM.TotalSeconds

In summary, Get-View will likely be much faster to execute because it has less behind-the-scenes stuff going on than the Get-VM “Wrapper” as I like to call it. Ths same applies to most of the Get- commands including Get-Host and Get-Datastore. You can replace these with the below:

Get-View -Viewtype Datastore
Get-View -Viewtype HostSystem

Part 2

Subscribe to the mailing list here or follow me on Twitter for updates on Part 2 where we will dive into more detail and analyse more complex scenarios.

Leave a Response

This site uses Akismet to reduce spam. Learn how your comment data is processed.