Description:

This script calculates the free and used disk space for all logical drives inside your vm’s and combines this data with free/used space on your datastores.

Code:

#Get VMware Disk Usage
# Created by Hugo Peeters
# http://www.peetersonline.nl
 
# VARIABLES
$Decimals = 1
$VCServer = “MYVCSERVER”
 
# SCRIPT
# Connect to VC
Write-Progress “Gathering Information” “Connecting to Virtual Center” -Id 0
$VC = Connect-VIServer $VCServer
# Create Output Collection
$myCol = @()
# List Datastores (Datastore Name)
Write-Progress “Gathering Information” “Listing Datastores” -Id 0
$Datastores = Get-Datastore | Sort Name
# List vms
Write-Progress “Gathering Information” “Listing VMs and Disk Files” -Id 0
$VMSummaries = @()
ForEach ($vm in (Get-VM))
{
$VMView = $VM | Get-View
ForEach ($VirtualSCSIController in ($VMView.Config.Hardware.Device | Where {$_.DeviceInfo.Label -match “SCSI Controller”}))
{
ForEach ($VirtualDiskDevice in ($VMView.Config.Hardware.Device | Where {$_.ControllerKey -eq $VirtualSCSIController.Key}))
{
$VMSummary = “” | Select VM, HostName, PowerState, DiskFile, DiskName, DiskSize, SCSIController, SCSITarget
$VMSummary.VM = $VM.Name
$VMSummary.HostName = $VMView.Guest.HostName
$VMSummary.PowerState = $VM.PowerState
$VMSummary.DiskFile = $VirtualDiskDevice.Backing.FileName
$VMSummary.DiskName = $VirtualDiskDevice.DeviceInfo.Label
$VMSummary.DiskSize = $VirtualDiskDevice.CapacityInKB * 1KB
$VMSummary.SCSIController = $VirtualSCSIController.BusNumber
$VMSummary.SCSITarget = $VirtualDiskDevice.UnitNumber
$VMSummaries += $VMSummary
}
}
Clear-Variable VMView -ErrorAction SilentlyContinue
}
# Loop through Datastores
ForEach ($Datastore in $Datastores)
{
# List vmdk files in datastore (vmdk Name)
Write-Progress “Gathering Information” (”Processing Datastore {0}-f $Datastore.Name) -Id 0
$DSView = $Datastore | Get-View
$fileQueryFlags = New-Object VMware.Vim.FileQueryFlags
$fileQueryFlags.FileSize = $true
$fileQueryFlags.FileType = $true
$fileQueryFlags.Modification = $true
$searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
$searchSpec.details = $fileQueryFlags
$searchSpec.sortFoldersFirst = $true
$dsBrowser = Get-View $DSView.browser
$rootPath =["+$DSView.summary.Name+"]$searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
ForEach ($result in $searchResult)
{
ForEach ($vmdk in ($result.File | ?{$_.Path -like*.vmdk”} | Sort Path))
{
Write-Progress “Gathering Information” (”Processing VMDK {0}-f $vmdk.Path) -Id 1
Write-Host==============================================================================# Find vm using the vmdk (VM Name)
$VMRef = ($VMSummaries | ?{$_.DiskFile -match $Datastore.Name -and $_.DiskFile -match $vmdk.Path})
“VMDK {0} belongs to VM {1}-f $vmdk.Path, $VMRef.VM
If ($VMRef.Powerstate -eq “PoweredOn”)
{
Write-Host “VM is powered on” -ForegroundColor “yellow”
$Partitions = Get-WmiObject -Class Win32_DiskPartition -ComputerName $VMRef.HostName
If ($?)
{
$Disks = Get-WmiObject -Class Win32_DiskDrive -ComputerName $VMRef.HostName
$LogicalDisks = Get-WmiObject -Class Win32_LogicalDisk -ComputerName $VMRef.HostName
$DiskToPartition = Get-WmiObject -Class Win32_DiskDriveToDiskPartition -ComputerName $VMRef.HostName
$LogicalDiskToPartition = Get-WmiObject -Class Win32_LogicalDiskToPartition -ComputerName $VMRef.HostName
Write-Host “Read partition and disk information” -ForegroundColor “yellow”
# Match disk based on SCSI ID’s
$DiskMatch = $Disks | ?{($_.SCSIPort - 1) -eq $VMRef.SCSIController -and $_.SCSITargetID -eq $VMRef.SCSITarget}
If ($DiskMatch -eq $null){Write-Warning “NO MATCHES!}
Else
{
Write-Host “Found match:” -ForegroundColor “yellow”
$DiskMatch
# Find the Partition(s) on this disk
$PartitionsOnDisk = ($DiskToPartition | ?{$_.Antecedent -eq $DiskMatch.__PATH})
If ($PartitionsOnDisk -eq $null){Write-Warning “NO PARTITIONS!}
Else
{
ForEach ($PartitionOnDisk in $PartitionsOnDisk)
{
Write-Host “Disk contains partition” -ForegroundColor “yellow”
$PartitionOnDisk.Dependent
$PartitionMatches = $Partitions | ?{$_.__PATH -eq $PartitionOnDisk.Dependent}
ForEach ($PartitionMatch in $PartitionMatches)
{
$LogicalDiskRefs = $LogicalDiskToPartition | ?{$_.Antecedent -eq $PartitionMatch.__PATH}
If ($LogicalDiskRefs -eq $null)
{
Write-Warning “NO LOGICAL DISKS!}
Else
{
ForEach ($LogicalDiskRef in $LogicalDiskRefs)
{
$LogicalDiskMatches = $LogicalDisks | ?{$_.__PATH -eq $LogicalDiskRef.Dependent}
ForEach ($LogicalDiskMatch in $LogicalDiskMatches)
{
Write-Host “Matching Logical Disk:” -ForegroundColor “yellow”
$LogicalDiskMatch
# Create Output Object
$myObj = “” | Select Datastore, DSSizeGB, DSFreeGB, DSPercentFree, DiskFile, VM, HardDisk, DriveLetter, DiskSizeGB, DiskFreeGB, PercFree
# List datastore name
$myObj.Datastore = $Datastore.Name
# Determine datastore size in GB
$myObj.DSSizeGB = [Math]::Round(($Datastore.CapacityMB * 1MB / 1GB),$Decimals)
$myObj.DSFreeGB = [Math]::Round(($Datastore.FreeSpaceMB * 1MB / 1GB),$Decimals)
# Determine datastore free space (DS%Free)
$myObj.DSPercentFree = [Math]::Round((100*($Datastore.FreeSpaceMB/$Datastore.CapacityMB)),$Decimals)
# List disk file name
$myObj.DiskFile = $vmdk.Path
# List VM Name
$myObj.VM = $VMRef.VM
# Determine virtual hard disk / logical drive
$myObj.HardDisk = $VMRef.DiskName
# Report driveletter
$myObj.DriveLetter = $LogicalDiskMatch.DeviceID
# Report Size
$myObj.DiskSizeGB = [Math]::Round(($LogicalDiskMatch.Size / 1GB),$Decimals)
# Report Free Space
$myObj.DiskFreeGB = [Math]::Round(($LogicalDiskMatch.FreeSpace / 1GB),$Decimals)
# Calculate Percentage free space
$myObj.PercFree = [Math]::Round((100 * ([int]($LogicalDiskMatch.FreeSpace / 1MB) / [int]($LogicalDiskMatch.Size / 1MB))),$Decimals)
Write-Host “RESULT:” -ForegroundColor “yellow”
$myObj
# Add output object to output collection
$myCol += $myObj
}
Clear-Variable LogicalDiskMatches -ErrorAction SilentlyContinue
}
}
Clear-Variable LogicalDiskRefs -ErrorAction SilentlyContinue
}
Clear-Variable PartitionMatches -ErrorAction SilentlyContinue
}
}
Clear-Variable PartitionsOnDisk -ErrorAction SilentlyContinue
}
Clear-Variable DiskMatch -ErrorAction SilentlyContinue
Clear-Variable Disks -ErrorAction SilentlyContinue
Clear-Variable LogicalDisks -ErrorAction SilentlyContinue
Clear-Variable DiskToPartition -ErrorAction SilentlyContinue
Clear-Variable LogicalDiskToPartition -ErrorAction SilentlyContinue
}
Clear-Variable Partitions -ErrorAction SilentlyContinue
}
Else
{
Write-Host “VM is powered off” -ForegroundColor “yellow”
}
Clear-Variable VMRef -ErrorAction SilentlyContinue
Write-Progress “Gathering Information” (”Processing VMDK {0}-f $vmdk.Path) -Id 1 -Completed
}
}
}
# Disconnect from VC
Disconnect-VIServer -Confirm:$False
 
