PowerCLI: Discover Idle VMs

There are a number of operations management tools available which can allow you to discover virtual machines which can be categorised as idle, where they continue to run  with a low utilisation profile and can be part of the overall VM sprawl in your environment.

I was recently investigating how to retrieve Idle VMs based on their CPU, Disk and Network usage by querying statistical information from vCenter, this was all possible using PowerCLI and math functions within Powershell.

Firstly we will connect to the VI server and  retrieve a collection of VMs and filter these to only return VMs where the Power State is equal to ‘PoweredOn’.

Connect-VIServer server1.domain.local
$VMs = Get-VM | Where-Object {$_.PowerState -eq "PoweredOn"} 

For each virtual machine retrieved in the collection we will want to retrieve statistical information for the following stats:

  • cpu.usagemhz.average
  • disk.usage.average
  • net.usage.average

For each stat retrieved we will want to use a date range where the start date is thirty days in the past and the finish date is the current date.  For Each metric retrieved we will count the number of objects in total and count the number of objects returned where the value is less or equal that specified  and then use the match function within Powershell to calculate this as a percentage.

In this example, the following thresholds were used:

Stat Unit Value
cpu.usagemhz.average Mhz 100
disk.usage.average KBps 20
net.usage.average KBps 1
$Output =ForEach ($VM in $VMs) 
    $CPUStat = Get-Stat -Entity $VM.Name -Stat cpu.usagemhz.average -Start (Get-Date).AddDays(-30) -Finish (Get-Date) 
    $CPUIdle = $CPUStat | Where-Object {$_.Value -le "100"} 
    $CPUDetection = ($CPUIdle.Count / $CPUStat.Count) *100 
    $DiskStat = Get-Stat -Entity $VM.Name -Stat disk.usage.average -Start (Get-Date).AddDays(-30) -Finish (Get-Date) 
    $DiskIdle = $DiskStat | Where-Object {$_.Value -le "20"} 
    $DiskDetection = ($DiskIdle.Count / $DiskStat.Count) * 100

    $NetworkStat = Get-Stat-Entity $VM.Name-Stat net.usage.average -Start (Get-Date).AddDays(-30) -Finish (Get-Date) 
    $NetworkIdle = $NetworkStat | Where-Object {$_.Value -le "1"} 
    $NetworkDetection = ($NetworkIdle.Count / $NetworkStat.Count) * 100

Now that we have retrieved the statistical information and calculated the percentage of samples that were less or equal to the  threshold value we will identity VMs that are believed to be idle by specifying that 90% of the returned samples were below the threashold limit for each stat retrieved and output this information to include the VM Name and the average value of each metric for the date range.

If ($CPUDetection -ge "90"-and $DiskDetection -ge "90"-and $NetworkDetection -ge "90")
        "" | Select @{N="Name";E={$VM.Name}},
        @{N="CPU Usage (Mhz)";E={[Math]::Truncate(($CPUStat.Value | Measure-Object-Average).Average)}},
        @{N="Disk I/O Usage (KBps)";E={[Math]::Truncate(($DiskStat.Value | Measure-Object-Average).Average)}},
        @{N="Network I/O Usage (KBps)";E={[Math]::Truncate(($NetworkStat.Value | Measure-Object-Average).Average)}}
$Output | Export-Csv -Path D:\Output\IdleVMS.csv -NoTypeInformation

The above can be downloaded in full from the below, where the values can be modified to meet your requirements, the default is to use the above values as above.


The script can be run as below:

 ./Get-IdleVMs.ps1 -CpuMhz 200 -DiskIO 15 -NetworkIO 2 -Percentage 85 -Days 10 -vCenter server1.domain.local

3 thoughts on “PowerCLI: Discover Idle VMs

  1. this didnt work for me throw error “Export-Csv : A parameter cannot be found that matches parameter name ‘Pathe’.
    At D:\SYDABR\VMware Scripts\Get-IdleVMs.ps1:37 char:27
    + $Output|Export-Csv -Pathe: <<<< \idlevms.csv -NoTypeInformation
    + CategoryInfo : InvalidArgument: (:) [Export-Csv], ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.ExportCsvCommand


  2. Export-Csv : Cannot bind argument to parameter ‘InputObject’ because it is null.
    At D:\SYDABR\VMware Scripts\Get-IdleVMs.ps1:37 char:19
    + $Output|Export-Csv <<<< -Path E:\idlevms.csv -NoTypeInformation
    + CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand


  3. I keep getting an error:

    Param ([string] $CPUMhz = “100”, [string] $DiskIO = “20”, [string] $NetworkIO = …
    + ~~~~~
    The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept
    assignments, such as a variable or a property.


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 )

Connecting to %s