Dec/0840
Get VMware Disk Usage with Powershell
Using VMware seriously requires a lot of (shared) storage. This kind of storage (on a SAN for instance) is quite expensive. So you might want to check if you are wasting a lot of this space. When you look at the storage in VMware, it consists of multiple abstraction layers. A virtual machine has one or more Logical Disks, which are indicated by driveletters. You can use WMI to determine the amount of used and free space (Win32_LogicalDisk). One or more logical disks are contained in a partition. One or more partitions reside on a physical disk. That physical disk is really a virtual disk, a vmdk file to be precise. One or more vmdk files reside in a Datastore, which can be found on a LUN on your SAN.
The following script enumerates most of these layers (from logical disk to datastore) and calculates the used and free space. The final line exports the results to a csv file for use in Excel. And the script also helps you to calculate the average free space by showing the totals without the duplicates (don’t try to average the averages in excel, that’s not accurate because datastores contain duplicates and averages should be weighed).
UPDATE: I have modified the script, so no more matching of disks is done based on disk size. The match is made based on SCSI IDs and WMI relations. Thanks to adavidm on the VI Toolkit Community
#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: $TotalDSFree”
Write-Host “Total DS Size: $TotalDSSize”
Write-Host “Average DS Free Percentage: $AverageDSFree”
Write-Host “Average Disk Free Percentage: $AverageDiskFree”
$myCol | Export-Csv -NoTypeInformation ‘P:\#TEMP\VMwareDiskUsage.csv’
Related posts:
16:13 on December 10th, 2008
Great stuff, I will try this on the Eval of ESX 3.5 U3 I am trying.
Linked to your post on my blog
Roger L
http://rogerlunditblog.blogspot.com/
11:42 on December 11th, 2008
Hi Hugo,
I got an error on this one, something to do with the “-f” any ideas?
Error formatting a string: Input string was not in a correct format..
At :line:46 char:70
+ Write-Progress “Gathering Information” (”Processing Datastore {0}” -f <<<< $Datastore.Name) -Id 0
BTW
Love the site and the scripts, excellent work:)
JoeL
11:44 on December 11th, 2008
Dear Hugo!
Very nice script! I have the following problem:
I would like to have a script which sents my an alarm by email if a LUN has for example only 15% of free space.
Could you write anything like that?
That would be very nice and I think a lot of VI3 admins need that.
Greetings
Simon
13:00 on December 11th, 2008
@JoeL
I think you might have a datastore with special characters in it. You can try to avoid the problem by using:
$DatastoreName = $Datastore.Name
Write-Progress “Gathering Info” “Processing $DatastoreName” -Id 0
Clear-Variable $DatastoreName -ErrorAction SilentlyContinue
Let me know if that helps.
Hugo
13:08 on December 11th, 2008
@Simon
Try this:
$VCServerName = “MYVCSERVER”
$Decimals = 0
$Limit = 15
$From = “no@spam.mow”
$To = “SomeOne@Foo.com”
$Title = “Datastore nearly full!”
$VC = Connect-VIServer $VCServerName
$Datastores = Get-Datastore
ForEach ($Datastore in $Datastores)
{
$PercentFree = [math]::Round(((100*($Datastore.FreeSpaceMB / $Datastore.CapacityMB )),$Decimals)
If ($PercentFree -lt $Limit)
{
$Body = “Datastore {0} has {1} percent free space.” -f $Datastore.Name, $PercentFree
# Mail part copied from http://mow001.blogspot.com/2005/11/sending-mail-from-msh.html
$SmtpServer = “smtp.Server.com”
$SmtpClient = new-object system.net.mail.smtpClient
$SmtpClient.host = $SmtpServer
}
}
4:50 on December 15th, 2008
When I run this script, I got the following error, can you check that?
VMDK AV_DEV.vmdk belongs to VM AV_DEV
VM is powered on
Get-WmiObject : RPC server is unavailable (Exception from HRESULT: 0×800706BA)
At line:50 char:28
+ $Partitions = Get-WmiObject <<<< -Class Win32_DiskPartition -ComputerName $V
MRef.HostName
7:31 on December 15th, 2008
@Jack
That’s a Windows / Networking issue. You cannot reach that server through RPC (Remote Procedure Call). Check network connectivity and possibly DCOM permissions. Lots of info on Google.
Hope that helps you out.
Hugo
23:34 on December 15th, 2008
Nice script, and just a couple of comments.
To get around the non-connectivity errors – this script could be modified to do this via sql, since VirtualCenter stores all the VM free/used/capacity, as well as Datastore free/used/capacity in a simple table structure for any client with VMtools installed.
Alternatively, we could trim the ‘dns’ name off the VMname and do a lookup to determine credentials to connect to the remote machine with a default local account. I think I will probably head down this path myself :)
Sometimes disks are on local VMFS volumes and you don’t want this as it will throw the stats out. It would nice to have an opt-out to exclude VMs with disks on ‘local’ storage. But this is pretty trivial.
Cheers.
7:58 on December 16th, 2008
Simon, Thanks so much for your comments. I’ll see what I can do to improve the script. If you want a quick improvement, check out VCPlus here: http://www.run-virtual.com/?page_id=184. It runs as a service and dumps the results in custom fields, so they are visible within the VI Client.
14:43 on December 18th, 2008
error :
Error formatting a string: Input string was not in a correct format..
At :line:46 char:70
+ Write-Progress “Gathering Information” (”Processing Datastore {0}” -f <<<< $Datastore.Name) -Id 0
the datastore name is quite long: “GL-dldn0534nsd-rg9-034-templates” will the script cater for that ?.
Regards
14:54 on December 18th, 2008
sorry – looks like i have same problem as Joel. Your extra code fixed that but now get :
Error formatting a string: Input string was not in a correct format..
At :line:65 char:65
+ Write-Progress “Gathering Information” (”Processing VMDK {0}” -f <<<< $vmdk.Path) -Id 1
$vmdk.path has a couple of “-” in it, could this be the problem ?.
16:58 on December 23rd, 2008
What about a section to exclude certain datastores?
8:12 on December 24th, 2008
@Ryan
You could modify this part:
# List Datastores (Datastore Name)
Write-Progress “Gathering Information” “Listing Datastores” -Id 0
$Datastores = Get-Datastore | Sort Name
Into something like this:
# List Datastores (Datastore Name)
Write-Progress “Gathering Information” “Listing Datastores” -Id 0
$exclusions = “Datastore1″, “Datastore2″
$Datastores = Get-Datastore | Where {$exclusions -notcontains $_.Name} | Sort Name
18:03 on December 29th, 2008
@Simon
Here’s a script to do what you are looking for:
New-PSDrive -Name PS -Root T:\scripting\ps -PSProvider filesystem
Set-Location PS:
Connect-VIServer -Server pni-dvsysmgt01.pni.com -User administrator
$dir = “c:\TEMP”
$outfile = “c:\temp\sysadminreport.txt”
$sysadmins = Import-Csv “t:\Scripting\ps\SysAdmin Email.txt” -ErrorAction stop
if (Test-Path $dir)
{}
else
{
New-Item $dir -type directory
}
if (Test-Path $outfile)
{
Remove-Item $outfile -force
}
####################################################################################
# Get Systems Excel Spreadsheet for processing
####################################################################################
$excelApp = new-object -comobject Excel.Application
$excelApp.visible = $true
$date = Get-Date –f yyyyMMdd
$copy = “\\pni-pcfs01\system$\VMWare\$date Powershell Datastore Report.xls”
Copy-Item “\\pni-pcfs01\system$\VMWare\VMWare LUN Configuration & Licensing.xls” $copy
$file = (dir $copy).FullName
$book = $excelApp.Workbooks.Open($file)
####################################################################################
# Get Datastores and create size related variables
####################################################################################
Get-Datastore|`
%{$arrDATASTORE = $_;`
$DS = $arrDATASTORE.Name;`
[int]$FREESPACE = $arrDATASTORE.FreeSpaceMB;`
[int]$CAPACITY = $arrDATASTORE.CapacityMB;`
[decimal]$PERCENTFREE = $FREESPACE / $CAPACITY;`
####################################################################################
# Update Systems Datastore Spreadsheet
####################################################################################
# get sheet and update sheet name
$sheet = $book.Worksheets.Item(1)
#$s1.name = “Datastore Report for PNI”
$row = 3
while(“True”)
{
if ($DS -match “Dellpe”)
{
write-host “$DS is internal”
break;
}
$datastore = $sheet.Cells.Item($row,1).Value2
$description = $sheet.Cells.Item($row,2).Value2
if (!$datastore)
{
write-host “$datastore does not match $DS”
break;
}
if ($datastore -match “$DS”)
{
write-host “$datastore matched!”
$sheet.Cells.Item($row,3).Value2 = [int][decimal]$CAPACITY / 1000
$sheet.Cells.Item($row,5).Value2 = [int][decimal]$FREESPACE / 1000
break;
;
}
$row++
}
# temporary break for excel testing:
#}
#break;
####################################################################################
# Get Datastores with less than 10% available space and list hosted VMs.
####################################################################################
If ( [decimal]$PERCENTFREE -lt .1) {
Add-Content $outfile “`n####################################################################`r”
Add-Content $outfile “$DS has less than 10% free space!`r”
Add-Content $outfile “Capacity: $CAPACITY MB`r”
Add-Content $outfile “Available: $FREESPACE MB`r”
Add-Content $outfile “The following VM’s are on this Datastore:`r”
Add-Content $outfile “####################################################################`r”
Get-Datacenter| Get-VM |`
%{$vm = $_;`
Get-HardDisk -VM $vm |`
%{$hd = $_;`
[int]$hdcap = $hd.capacitykb/1000
# Write-Host -Separator ” ” $vm.name $hd.filename “$hdcap MB”
If ($hd.filename -match “\[$DS\]“) {
[int]$hdcap = $hd.capacitykb/1000
$name = $vm.Name
$hdname = $hd.Filename
$vmgroup = $vm.CustomFields.Item(“Group”)
$vmadmin = $vm.CustomFields.Item(“System Administrator”)
If ($name -match “replicat”) {
$vmgroup = “VizionCore Replication”
$vmadmin = “John Taber”
}
If ($vmgroup -match “$description”)
{Add-Content $outfile “$name `t $hdname `t $hdcap MB `r”}
Else {
foreach($sysadmin in $sysadmins) {
$adminfirstname = $sysadmin.First
$adminlastname = $sysadmin.Last
If ($vmadmin -match “$adminlastname”) {
Add-Content $dir\$adminlastname.txt “$name is located on the wrong Datastore. `r”
Add-Content $dir\$adminlastname.txt “The harddrive file $hdname is located on datastore $DS. `r”
Add-Content $dir\$adminlastname.txt “This datastore is reserved for $description usage. `r”
Add-Content $dir\$adminlastname.txt “$name is classified as $vmgroup and should be on a $vmgroup datastore. `r”
Add-Content $dir\$adminlastname.txt “Unfortunately, the $DS datastore has less than 10 percent free disk space. `r”
Add-Content $dir\$adminlastname.txt “Please relocate the $name VM as soon as possible. `r”
Add-Content $dir\$adminlastname.txt “`r”
}
}
Add-Content $outfile “**** Wrong Group ***** Group is $vmgroup and should be $description `t $name `t $hdname `t $hdcap MB `r”
}
}
}
}
}
}
Add-Content $outfile “`n`n`nEnd of Report.”
####################################################################################
# Clean up Excel work.
####################################################################################
$excelApp.Quit()
$book = $null
$sheet = $null
$excelApp = $null
[GC]::Collect()
####################################################################################
# Send email to team with Datastore summary and recommendations
####################################################################################
#$email = new-object -comobject “cdo.message”
#$email.From = “pni-dvsysmgt01.pni.com”
#$email.To = “phx.it.systems@pni.com”
#$email.Subject = “Datastorage Space Managment.”
#$email.TextBody = “Please examine the following output. This is important that we keep these datastores with 10 percent or more of freespace to give us room for replication, snapshots and emergency filesystem growth!`n`n”
#
#foreach ($f in get-content $outfile)
# {
# $email.TextBody += “$f `n”
# }
#
#
#$email.Configuration.Fields.Item(“http://schemas.microsoft.com/cdo/configuration/sendusing”) = 2
#$email.Configuration.Fields.Item(“http://schemas.microsoft.com/cdo/configuration/smtpserver”) = “mail.pni.com”
#$email.Configuration.Fields.Item(“http://schemas.microsoft.com/cdo/configuration/smtpserverport”) = 25
#
#$email.Configuration.Fields.Update()
#$email.Send()
####################################################################################
# Send email to Sysadmin with Datastore summary and recommendations
####################################################################################
foreach($sysadmin in $sysadmins) {
$adminfirstname = $sysadmin.First
$adminlastname = $sysadmin.Last
$adminemail = $sysadmin.Email
$adminfile = “$dir\$adminlastname.txt”
$email = new-object -comobject “cdo.message”
$email.From = “pni-dvsysmgt01.pni.com”
$email.To = “$adminemail”
$email.CC = “sgray@pni.com,john.taber@pni.com”
$email.Subject = “Datastorage Space Managment.”
$email.TextBody = “Please examine the following output. This is important that we keep these datastores with 10 percent or more of freespace to give us room for replication, snapshots and emergency filesystem growth!`n The latest information can be found in $copy `n`n”
if (test-path $adminfile) {
foreach ($f in get-content $adminfile)
{
$email.TextBody += “$f `n”
}
$email.Configuration.Fields.Item(“http://schemas.microsoft.com/cdo/configuration/sendusing”) = 2
$email.Configuration.Fields.Item(“http://schemas.microsoft.com/cdo/configuration/smtpserver”) = “mail.pni.com”
$email.Configuration.Fields.Item(“http://schemas.microsoft.com/cdo/configuration/smtpserverport”) = 25
$email.Configuration.Fields.Update()
$email.Send()
del $adminfile
}
}
0:26 on February 3rd, 2009
I stumbled across this website after a google search. I’m interested in running this script. What do I need to have installed for it to run? Can I just copy and paste into a file, what file extension does it need etc. to execute. Thanks!
14:58 on February 7th, 2009
@Rhodan
Hi Rhodan. You need Windows Powershell and the VI Toolkit which adds the Vmware powershell components used in the script.
Save the file as a ps1 extension, and have a search for “how to run powershell scripts” on google you should find a microsoft page by the scripting guy which will tell you what to do.
Hope this helps
15:14 on February 7th, 2009
Hi Hugo
Great Script
Trying to understand though why some of my VM’s would show up as “NO MATCHES!” when doing the SCSI matching. Can you explain a little about this part of the script so I can work out where it’s going wrong. I’m wondering if there are problems with my VM’s
Regards
C Stewart
20:07 on February 17th, 2009
Hi Hugo,
I am getting the expected results for only a few VMs but most are getting this error messsage while running the script:
VMDK UPLSVUXYZ.vmdk belongs to VM USPLSVUXYZ
Get-WmiObject : Access is denied. (Exception from HRESULT: 0×80070005 (E_ACCESS DENIED)
At C:\temp\VM-SAN-Script.ps1:72 char:28
+ $Partitions = Get-WmiObject <<<< – Class Win32_DiskPartition – ComputerName $VMRef.Hostname
Regards
Eric C
9:08 on February 18th, 2009
Eric,
The error indicates you have insufficient permissions to do a WMI query to the machines. Make sure the account running the script is local admin on all target vm’s.
17:42 on February 18th, 2009
I am looking to use PowerShell to run against my environment, asks me which cluster I want to investigate, and then enumerate each lun, the used space, total capacity, and free space, then dump that to a csv or xls file. Can you please advise?
9:53 on February 19th, 2009
Do you mean LUNs or datastores? (Subtle difference)
Datastores is easily done:
# Variables
$VIServer = “MyVCServer.domain.local”
$ExportFile = ‘D:\scripts\datastores.csv’
#Functions
function Create-ListBox
{
param ([array]$Items,[string]$Title = “ListBox”,[string]$Text = “Please make your selection”)
If ($Items -eq $null)
{
Write-Warning ‘Argument “Items” is mandatory.’;throw “Syntax error.”
}
[System.Reflection.Assembly]::LoadWithPartialName(“System.Windows.Forms”) | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName(“System.Drawing”) | Out-Null
$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = $Title
$objForm.Size = New-Object System.Drawing.Size(300,200)
$objForm.StartPosition = “CenterScreen”
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq “Enter”){$x=$objListBox.SelectedItem;$objForm.Close()}})
$objForm.Add_KeyDown({if ($_.KeyCode -eq “Escape”){$objForm.Close()}})
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = “OK”
$OKButton.Add_Click({$x=$objListBox.SelectedItem;$objForm.Close()})
$objForm.Controls.Add($OKButton)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = “Cancel”
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)
$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,20)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = $Text
$objForm.Controls.Add($objLabel)
$objListBox = New-Object System.Windows.Forms.ListBox
$objListBox.Location = New-Object System.Drawing.Size(10,40)
$objListBox.Size = New-Object System.Drawing.Size(260,20)
$objListBox.Height = 80
ForEach ($item in $Items)
{
[void] $objListBox.Items.Add($item)
}
$objForm.Controls.Add($objListBox)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
return $x
}
#Script
$VC = Connect-VIServer $VIServer
$Cluster = Create-ListBox -Items (Get-Cluster | Sort Name) -Title “Select Cluster” -Text “Please select a cluster”
$Cluster | Get-VMHost | Get-Datastore | Sort Name | Select Name, CapacityMB, FreeSpaceMB -Unique | Export-Csv $OutputFile -NoTypeInformation
Disconnect-VIServer -Confirm:$False
21:53 on February 18th, 2009
I have blogged about several bits and pieces you could use. There’s a Create-Listbox function I wrote, which makes selecting the cluster really nifty. Enumerating LUNs should be a breeze with the VI Toolkit 1.5 Get-SCSILun cmdlet. Capacity and free space are properties of those luns, so easily extracted.
I’ll write a script when I have some spare time. Check back soon.
23:35 on March 4th, 2009
OK, local admin account, not domain account. Thanks will give that a shot.
21:23 on March 17th, 2009
Cool Script.. How do I get the script to continue after failures because of “access denied” or “RPC errors”? and in report leave entries blank..
denied@admin
22:28 on March 17th, 2009
@Ainz
Try setting $errorActionPreference = SilentlyContinue
Hugo
16:08 on March 18th, 2009
I did try setting it.. but I get the term ‘SilentlyContinue” is not recognized as a cmdlet
19:32 on March 18th, 2009
Sorry, that should be:
$ErrorActionPreference = “SilentlyContinue”
(with quotes)
21:21 on March 18th, 2009
Hugo,
I was able to get the script to continue after failures and pipe failed VMs into a error log..
“trap {
Write-Host “error connecting to $VMRef.HosteName” -Foregroundcolor Red
“$VMRef.VM” | Out-File d:\Reports\errors.txt -append
continue
}”
Thanks..
23:47 on April 14th, 2009
Hi Hugo,
i tryed to run your great script against our Virtual Center.
Starting fine…. but when the script reaches a VM running Linux an exception occured and the script stopped. I know running WMI – Commands against a Linux Host doesnt work – but – i am an absulute Powershell Beginner and therefore i have no idea to “help” the script continue after the exception.
Do you have a idea to solve this?
Thanks in advance and excuse my horrible english,
Frank
11:06 on May 15th, 2009
Hi all,
I’ve recently had to do some work on some disk space reporting which may be of use. In terms of excluding ‘local’ datastore, the datastore view (get-datastore | get-view) contains summary.MultipleHostAccess. You can use this field, to exclude datastores that are local by ‘assuming’ that any VMFS or NFS volume that has MultipleHostAccess true, will be shared storage, and anything set to false will be local.
Working well, so far.
SP
@admin
13:48 on July 8th, 2009
Hi All,
I know it’s been a bit since the last post but I have a quick question. The above script works great in my environment – really gives great info in the CSV. Is it possible to capture in that CSV what VMDK’s that the script finds that are powered off? I am very new to PowerShell – I did see that the script does know here:
{
Write-Host “VM is powered off” -ForegroundColor “yellow”
}
That a VM is off and points to the VMDK althougth it doesn’t know who the owner of the VMDK is…can this data be stored and also added to the CSV? This is to flag to me so I know what the LUN and Datastore picture is with all VM’s on but to also know what VM’s are currently off to ensure there is enough space on the LUN/Datastore when it is turned back on…
Thanks for the great script
Kevin Nelson
15:23 on July 8th, 2009
@Kevin
Okay, no problem. I do my best to keep supporting all scripts. Also plder ones.
Try replacing the Write-Host “VM powered off” bit with this:
# 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 = “UNKNOWN: VM OFF”
# Report Size
$myObj.DiskSizeGB = “UNKNOWN: VM OFF”
# Report Free Space
$myObj.DiskFreeGB = “UNKNOWN: VM OFF”
# Calculate Percentage free space
$myObj.PercFree = “UNKNOWN: VM OFF”
Write-Host “RESULT:” -ForegroundColor “yellow”
$myObj
# Add output object to output collection
$myCol += $myObj
Hugo
18:36 on July 9th, 2009
Thank you very much it seems to have worked as I expected. I did get a lot of errors though at the end of the script:(this is a sample there was over a page of them in the toolkit screen….)
Measure-Object : Property “DiskFreeGB” is not numeric.
At C:\VMScripts\GetDataStoreInfoWithPoweredOff.ps1:203 char:65
+ $AverageDiskFree = [Math]::Round(100 * (($myCol | Measure-Object <<<< DiskFr
eeGB -Sum).Sum / ($myCol | Measure-Object DiskSizeGB -Sum).Sum),$Decimals)
Measure-Object : Property "DiskFreeGB" is not numeric.
At C:\VMScripts\GetDataStoreInfoWithPoweredOff.ps1:203 char:65
+ $AverageDiskFree = [Math]::Round(100 * (($myCol | Measure-Object <<<< DiskFr
eeGB -Sum).Sum / ($myCol | Measure-Object DiskSizeGB -Sum).Sum),$Decimals)
Measure-Object : Property "DiskFreeGB" is not numeric.
At C:\VMScripts\GetDataStoreInfoWithPoweredOff.ps1:203 char:65
The outputed CSV seems to have the data I was looking for but there were these errors….
Kevin
13:25 on July 28th, 2009
Hi Hugo,
I’m trying to run this script in our vSphere 4 evironment but i’m getting the error:
Exception calling “SearchDatastoreSubFolders” with “2″ argument(s): “Not initialized: boolean fileOwner”
There is 1 esx 3.5 host available, connected to this host the script will run better, also with the same error, the file is present after running the script but no data inside it..
Do you have any suggestion??
Fabian
11:37 on September 10th, 2009
I too have tried running this on my vSphere environment and get the same error…
7:49 on October 12th, 2009
Hi,
Can you tell me how can I find out the poweroff Vm and also when it is poweroff and by whom with powershell script.
I know how to get the poweroff vm, but not able to get the other details.
If possible can you put your comments on this.
Regards
Gurjit Dhillon
5:35 on October 30th, 2009
Hi,
Is it possible to get all or most of the details returned by Get-WMIObject by using Get-VMguest instead? I’m trying to figure out how I can get the logical disk information without network access to the VM’s, such as in a DMZ.
Cheers
8:40 on October 30th, 2009
Hi Stephen,
I’m afraid that won’t work. The only disk-related information Get-VMGuest returns is a list of logical volumes and sizes. For this script, you need the relationship between the vDisks and the logical volumes.
Hugo
16:44 on January 22nd, 2010
This script is excellent and works great for my windows VM. Do you have suggestions for doing something similar to gain visibility into the disk usage by my Linux VMs?They all use LVM to manage the disk space.
14:24 on January 23rd, 2010
Try searching the internet for linux vm invoke-psscript. That might yield some results. Good luck!