# OUTPUT
Write-Host===================================================Write-Host===================================================$TotalDSFree = ($myCol | Select Datastore, DSFreeGB -Unique | Measure-Object DSFreeGB -Sum).Sum
$TotalDSSize = ($myCol | Select Datastore, DSSizeGB -Unique | Measure-Object DSSizeGB -Sum).Sum
$AverageDSFree = [Math]::Round(100 * ($TotalDSFree / $TotalDSSize),$Decimals)
$AverageDiskFree = [Math]::Round(100 * (($myCol | Measure-Object DiskFreeGB -Sum).Sum / ($myCol | Measure-Object DiskSizeGB -Sum).Sum),$Decimals)
Write-Host “Total DS Free: $TotalDSFreeWrite-Host “Total DS Size: $TotalDSSizeWrite-Host “Average DS Free Percentage: $AverageDSFreeWrite-Host “Average Disk Free Percentage: $AverageDiskFree$myCol | Export-Csv -NoTypeInformation ‘D:\scripts\VMwareDiskUsage.csv’

Download:
get-vmdiskusage (rename to .ps1)

Disclaimer:

All the scripts on PeetersOnline.nl are published under a Creative Commons license, which means you should refer to me if you want to republish (pieces of) them. Thank you. More information about Creative Commons can be found here: Creative Commons Attribution 3.0 Netherlands License.

