PowerCLI – Virtual Machine Disk Usage Report
by dwarfsoft on Oct.25, 2012, under Scripting, Work
So, this is one of my largest PowerCLI/PowerShell scripts. This maps for each Virtual Machine, every Disk that is attached and trace back to what the LUN ID is that the disk is stored on, be it a VMDK, Thick/Thin provisioned disk, Physical/Virtual mode RDM, etc.
For the moment I will just paste the script as it stands, but I hope to return and explain some of the components.
Param ($OutFile = ".\VMDiskUsageReport.csv", [switch]$Debug) Connect-VIServer VIServerName $vms = get-vm $count = 0 $VMSummaries = @() if ($Debug) { #Limit the Number of VMs if we are running in Debug mode $vms = ($vms | ?{ ($_.Name -like '*MG*') -or ($_.Name -like '*EV*') }) } Foreach ($vm in $vms) { $count++ $percentcomplete = ($count/$vms.count)*100 Write-Progress -activity "Mapping VM Disks to LUNs" -status "Getting View" -PercentComplete $percentComplete $vmview = $vm | Get-View $vdd = $vm |Get-HardDisk $vdd | %{ $vdisk = $_ Write-Progress -activity "Mapping VM Disks to LUNs" -status "Mapping Hard-Disk" -PercentComplete $percentComplete $VMSummary = Ҡ| Select VM, HostName, VMHostName,VMHostCluster, Site, PowerState, DiskType, DiskFile, DiskName, LogicalDisks, LogicalDisksGuess, DataStore, DiskSize, DiskSizeInGB, SCSIController, SCSITarget, LUNId, SANLunId $VMSummary.VM = $VM.Name $VMSummary.HostName = $VMView.Guest.HostName $VMSummary.VMHostName = $VM.VMHost.Name $VMSummary.VMHostCluster = $VM.VMHost.Parent.Name $VMSummary.Site = $VMSummary.VMHostCluster.Substring(3,3) if (($VMSUmmary.HostName -eq "") -or ($VMSummary.HostName -eq $null)) { $VMSummary.HostName = $VM.Name } $VMSummary.PowerState = $VM.PowerState $VMSummary.DiskName = $_.Name $VMSummary.DiskFile = $_.ExtensionData.Backing.FileName #$_.FileName $VMSummary.DataStore = $VMSummary.DiskFile.Split("[]")[1] $VMSummary.DiskSize = $_.CapacityKB * 1KB $VMSummary.DiskSizeInGB = $VMSummary.DiskSize/1GB $ControllerKey = $vdisk.ExtensionData.ControllerKey $Controller = $vmview.Config.Hardware.Device | ?{ $_.Key -eq $ControllerKey } $BusNumber = $Controller.BusNumber $VMSummary.SCSIController = $_.ExtensionData.ControllerKey-1000 # $scsi.BusNumber $VMSummary.SCSITarget = $_.ExtensionData.UnitNumber #$_.UnitNumber $VMSummary.DiskType = $_.DiskType switch -wildcard ($_.DiskType) { "Flat" { #On DS #$ds = Get-Datastore $VMSummary.DataStore Write-Progress -activity "Mapping VM Disks to LUNs" -status "Getting LUN from Flat File" -PercentComplete $percentComplete $ds = Get-Datastore -VM $vm | ?{ $_.Name -eq $VMSummary.DataStore } $dsview = ($ds | Get-View) $ESXView = Get-View -id $vm.Host.Id $StorageSystemView = Get-View -Id $ESXView.ConfigManager.StorageSystem $volume = ($StorageSystemView.FileSystemVolumeInfo.MountInfo | ?{$_.Volume.Name -eq $VMSummary.DataStore }).Volume $diskname = ($volume.Extent | Select DiskName -f 1).DiskName $VMSummary.LUNId = $diskname } "Raw*" { Write-Progress -activity "Mapping VM Disks to LUNs" -status "Getting LUN from RDM" -PercentComplete $percentComplete $VMSummary.LUNId = ([string]$vdisk.ScsiCanonicalName) Write-Host "$($VMSummary.HostName) RDM $($VMSummary.LUNId)" -F Green } } $DiskFileName = $VMSummary.DiskFile.Split("[]/.")[-2] $FolderName = $VMSummary.DiskFile.Split("[]/.")[-3] if ($vm.PowerState -eq "PoweredOn") { try { #On VM Write-Progress -activity "Mapping VM Disks to LUNs" -status "Extracting Guest Logical Disks/Volumes" -PercentComplete $percentComplete Write-Host "SCSI $($VMSummary.HostName)/$($VMSummary.SCSIController):$($VMSummary.SCSITarget) " -F Blue $PhysicalDisks = gwmi -ComputerName $VMSummary.HostName Win32_DiskDrive -ErrorAction "silentlyContinue" | ?{($_.SCSIBus -eq $VMSummary.SCSIController) -and ($_.SCSITargetId -eq $VMSummary.SCSITarget) } $PartName = ($PhysicalDisks.PSBase.GetRelated('Win32_DiskPartition') | Select Name -f 1).Name $DiskParts = (gwmi -ComputerName $VMSummary.HostName Win32_DiskPartition -ErrorAction "silentlyContinue" | ?{ $_.Name -eq $PartName } ) $LogicalDisks = @($DiskParts | %{ $_.PSBase.GetRelated('Win32_LogicalDisk') }) #Ldi = logical disk info $ldi = "" $LogicalDisks | %{ $ldi += " $($_.DeviceId) [$($_.VolumeName)] ($( "{0:P}" -F ($_.FreeSpace/$_.Size) ) Free);".Trim() } $VMSummary.LogicalDisks = $ldi } catch { } } else { Write-Host "SCSI $($VMSummary.HostName)/$($VMSummary.SCSIController):$($VMSummary.SCSITarget) " -F Gray } $x = ($VMSummary.LUNId+" ").Substring(4).Trim() if (($x.Length -gt 0)) { $VMSummary.SANLunId = [String]::Join(":",(0..(($x.Length)-1) | ?{ ($_/2) -eq [int]($_/2) } | %{ $x.Substring($_,2) })) } $VMSummary.SANLunId = $VMSummary.SANLunId.ToUpper() if ($VMSummary.LogicalDisks.Length -le 0) { Write-Host " Logical Disks guessing " -F Red Write-Progress -activity "Mapping VM Disks to LUNs" -status "Could not Map Logical Disks, so Guessing them Instead" -PercentComplete $percentComplete # A factor that we use to weed out Volumes that are too small for the drive $differential = 0.99 if ($vm.Guest.Disks.Count -eq $vm.HardDisks.Count) { # a 1 for 1 match, this may not be entirely correct, but assume that it is #$vdisk.CapacityKb*1KB Write-Host " a 1 for 1 match, this may not be entirely correct, but assume that it is" -F Red $VMSummary.LogicalDisksGuess = ($vm.Guest.Disks | sort -Property Path |?{ (($_.Capacity / $VMSummary.DiskSize) -lt 1) -and (($_.Capacity / $VMSummary.DiskSize) -gt $differential) } | %{ "$($_.Path.Substring(0,2)) {$("{0,5:N2} GB" -F ($_.Capacity/1GB))} ($("{0,5:N2} GB Free" -F ($_.FreeSpace/1GB)))" }) -Join "; or " } elseif ($vm.Guest.Disks.Count -lt $vm.HardDisks.Count) { # Obviously one disk is not partitioned Write-Host "Obviously one disk is not partitioned" -F Red $VMSummary.LogicalDisksGuess = ($vm.Guest.Disks | sort -Property Path |?{ (($_.Capacity / $VMSummary.DiskSize) -lt 1) -and (($_.Capacity / $VMSummary.DiskSize) -gt $differential) } | %{ "$($_.Path.Substring(0,2)) {$("{0,5:N2} GB" -F ($_.Capacity/1GB))} ($("{0,5:N2} GB Free" -F ($_.FreeSpace/1GB)))" }) -Join "; or " } else { #More Volumes than assigned disks. There must be multiple partitions Write-Host "More Volumes than assigned disks. There must be multiple partitions" -F Red try{ $VMSummary.LogicalDisksGuess = ($vm.Guest.Disks | sort -Property Path |?{ (($_.Capacity / $VMSummary.DiskSize) -lt 1) -and (($_.Capacity / $VMSummary.DiskSize) -gt 0) } | %{ "$($_.Path.Substring(0,2)) {$("{0,5:N2} GB" -F ($_.Capacity/1GB))} ($("{0,5:N2} GB Free" -F ($_.FreeSpace/1GB)))" }) -Join "; or " } catch { } } $VMSUmmary.LogicalDisksGuess } Write-Progress -activity "Mapping VM Disks to LUNs" -status "Adding Summary to List" -PercentComplete $percentComplete $VMSummaries += $VMSummary } } $VMSummaries | Export-Csv -Path $OutFile |
One of the strengths of this script is that it tries to map each VMDK/RDM/etc through the OS to get a drive letter, and if it cannot do this directly (through VMGuest or gwmi to the Servers FQDN) then it tries to guess what the Drive letter COULD be based upon the size of the disk that is presented and the size of the disks that are visible by the OS.
Cheers, Chris.
You must be logged in to post a comment.