Jume - My Virtualization Blog

My personal and professional virtualization blog. Everything about VMware, PowerCLI, Powershell, Agile, Scrum, VSAN and Cloud related.

Some short PowerShell tips #2

Tip 3: 

Are you using Where-Object or if/else/elseif in a loop to filter out or to process whatever you need? Consider using: Group-Object.​ 
PS H:\> Get-Help Group-Object


    Group-Object [[-Property] <Object[]>] [-NoElement] [-AsHashTable] [-AsString] [-InputObject <psobject>] [-Culture <
    string>] [-CaseSensitive]  [<CommonParameters>]


That does Group-Object do? It groups ​your array of objects together based on the same value on a property in that array. It does it very efficient and also very fast: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/group-object?view=powershell-6

For example, I want to run some actions against my PoweredOn VMs and some other actions on my PoweredOff VMs. I can group them together based on power state and run actions on it. To make it simple, I group them and only care for the amount of VMs:

PS C:\> $AllVMsGrouped = Get-VM | Group-Object -Property "PowerState"
PS C:\> $AllVMsGrouped

Count Name                      Group
----- ----                      -----
  118 PoweredOff                {…}
10441 PoweredOn                 {…}

PS C:\> 

By default, the Group-Object returns a table with the count of objects in a group, the unique name and the group (all the objects in that group). And I have the results! Now I can loop through the group, or pipe the whole group to another cmdlet.

When working with a large dataset the speed difference can be significant. Here is a script that does the same and measures the time it takes to process the different methods. Group-Object is by far the fastest.

# Connect to (all) your vCenter servers here

$TimesToRun = 5             # Select how many times to run the test
$SecondsBetweenTests = 20   # Select how long to wait before starting the next test

$IfTimeResults = @()
$WhereTimeResults = @()
$GroupingTimeResults = @()
$AdvGroupingTimeResults = @()

# Loop through TimesToRun
for ($i = 1; $i -lt $TimesToRun + 1; $i++) {
    Write-Host "Running test $($i)..."

    $VMs = Get-VM                           # Every run ensures fresh data to prevent caching

    Start-Sleep $SecondsBetweenTests        # This prevents starting the measurement before the previous command finishes
    # The if method
    $IfTime = Measure-Command {
        $PoweredOff = 0
        $PoweredOn = 0
        foreach ($VM in $VMs) {
            if ($VM.PowerState -eq 0) {$PoweredOff += 1}
            if ($VM.PowerState -eq 1) {$PoweredOn += 1}
        write-host "PoweredOff: $($PoweredOff)"
        write-host "PoweredOn: $($PoweredOn)"
    $IfTimeResults += $IfTime
    Start-Sleep $SecondsBetweenTests
    # The Where-Object method
    $WhereTime = Measure-Command {
        write-host "PoweredOff: $(($VMs | Where-Object{$_.PowerState -eq 'PoweredOff'}).count)"
        write-host "PoweredOn: $(($VMs | Where-Object{$_.PowerState -eq 'PoweredOn'}).count)"
    $WhereTimeResults += $WhereTime
    Start-Sleep $SecondsBetweenTests
    # The Group-Object method
    $GroupingTime = Measure-Command {
        $VMsGrouped = $VMs | Group-Object -Property "PowerState"
        write-host "PoweredOff: $(($VMsGrouped | Where-Object {$_.Name -eq 'PoweredOff'}).Count)"
        write-host "PoweredOn: $(($VMsGrouped | Where-Object {$_.Name -eq 'PoweredOn'}).Count)"
    $GroupingTimeResults += $GroupingTime
    Start-Sleep $SecondsBetweenTests
    # Another Group-Object method
    $AdvGroupingTime = Measure-Command {
        $AdvVMsGrouped = $VMs | Group-Object -Property "PowerState" -AsHashTable -AsString
        write-host "PoweredOff: $(($AdvVMsGrouped.PoweredOff).Count)"
        write-host "PoweredOn: $(($AdvVMsGrouped.PoweredOn).Count)"
    $AdvGroupingTimeResults += $AdvGroupingTime

# Write the result of the tests
Write-Host "IfTimeResults: $([math]::Round(($IfTimeResults | Measure-Object -Average -Property TotalMilliseconds).Average,1))"
Write-Host "WhereTimeResults: $([math]::Round(($WhereTimeResults | Measure-Object -Average -Property TotalMilliseconds).Average,1))"
Write-Host "GroupingTimeResults: $([math]::Round(($GroupingTimeResults | Measure-Object -Average -Property TotalMilliseconds).Average,1))"
Write-Host "AdvGroupingTimeResults: $([math]::Round(($AdvGroupingTimeResults | Measure-Object -Average -Property TotalMilliseconds).Average,1))" 
PS H:\> $AdvVMsGrouped = $VMs | Group-Object -Property "PowerState" -AsHashTable
PS H:\> $AdvVMsGrouped.GetEnumerator().Name | Get-Member

   TypeName: VMware.VimAutomation.ViCore.Types.V1.Inventory.PowerState

PS H:\> $AdvVMsGrouped = $VMs | Group-Object -Property "PowerState" -AsHashTable -AsString
PS H:\> $AdvVMsGrouped.GetEnumerator().Name | Get-Member

   TypeName: System.String 

And now time for the results. I ran mine against an enterprise environment with 8 vCenters, so lot's of VMs. The results are all in milliseconds:


While all give the same results, the last one is the fastest!!!

That's it for now. Thanks for reading and if you want to get updates, please subscribe. Please share this post and let me know your results.

Stay Informed

When you subscribe to the blog, we will send you an e-mail when there are new updates on the site so you wouldn't miss them.

VMUG Advantage
Some short Powershell tips #1

Related Posts



No comments made yet. Be the first to submit a comment
Tuesday, 28 November 2023