Creative Commons License
 

 

 

26 Responses to Get-VMDiskUsage

  1. ckotte says:

    Hi,
    is it possible to include also the disks of VMs which are offline, which have LINUX installed and which are in other domains?
    Thanks.
    Regards,
    Christian

    • admin says:

      Hi Christian,

      Off line VMs is not possible, as you need to access the client OS the retrieve the disk information. Linux might be possible via the VMware Tools. Try searching the VMware VI Toolkit Communities for an answer. For conencting to VMs in other domains, take a look at the *-VICredentialStore cmdlets. They allow for storing and retrieving credentials for use within scripts.

      Hope this helps,
      Hugo

      »crosslinked«

  2. ckotte says:

    @admin
    Would it be possible to include only the vmdk disk size information for such VMS? Otherwise, I have to completely rewrite the script since the loop continues if the VM is powered of or if WMI is not available.

  3. ckotte says:

    I meant to say that you can rewrite the script if it’s possible?

  4. admin says:

    @ckotte
    You could add the vmdk file size (DiskFileSize) to the script:
    # Create Output Object
    $myObj = “” | Select Datastore, DSSizeGB, DSFreeGB, DSPercentFree, DiskFile, DiskFileSizeGB, VM, HardDisk, DriveLetter, DiskSizeGB, DiskFreeGB, PercFree

    # List disk file name
    $myObj.DiskFile = $vmdk.Path
    # List disk size
    $myObj.DiskFileSizeGB = [math]::Round(($vmdk.FileSize / 1GB),$decimals)

  5. Matt Hentov says:

    Just a small quibble.

    Odd that you would publish your scripts under Creative Commons yet not post them for download as pure ASCII.

    Thanks for solving this problem for us. I thought I’d need to purchase some expensive third party tool to get this data.

    Many thanks again.

    - Matt

  6. Nicholas Carde says:

    Hello,

    I’m getting the following error when the script attempts to create the .csv file (the output directory location exists) — thank you for your help!

    Total DS Free: 1934.4
    Total DS Size: 14284.6
    Average DS Free Percentage: 13.5
    Average Disk Free Percentage: 23.2
    Export-Csv : Cannot bind argument to parameter ‘Path’ because it is null.
    At D:\Temp\powershell\scripts\get-vmdiskusage.ps1:181 char:20
    + $myCol | Export-Csv <<<< -NoTypeInformation $ouputfile

  7. Nicholas Carde says:

    Thank you, Hugo!

    In the download script link above there is a typo in the export-csv section (calls $ouputfile instead of $outputfile as declared on Line 8).

  8. GoldenArm says:

    I am getting this error when I run this code :

    Exception calling “SearchDatastoreSubFolders” with 2 arguments: Permission to perform this operation was denied”

    Could you tell me why …?

    Thanks in Advance
    _M

  9. GoldenArm says:

    THanks. Found out that already. I referred the “visdk25programmingguide.pdf” document….see the last line on page 184 of this book. Which has the details.

    Thanks
    -M

    • GoldenArm says:

      Hi Admin,

      After fixing the privelege issue when I ran the code….I get a different error….Get-WmiObject : Access is denied. (Exception from HRESULT: 0×80070005 (E_ACCESSDENIED))…could you tell me what needs to be done to get rid of this?

      Thanks
      -M

  10. Ancksu says:

    Hi Admi,
    Your script is exactly what I need, but I have error message. Can you please help, me ?

    Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
    At C:\temp\disk.ps1:70 char:28
    + $Partitions = Get-WmiObject <<<< -Class Win32_DiskPartition -ComputerName $V
    MRef.HostName
    + CategoryInfo : InvalidOperation: (:) [Get-WmiObject], COMExcept
    ion
    + FullyQualifiedErrorId : GetWMICOMException,Microsoft.PowerShell.Commands
    .GetWmiObjectCommand

    Thanks in advance

  11. admin says:

    Ancksu,
    The error is: RPC server is unavailable. This means you could not connect to one of the servers. It might be powered off or running linux. You can try adding $erroractionpreference = “SilentlyContinue” to the start of the script.
    Hugo

  12. Ancksu says:

    Thanks Hugo,
    I did it but the problem is I only get 3 on 30 VM.
    In the 30 VM one is a linux so normal, but all the other VM are Windows and are power on.
    Do you have any idea ?

  13. Ancksu says:

    I solved the problem. In fact it was a naming problem.
    By replacing $VMRef.HostName by $VMRef.VM and that work perfectly.
    thanks

  14. Roland says:

    Hi Hugo

    When I run your script in PowerCLI it returns all VMs to be offline and therefore it will not output any information. But in fact all my VMs (almost all Windows machines) are running and have up to date VM tools. I’m running vSphere/vCenter 4.0 U1, what am I doing wrong?

    Cheers
    Roland

  15. Sabbir says:

    Hi Hugo

    When I run your script in PowerCLI it ruturens most of the VMs information except few of them. The script stop running and no error shows like it hung.

    Cheers
    Sabbir

  16. Molotsi says:

    Hi Hugo

    This is the error i am receiving.

    ps1:57 char:53
    + $searchResult = $dsBrowser.SearchDatastoreSubFolders( <<<< $rootPath, $search
    Spec)
    Exception calling "SearchDatastoreSubFolders" with "2" argument(s): "Not initia
    lized: boolean fileOwner"
    At C:\Program Files\VMware\Infrastructure\vSphere PowerCLI\Scripts\vDiskUseSRD.
    ps1:57 char:53
    + $searchResult = $dsBrowser.SearchDatastoreSubFolders( <<<< $rootPath, $search
    Spec)
    Exception calling "SearchDatastoreSubFolders" with "2" argument(s): "Not initia
    lized: boolean fileOwner"
    At C:\Program Files\VMware\Infrastructure\vSphere
    PowerCLI\Scripts\vDiskUseSRD.

  17. Chris Millian says:

    Hi Hugo,

    Thank you for this wonderful script:-)
    I have just one question.
    When I run the scrip there are some lines mentioning in yellow:
    VM is powered on
    Read partition and disk information
    Warning: NO MATCHES!
    What does this mean?
    Whe have over 300 VDIs and 40 Servers and the output gives me about 60 VDIs.
    Is there a logical explanation for this?

    Thanks in advanced

  18. Erik Stottelaar says:

    Hi Hugo,

    Thanks for publishing this script but can i skip my 2 linux vm’s because when i run the script it fails on my 2 linux vms at the wmi function. And is it possible to run it for a specified cluster in my vcenter?

    thanks in advanced,

    Erik

  19. admin says:

    Erik,
    To make sure you run it against only Windows VM’s, replace this:
    ForEach ($vm in (Get-VM))
    With this:
    ForEach ($vm in (Get-VM | Where {$_.Guest.OSFullName -match “Windows”}))

    To restrict the script to a single cluster, use this instead:
    ForEach ($vm in (Get-VM -Location (Get-Cluster “myCluster”)))

    Hugo

  20. Erik Stottelaar says:

    Hi Hugo,

    Thanks a lot for the quick reponse. Your script is working fine now !

    regards,

    Erik

  21. jim kunysz says:

    i ran the script in our environment, over 60 esx hosts, and 1200 guests.
    It spit out 6 rows of data. 6 datastores and 6 vm’s. wout have erroring on the linux guests have prevented data wrom being exported?

  22. David Dionne says:

    FWIW, `du -hs` will list the actual amount of space used in a vmdk regardless of what OS it’s running or if it’s online or offline….but it only works for the vmdk, it can’t discriminate partitions on a multi partition vmdk.

    I have a vm with a 1TB thin provisioned disk. The `ls` command will show it as a 1TB file, the `du` command will show how much of that 1TB is actually being used.

    Example:

    ~ # ls -alh /vmfs/volumes/OF/vcha1/
    drwxr-xr-x 1 root root 560 Jan 19 12:25 .
    drwxr-xr-t 1 root root 12.3K Jan 19 11:34 ..
    -rw——- 1 root root 1000.0G Jan 19 12:25 vcha1-flat.vmdk
    -rw——- 1 root root 545 Jan 19 12:25 vcha1.vmdk
    ~ # du -hs /vmfs/volumes/OF/vcha1/*
    17.1G /vmfs/volumes/OF/vcha1/vcha1-flat.vmdk
    0 /vmfs/volumes/OF/vcha1/vcha1.vmdk

    Should be pretty easy to work into a script (perl or powershell).