We've been battling with disk block size issues on our SAN since we got it. We're to a point where we're converting all our exiting VMs over to 32k block size disks. Our SAN kind of requires it to function smoothly.
The below PowerShell script builds off my last domain disk script and adds the block size of the NTFS partition. It lists the disk alongside the VMFS datastore it is stored on. Using it you can easily see if the disk is on the proper data store.gging.
The script emails an HTML formatted report that shows each disk, its size, how much is used, it's block size, the datastore it's on, and how much space is left. It also writes an HTML file to C:\Scripts so you can manually load up the output for testing.
I run this as needed with domain admin rights. Change $Debug to $True to get console output while it runs.
Below is an example of the output as seen in email:
clear-host
$ErrorActionPreference = "silentlycontinue"
$Debug = $False
$user = "domain\user"
$pass = "password"
$Global:ReportBody = ""
$vCenter = "vcenter"
$Color = "blue"
If ($Debug){
$Global:eMailRecipient = "user@company.com" #--[ Destination email address during
debug.
}Else{
$Global:eMailRecipient = "group@$company.com" #--[ Destination email address.
}
Function Cleanup-Variables {
Get-Variable | Where-Object { $startupVariables -notcontains $_.Name } | % { Remove-Variable -Name "$($_.Name)" -Force -Scope "global" }
}
Function SendEmail {
$email = New-Object System.Net.Mail.MailMessage
$email.From = "DailyReports@company.com"
$email.IsBodyHtml = $True
$email.To.Add($Global:eMailRecipient)
$email.Subject = "PowerShell
Disk Block Size Report"
$email.Body = $Global:ReportBody
$smtp = new-object Net.Mail.SmtpClient('email-server-ip')
$smtp.Send($email)
If ($Debug){Write-Host "`n`rEmail
sent..."}
}
Function Ping {
$timeout = 120;
trap { continue; }
$ping = new-object System.Net.NetworkInformation.Ping
$reply = new-object System.Net.NetworkInformation.PingReply
$reply = $ping.Send($Global:Target, $timeout);
if( $reply.Status -eq "Success" ){return $true;}
return $false;
}
Function Get-VMDriveReport ($Color) {
If ($Debug){Write-Host "`r`n--[ Processing $Global:Target ]--".PadRight((95-$Global:Target.length),"-") -ForegroundColor $Color}
If (Test-Connection -ComputerName $Global:Target -count 1 -BufferSize 16 -ErrorAction SilentlyContinue ) {
$HtmlData += '<tr>'
$VM = Get-VM $Global:Target
#$Datacenter = $VM | Get-Datacenter
#$Cluster = $VM | Get-Cluster
#$Datastore = $VM | Get-Datastore
#$VMView = Get-View -ViewType
VirtualMachine -Filter @{"Name"=$Global:Target}
#$VMHardDisks = Get-HardDisk -VM
$Global:Target
#$VirtualSCSIControllers =
$VMView.Config.Hardware.Device | where {$_.DeviceInfo.Label -match "SCSI
Controller"}
#$VirtualDiskDevices =
$VMView.Config.Hardware.Device #| where {$_.ControllerKey -eq
$VirtualSCSIControllers.Key}
$DiskDrives = Get-WmiObject Win32_DiskDrive -ComputerName $Global:Target
$datastores = Get-Datastore -Name ($Global:Target | Get-HardDisk -DiskType flat | %{$_.Filename.Split(']')[0].TrimStart('[')} | Sort-Object -Unique) | Sort-Object -Property Name
$DiskCount = 1
foreach($DiskDrive in $DiskDrives){
$DiskNumber = ($DiskDrive.Deviceid.Substring(4)).trimstart("PHYSICALDRIVE")
If ($Debug){Write-Host "Physical
Disk # =" $DiskNumber}
$vmdisk = Get-VM $Global:Target | Get-harddisk
$vmdiskDatastore = $vmdisk.Filename.Split(']')[0].trim('[')
If ($Debug){Write-Host "Datastore =" $vmdiskDatastore}
$Partitions = Get-WmiObject -ComputerName $Global:Target -Query "ASSOCIATORS
OF
{Win32_DiskDrive.DeviceID=`"$($DiskDrive.DeviceID.Replace('\','\\'))`"}
WHERE AssocClass = Win32_DiskDriveToDiskPartition"
If ($Partitions) {
$VolCount = 1
foreach($Part in $Partitions){
If ($Debug){Write-Host "---------------"}
Write-Host " Partiton # =" $Part.DeviceID
$Vols = Get-WmiObject -ComputerName $Global:Target -Query "ASSOCIATORS
OF {Win32_DiskPartition.DeviceID=`"$($Part.DeviceID)`"} WHERE
AssocClass = Win32_LogicalDiskToPartition"
ForEach($Vol in $Vols){
$Block = Get-WmiObject -ComputerName $Global:Target -Class win32_volume | where-object { ($_.Name -eq ($Vol.name + "\")) -and ($_.FileSystem -eq "NTFS") } | Select-Object Blocksize
If ($Debug){Write-Host " Partition ID =" $Vol.name}
$HtmlData += '<td><font color=' + $Color + '>' + $Global:Target + '</td>' #--[ Target name ]--
$HtmlData += '<td><font color=' + $Color + '>' + ($DiskDrive.Deviceid.Substring(4))
+ '</td>' #--[ Physical
disk # ]--
$HtmlData += '<td><font color=' + $Color + '>' + $VM.VMHost.Name.split(".")[0].ToUpper() + '</td>' #--[ ESX Host ]--
$HtmlData += '<td><font color=' + $Color + '>' + $vmdiskDatastore + '</td>' #--[ ESX Datastore ]--
$HtmlData += '<td><font color=' + $Color + '>' + $Block.Blocksize + '</td>' #--[ Blocksize
]--
$DiskSizeGB = ([System.Math]::Round(($DiskDrive.Size/1GB),0))
$HtmlData += '<td><font color=' + $Color + '>' + $DiskSizeGB + '</td>' #--[ Disk size in
GB ]--
$HtmlData += '<td><font color=' + $Color + '>' + $VolCount + "</td>" #--[ Volume or
Partition # ]--
$HtmlData += '<td><font
color=' + $Color + '>' + $DiskDrive.Partitions + '</td>' #--[ Total # of
Vols or Partitions ]--
$HtmlData += '<td><font color=' + $Color + '>' + $Vol.Name + '</td>' #--[ Drive letter
in Windows ]--
If([string]::IsNullOrEmpty($Vol.VolumeName)){
$HtmlData += '<td><font color=' + $Color + '>N/A</td>' #--[ Volume label if one exists ]--
}Else{
$HtmlData += '<td><font color=' + $Color + '>' + $Vol.VolumeName + '</td>' #--[ Volume label
if one exists ]--
}
$PartSizeGB = ([System.Math]::Round(($Vol.Size/1GB),0)) #--[ Volume size
in bytes ]--
$HtmlData += '<td><font color=' + $Color + '>' + $PartSizeGB + '</td>' #--[ Partition size in GB ]--
$PartFreeGB = ([System.Math]::Round(($Vol.FreeSpace/1GB),0))
$HtmlData += '<td><font color=' + $Color + '>' + $PartFreeGB + '</td>' #--[ Partition freespace in GB ]--
$HtmlData += '</tr>'#>
$VolCount++
}
}
}
$DiskCount++
}
$Global:ReportBody += $HtmlData
}Else{
If (Ping){
$Global:ReportBody += '<tr><td><font
color=' + $Color + '>' + $Global:Target + '</td><td colspan=11><center><font
color=darkcyan>Ping succeeded but failed to successfully connect to target
system.</center></td></tr>'
If ($Debug){Write-Host " ...Failed
to sucessfully ping target system...`n" -ForegroundColor Cyan}
}Else{
$Global:ReportBody += '<tr><td><font
color=' + $Color + '>' + $Global:Target + '</td><td colspan=11><center><font color=darkcyan>Failed
to successfully ping or connect to target
system.</center></td></tr>'
}
} #--[ End - test-connection ]--
sleep -Milliseconds 50
}
#--[ Add header to html log file
]--
$Global:ReportBody = @()
$Global:ReportBody += '
<style
type="text/css">
table.myTable
{ border:5px solid black;border-collapse:collapse; }
table.myTable
td { border:2px solid black;padding:5px;background: #E6E6E6 }
table.myTable
th { border:2px solid black;padding:5px;background: #B4B4AB }
table.bottomBorder { border-collapse:collapse; }
table.bottomBorder
td, table.bottomBorder th { border-bottom:1px dotted black;padding:5px; }
</style>
<center><h1>-= SAN
Disk Block Size Report =-</h1></center>
The following report displays
disks configured on every server and PC in the domain.
<ul>
<li>Only powered-on Vms are shown
since the script relies on WMI to get the block size.</li>
<li>"Vol", or "Volume"
refers to a Windows disk partition.</li>
<li>The hidden system partition,
which is typically Vol 0 on Disk 0, is not shown.</li>
<li>Disk numbers are assigned
beginning with 0 (zero).</li>
<li>Partition numbers are assigned
beginning with 1 (not 0).</li>
</ul>'
ForEach ($VC in $vCenter){
Connect-VIServer -Server $VC -User $user -Password $pass | Out-Null
$GuestVMs = get-vm | where {$_.PowerState -eq "PoweredOn" -and $_.Guest.OSFullName
-match "Microsoft
Windows*" } | sort name
$Global:ReportBody += '<table
class="myTable"><tr><th>Target
VM</th><th>Disk ID</th><th>ESX
Host</th><th>DataStore</th><th>Block
Size</th><th>Disk Size</th><th>Vol #</th><th>Total
Vols</th><th>Drive</th><th>Vol
Label</th><th>Vol Size</th><th>Vol Free</th></tr>'
ForEach($Global:Target in $GuestVMs ){
Get-VMDriveReport $Color
If ($Color -eq "green"){$Color = "blue"}Else{$Color = "green"}
}
$Global:ReportBody += '</table><br><br>'
}
If (Test-Path "c:\Scripts\temp.html"){Remove-Item "c:\Scripts\temp.html" -Confirm:$false -Force:$true }
If ($Debug){$Global:ReportBody | Out-File c:\Scripts\temp.html}
SendEmail
If ($Debug){Write-Host "`r`n---
COMPLETED ---" -ForegroundColor Red}