Examples/Get-PfaDiskUsage.ps1

#Require -PSEdition Desktop
#Requires -Modules @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="11.5.0.0"}
#VMware.VimAutomation.Storage

function Get-PfaDiskUsage {
    [CmdletBinding(DefaultParameterSetName='Default')]
    param ( 
        [String[]]$ComputerName = "$env:COMPUTERNAME",
        [Parameter(ParameterSetName = "IncludeShares")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential,
        [Switch]$IncludeShares,
        [Parameter(ParameterSetName = "IncludeShares")]
        [Switch]$IgnoreSpecialShares,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [PureStorageRestApi[]]$Connection,
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Util10.VersionedObjectImpl]$VIConnection,
        [Switch]$SendEmail
    )

    begin {
        $StartTime = Get-Date
        $ErrorMessage = $null

        # Define parameters array for the "Utilization" column
        $paramsUtilization = @{ 
            # Column name
            Column = "Utilization"
            # Test criteria: Is value greater than or equal to Argument?
            ScriptBlock = {[double]$args[0] -ge [double]$args[1]}  
            # CSS attribute to add if ScriptBlock is true
            CSSAttribute = "style"
            # Format column with 2 decimal places and add a percent symbol
            StringFormat = "{0:N2} %"
        }
    }

    process {
        $Computers = @($ComputerName | ForEach-Object {
            $Computer = $_
            try {
                if ($null -ne $Credential) {
                    $CimSession = New-CimSession -ComputerName $_ -Credential $Credential -ErrorVariable +ErrorMessage -ErrorAction SilentlyContinue
                } else {
                    $CimSession = New-CimSession -ComputerName $_ -ErrorVariable +ErrorMessage -ErrorAction SilentlyContinue
                }
                $Disks = @(Get-CimInstance -CimSession $CimSession -Class CIM_LogicalDisk -ErrorVariable +ErrorMessage -ErrorAction Stop | Where-Object DriveType -eq '3' | Select-Object SystemName, DeviceId, VolumeName, Description, Size, FreeSpace)
                $VM = Get-VM | Where-Object {$_.ExtensionData.Guest.Hostname -like "$($Computer)*"}

                $WindowsVolumes = @{}
                Get-CimInstance -CimSession $CimSession -ClassName Win32_DiskDrive -PipelineVariable Disk | ForEach-Object {
                    Get-CimAssociatedInstance -InputObject $Disk -ResultClassName Win32_DiskPartition -PipelineVariable Partition | ForEach-Object {
                        Get-CimAssociatedInstance -InputObject $Partition -ResultClassName Win32_LogicalDisk | ForEach-Object {
                            if ($null -eq $WindowsVolumes["$($Disk.SCSIBus):$($Disk.SCSITargetId)"]) {
                                $WindowsVolumes.Add("$($Disk.SCSIBus):$($Disk.SCSITargetId)", $_.DeviceID)
                            } else {
                                $WindowsVolumes["$($Disk.SCSIBus):$($Disk.SCSITargetId)"] = ,@("$($WindowsVolumes["$($Disk.SCSIBus):$($Disk.SCSITargetId)"]),$($_.DeviceID)")
                            }
                        }
                    }
                }
                $PureVolumes = Get-ScsiController -VM $VM -PipelineVariable Controller | ForEach-Object {
                    Get-HardDisk -VM $VM -PipelineVariable Disk | Where-Object {$_.ExtensionData.ControllerKey -eq $Controller.Key} | ForEach-Object {
                        $Connection | ForEach-Object {
                            $VVol = Invoke-PfaApiRequest -Array $_ -Request RestMethod -Method Get -Path "/volume?tags=true&filter=value='$($Disk.ExtensionData.Backing.BackingObjectId)'&limit=1" -SkipCertificateCheck -ApiVersion 1.18 -ErrorVariable +ErrorMessage -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name
                            if ($null -ne $VVol) {
                                $VolumeMetrics = Invoke-PfaApiRequest -Array $_ -Request RestMethod -Method Get -Path "/volumes/space?names=$VVol" -SkipCertificateCheck -ErrorVariable +ErrorMessage -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Space
                                [PSCustomObject][Ordered]@{
                                        VM          =   $VM.name
                                        HD          =   $Disk.Name
                                        VMDK        =   $Disk.Filename
                                        Device      =   "$($Controller.ExtensionData.BusNumber):$($Disk.ExtensionData.UnitNumber)"
                                        vVol        =   $VVol
                                        Drive       =   Invoke-Command -Command {
                                                            $WindowsVolumes["$($Controller.ExtensionData.BusNumber):$($Disk.ExtensionData.UnitNumber)"]
                                                        }
                                        Snapshots   =   $VolumeMetrics.Snapshots
                                        Size        =   $VolumeMetrics.Unique
                                        Reduction   =   $VolumeMetrics.Total_Reduction
                                }
                            }
                        }
                    }
                }
                
                if ($IncludeShares) {
                    if ($IgnoreSpecialShares) {
                        $Filter = @{
                            "Filter" = "Type = 0"
                        }
                    } else {
                        $Filter = @{
                            "Filter" = "Type = 0 OR Type >= 3"
                        }
                    }
                    if ($PSVersionTable.PSVersion.Major -ge 6) {
                        $AdminShares = 'A'..'Z' | ForEach-Object { "$_$" } -End {"ADMIN$";"IPC$"}
                    } else {
                        $AdminShares = @("A$","B$","C$","D$","E$","F$","G$","H$","I$","J$","K$","L$","M$","N$","O$","P$","Q$","R$","S$","T$","U$","V$","W$","X$","Y$","Z$", "ADMIN$", "IPC$")
                    }
                    $Shares = @(Get-CimInstance -CimSession $CimSession -Class Win32_Share @Filter -ErrorAction SilentlyContinue) | Sort-Object {
                        $Index = $AdminShares.IndexOf($_.Name)
                        if ($Index -ne -1) {
                            $Index
                        } else {
                            [System.Double]::PositiveInfinity
                        }
                    }
                }
                $Disks | ForEach-Object {
                    $Disk = $_
                    if ($null -ne $Shares) {
                        $Share = ($Shares | Where-Object {$_.Path.StartsWith($Disk.DeviceId)}).Name  -join ", "
                    } else {
                        $Share = ""
                    }
                    if ($null -ne $PureVolumes) {
                        $PureDisk = $PureVolumes | Where-Object {$_.Drive -eq $Disk.DeviceId -or $_.Drive -match $Disk.DeviceId}
                    }
                    $_ | Add-Member -NotePropertyMembers @{
                        "Used"              =   $_.Size - $_.FreeSpace
                        "Shares"            =   $Share
                        "Utilization"       =   ($_.Size - $_.FreeSpace) / $_.Size * 100
                        "VVolName"          =   $PureDisk.VVol
                        "VVolSize"          =   $PureDisk.Size
                        "VVolSnapshotSize"  =   $PureDisk.Snapshots
                        "VVolReduction"     =   $PureDisk.Reduction
                    }
                }
                $ComputerObject = [PSCustomObject]@{
                    "ComputerName"  = $_
                    "Disks"         = @($Disks | Select-Object -ExcludeProperty SystemName)
                }
                $ComputerObject
            } finally {
                if ($null -ne $CimSession) {
                    Remove-CimSession -CimSession $CimSession -ErrorAction SilentlyContinue
                }
            }
        })
        if ($Computers.Count -gt 0) {
            if (Get-Module -Name "PS2HTMLTable") {
                $HTML = New-HTMLHead -Theme light -Title "Disk Usage Report"
                $HTML += '<table id="container" cellpadding="0" cellspacing="0" border="0">'
                $Computers | ForEach-Object {
                    $HTML += "<tr><td style=""border: none;text-align: left;"" colspan=""10""><h3>$($_.ComputerName) ($($_.Disks.Count))</h3></td></tr><tr><th colspan=""7"" style=""background: black;border-right: 1px solid #dddddd;border-top: 1px solid black;border-left: 1px solid black;""></th><th colspan=""4"" style=""background: black;color:white;border-right: 1px solid #dddddd;border-top: 1px solid black;"">Pure Storage</th></tr>"
                    $HTMLTable = $_.Disks | Select-Object @{Name = "Drive";Expression = {$_.DeviceId}}, 
                                                          @{Name = "Label";Expression = {$_.VolumeName}}, 
                                                          @{Name = "Size";Expression = {Format-Byte -Bytes $_.Size}},
                                                          @{Name = "Used";Expression = {Format-Byte -Bytes ($_.Size - $_.FreeSpace)}},
                                                          @{Name = "Free Space";Expression = {Format-Byte -Bytes $_.FreeSpace}},
                                                          Shares,
                                                          Utilization,
                                                          @{Name = "Volume Name";Expression = {$_.VVolName}},
                                                          @{Name = "Volume Usage";Expression = {Format-Byte -Bytes $_.VVolSize}},
                                                          @{Name = "Snapshot Usage";Expression = {Format-Byte -Bytes $_.VVolSnapshotSize}},
                                                          @{Name = "Volume Reduction";Expression = {if ($null -ne $_.VVolReduction) {"{0:N2}x" -f $_.VVolReduction} else {"1.00x"}}} |
                                                          New-HTMLTable -HTMLDecode -SetAlternating -NestedTable -RemoveColumnGroup -ColumnClassPrefix "col"
                    # Color "Utilization" cell yellow if value is greater than or equal to 60%
                    $HTMLTable = Add-HTMLTableColor -HTML $HTMLTable -Argument 60 -CSSAttributeValue "background-color:#fac13a;" @paramsUtilization
                    # Color "Utilization" cell orange if value is greater than or equal to 75%
                    $HTMLTable = Add-HTMLTableColor -HTML $HTMLTable -Argument 75 -CSSAttributeValue "background-color:#fa8a1c;" @paramsUtilization
                    # Color "Utilization" cell red if value is greater than or equal to 90%
                    $HTMLTable = Add-HTMLTableColor -HTML $HTMLTable -Argument 90 -CSSAttributeValue "background-color:#e44f12;" @paramsUtilization -ApplyFormat
                    $HTMLTable += "<tr><td class=""top-border""></td><td class=""top-border""></td><td class=""top-border bold"">$(Format-Byte ($_.Disks.Size | Measure-Object -Sum).Sum)</td><td class=""top-border bold"">$(Format-Byte (($_.Disks.Size | Measure-Object -Sum).Sum - ($_.Disks.FreeSpace | Measure-Object -Sum).Sum))</td><td class=""top-border bold"">$(Format-Byte ($_.Disks.FreeSpace | Measure-Object -Sum).Sum)</td><td class=""top-border""></td><td class=""top-border bold"">$("{0:N2} %" -f ($_.Disks.Utilization | Measure-Object -Average).Average)</td><td class=""top-border""></td><td class=""top-border bold"">$(Format-Byte ($_.Disks.VVolSize | Measure-Object -Sum).Sum)</td><td class=""top-border bold"">$(Format-Byte ($_.Disks.VVolSnapshotSize | Measure-Object -Sum).Sum)</td><td class=""top-border bold"">$("{0:N2}x" -f ($_.Disks.VVolReduction | Measure-Object -Average).Average)</td></tr>"
                    $HTML += $HTMLTable
                }
                $HTML += "</table>"
                $HTML = $HTML | Close-HTML -Validate
            } else {
                $HTML = @"
                    <!DOCTYPE html>
                    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
                        <head>
                            <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
                            <title>Disk Usage Report</title>
                            <style>
                            body {
                                color: #333333;
                                font-family: Calibri,Tahoma,Arial,Verdana;
                                font-size: 11pt;
                                margin: 0px;
                                padding: 0px;
                            }
                            h3 {
                                margin: 5px 0px 5px 0px;
                            }
                            h4 {
                                margin: 0px;
                            }
                            table {
                                border-collapse: collapse;
                            }
                            tr.last-child {
                                border-bottom: 1px solid black;
                            }
                            th {
                                text-align: center;
                                font-weight: bold;
                                border-top: 1px solid black;
                                border-bottom: 1px solid black;
                                white-space: nowrap;
                                padding: 0px 10px 0px 10px;
                            }
                            th.align-left {
                                text-align: left;
                            }
                            td {
                                padding: 2px 10px 2px 10px;
                                text-align: center;
                                white-space: nowrap;
                            }
                            td.bold {
                                font-weight: bold;
                            }
                            td.top-border {
                                border-top: 1px solid black;
                            }
                            .odd {
                                background-color: #dddddd;
                            }
                            .even {
                                background-color: #ffffff;
                            }
                             
                            </style>
                        </head>
                        <body>
"@

                $HTML += '<table id="container" cellpadding="0" cellspacing="0" border="0">'
                $Computers | ForEach-Object {
                    $HTML += "<tr><td style=""border: none;text-align: left;"" colspan=""10""><h3>$($_.ComputerName) ($($_.Disks.Count))</h3></td></tr><tr><th colspan=""7"" style=""background: black;border-right: 1px solid #dddddd;border-top: 1px solid black;border-left: 1px solid black;""></th><th colspan=""4"" style=""background: black;color:white;border-right: 1px solid #dddddd;border-top: 1px solid black;"">Pure Storage</th></tr>"
                    $Xml = [System.Xml.Linq.XDocument]::Parse("$($_.Disks | Select-Object @{Name = "Drive";Expression = {$_.DeviceId}},
                                                          @{Name = "Label";Expression = {$_.VolumeName}},
                                                          @{Name = "Size";Expression = {Format-Byte -Bytes $_.Size}},
                                                          @{Name = "Used";Expression = {Format-Byte -Bytes ($_.Size - $_.FreeSpace)}},
                                                          @{Name = "Free Space";Expression = {Format-Byte -Bytes $_.FreeSpace}},
                                                          Shares,
                                                          @{Name = "Utilization";Expression = {"{0:N2} %" -f $_.Utilization}},
                                                          @{Name = "Volume Name";Expression = {$_.VVolName}},
                                                          @{Name = "Volume Usage";Expression = {Format-Byte -Bytes $_.VVolSize}},
                                                          @{Name = "Snapshot Usage";Expression = {Format-Byte -Bytes $_.VVolSnapshotSize}},
                                                          @{Name = "Volume Reduction";Expression = {if ($null -ne $_.VVolReduction) {"{0:N2}x" -f $_.VVolReduction} else {"1.00x"}}} |
                                                          ConvertTo-Html -Fragment)"
)
                    $Xml.Element("table").Element("colgroup").Remove()
                    foreach ($XmlTr in $($Xml.Descendants("tr"))) {
                        if ($XmlTr.Where({$_.Element('td')})) {
                            if (($XmlTr.NodesBeforeSelf() | Measure-Object).Count % 2 -eq 0) {
                                $XmlTr.SetAttributeValue("class", "even $($XMlTr.Attribute("class").Value)".Trim())
                            } else {
                                $XmlTr.SetAttributeValue("class", "odd $($XMlTr.Attribute("class").Value)".Trim())
                            }
                        }
                    }
                    $HTMLTable = [System.Xml.Linq.XDocument]::Parse($Xml).Document.ToString().Replace("<table>", "").Replace("</table>", "")
                    $HTMLTable += "<tr><td class=""top-border""></td><td class=""top-border""></td><td class=""top-border bold"">$(Format-Byte ($_.Disks.Size | Measure-Object -Sum).Sum)</td><td class=""top-border bold"">$(Format-Byte (($_.Disks.Size | Measure-Object -Sum).Sum - ($_.Disks.FreeSpace | Measure-Object -Sum).Sum))</td><td class=""top-border bold"">$(Format-Byte ($_.Disks.FreeSpace | Measure-Object -Sum).Sum)</td><td class=""top-border""></td><td class=""top-border bold"">$("{0:N2} %" -f ($_.Disks.Utilization | Measure-Object -Average).Average)</td><td class=""top-border""></td><td class=""top-border bold"">$(Format-Byte ($_.Disks.VVolSize | Measure-Object -Sum).Sum)</td><td class=""top-border bold"">$(Format-Byte ($_.Disks.VVolSnapshotSize | Measure-Object -Sum).Sum)</td><td class=""top-border bold"">$("{0:N2}x" -f ($_.Disks.VVolReduction | Measure-Object -Average).Average)</td></tr>"
                    $HTML += $HTMLTable
                }
                $HTML += "</table>"
                $HTML += "</body></html>"
            }
            try {
                $HTML | Out-File PfaDiskUsage.html
            } catch {
                throw $_
            }
        }
        $ErrorMessage | Sort-Object OriginInfo | ForEach-Object {
            Write-Error "$($_.OriginInfo) - $($_.Exception.Message)"
        }
    }
}