Public/Get-VBDiskInventory.ps1

# ============================================================
# FUNCTION : Get-VBDiskInventory
# VERSION : 1.2.2
# CHANGED : 01-05-2026 -- UniqueId now resolved via Win32_PhysicalMedia on NVMe
# to replace EUI placeholder (eui.FFFFFFFFFFFFFFFF)
# with real manufacturer identifier
# AUTHOR : Vibhu
# PURPOSE : Collects full disk inventory -- volume, physical, and raw disk data
# ENCODING : UTF-8 with BOM
# ------------------------------------------------------------
# CHANGELOG (last 3-5 only -- full history in Git)
# v1.2.2 -- 01-05-2026 -- Fixed NVMe UniqueId via Win32_PhysicalMedia fallback
# v1.2.1 -- 01-05-2026 -- Fixed NVMe SerialNumber via AdapterSerialNumber fallback
# v1.2.0 -- 01-05-2026 -- Added 8 new properties; renamed Status -> CollectionStatus
# v1.1.0 -- 16-04-2026 -- Added CollectionTime; merged from Get-VBDiskInformation
# ============================================================

function Get-VBDiskInventory {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Name', 'Server', 'Host')]
        [string[]]$ComputerName = $env:COMPUTERNAME,
        [PSCredential]$Credential
    )

    process {
        foreach ($computer in $ComputerName) {
            try {
                $scriptBlock = {
                    # Step 1 -- Collect all disk data sources
                    $allPhysicalDisks     = Get-PhysicalDisk
                    $allVolumes           = Get-Volume | Where-Object { $_.DriveLetter -and $_.DriveType -eq 'Fixed' }
                    $allPartitions        = Get-Partition
                    $allWmiDisks          = Get-CimInstance -ClassName Win32_DiskDrive
                    $allPhysicalMedia     = Get-CimInstance -ClassName Win32_PhysicalMedia
                    $systemDrive          = $env:SystemDrive.TrimEnd('\')
                    $collectionTime       = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss')

                    $diskInfo             = @()
                    $processedDiskNumbers = @()

                    # Step 2 -- Process volume-based disks
                    foreach ($volume in $allVolumes) {
                        $partition = $allPartitions | Where-Object { $_.AccessPaths -contains "$($volume.DriveLetter):\" }
                        $physDisk  = $allPhysicalDisks | Where-Object { $_.DeviceId -eq $partition.DiskNumber }
                        $wmiDisk   = $allWmiDisks | Where-Object { $_.Index -eq $partition.DiskNumber }
                        $mediaDisk = $allPhysicalMedia | Where-Object { $_.Tag -eq $wmiDisk.Name }

                        if ($physDisk) {
                            $processedDiskNumbers += $physDisk.DeviceId
                            $storageType  = if ($physDisk.BusType -eq 'iSCSI') { 'iSCSI' } else { 'Local' }
                            $diskType     = if ($physDisk.BusType -eq 'RAID') { 'RAID' } else { 'Direct Disk' }
                            $partCount    = ($allPartitions | Where-Object { $_.DiskNumber -eq $physDisk.DeviceId }).Count
                            $isSystemDisk = "$($volume.DriveLetter):" -eq $systemDrive
                            # NVMe: AdapterSerialNumber holds real manufacturer serial; SerialNumber returns EUI/NGUID
                            $serialNumber = if ($physDisk.BusType -eq 'NVMe' -and $physDisk.AdapterSerialNumber) { $physDisk.AdapterSerialNumber.Trim() } else { $physDisk.SerialNumber }
                            # NVMe: Win32_PhysicalMedia.SerialNumber holds real unique ID; Get-PhysicalDisk.UniqueId returns EUI placeholder
                            $uniqueId     = if ($physDisk.BusType -eq 'NVMe' -and $mediaDisk.SerialNumber) { $mediaDisk.SerialNumber.Trim() } else { $physDisk.UniqueId }

                            $diskInfo += [PSCustomObject]@{
                                ComputerName        = $env:COMPUTERNAME
                                DriveLetter         = "$($volume.DriveLetter):"
                                Label               = $volume.FileSystemLabel
                                FileSystem          = $volume.FileSystem
                                TotalSizeGB         = [math]::Round($volume.Size / 1GB, 2)
                                UsedGB              = [math]::Round(($volume.Size - $volume.SizeRemaining) / 1GB, 2)
                                FreeGB              = [math]::Round($volume.SizeRemaining / 1GB, 2)
                                FreePercent         = [math]::Round(($volume.SizeRemaining / $volume.Size) * 100, 1)
                                AllocationUnitSize  = $volume.AllocationUnitSize
                                OperationalStatus   = $volume.OperationalStatus
                                IsSystemDisk        = $isSystemDisk
                                StorageType         = $storageType
                                DiskType            = $diskType
                                MediaType           = $physDisk.MediaType
                                BusType             = $physDisk.BusType
                                SpindleSpeed        = $physDisk.SpindleSpeed
                                Usage               = $physDisk.Usage
                                FriendlyName        = $physDisk.FriendlyName
                                UniqueId            = $uniqueId
                                HealthStatus        = $physDisk.HealthStatus
                                SerialNumber        = $serialNumber
                                FirmwareVersion     = $physDisk.FirmwareVersion
                                InterfaceType       = $wmiDisk.InterfaceType
                                WMICaption          = $wmiDisk.Caption
                                WmiStatus           = $wmiDisk.Status
                                PartitionCount      = $partCount
                                DiskNumber          = $physDisk.DeviceId
                                IsVolumeBased       = $true
                                CollectionStatus    = 'Success'
                                CollectionTime      = $collectionTime
                            }
                        }
                    }

                    # Step 3 -- Process raw (unpartitioned) disks
                    foreach ($physDisk in $allPhysicalDisks) {
                        if ($physDisk.DeviceId -notin $processedDiskNumbers) {
                            $wmiDisk      = $allWmiDisks | Where-Object { $_.Model -like "*$($physDisk.FriendlyName)*" }
                            $mediaDisk    = $allPhysicalMedia | Where-Object { $_.Tag -eq $wmiDisk.Name }
                            $storageType  = if ($physDisk.BusType -eq 'iSCSI') { 'iSCSI' } else { 'Local' }
                            $diskType     = if ($physDisk.BusType -eq 'RAID') { 'RAID' } else { 'Direct Disk' }
                            $totalSizeGB  = [math]::Round($physDisk.Size / 1GB, 2)
                            $partCount    = ($allPartitions | Where-Object { $_.DiskNumber -eq $physDisk.DeviceId }).Count
                            # NVMe: AdapterSerialNumber holds real manufacturer serial; SerialNumber returns EUI/NGUID
                            $serialNumber = if ($physDisk.BusType -eq 'NVMe' -and $physDisk.AdapterSerialNumber) { $physDisk.AdapterSerialNumber.Trim() } else { $physDisk.SerialNumber }
                            # NVMe: Win32_PhysicalMedia.SerialNumber holds real unique ID; Get-PhysicalDisk.UniqueId returns EUI placeholder
                            $uniqueId     = if ($physDisk.BusType -eq 'NVMe' -and $mediaDisk.SerialNumber) { $mediaDisk.SerialNumber.Trim() } else { $physDisk.UniqueId }

                            $diskInfo += [PSCustomObject]@{
                                ComputerName        = $env:COMPUTERNAME
                                DriveLetter         = 'N/A (Raw Disk)'
                                Label               = 'N/A'
                                FileSystem          = 'N/A'
                                TotalSizeGB         = $totalSizeGB
                                UsedGB              = 0
                                FreeGB              = $totalSizeGB
                                FreePercent         = 100
                                AllocationUnitSize  = 'N/A'
                                OperationalStatus   = 'N/A'
                                IsSystemDisk        = $false
                                StorageType         = $storageType
                                DiskType            = $diskType
                                MediaType           = $physDisk.MediaType
                                BusType             = $physDisk.BusType
                                SpindleSpeed        = $physDisk.SpindleSpeed
                                Usage               = $physDisk.Usage
                                FriendlyName        = $physDisk.FriendlyName
                                UniqueId            = $uniqueId
                                HealthStatus        = $physDisk.HealthStatus
                                SerialNumber        = $serialNumber
                                FirmwareVersion     = $physDisk.FirmwareVersion
                                InterfaceType       = $wmiDisk.InterfaceType
                                WMICaption          = $wmiDisk.Caption
                                WmiStatus           = $wmiDisk.Status
                                PartitionCount      = $partCount
                                DiskNumber          = $physDisk.DeviceId
                                IsVolumeBased       = $false
                                CollectionStatus    = 'Success'
                                CollectionTime      = $collectionTime
                            }
                        }
                    }

                    return $diskInfo
                }

                # Step 4 -- Local vs remote execution
                if ($computer -eq $env:COMPUTERNAME) {
                    $result = & $scriptBlock
                } else {
                    $params = @{
                        ComputerName = $computer
                        ScriptBlock  = $scriptBlock
                        ErrorAction  = 'Stop'
                    }
                    if ($Credential) { $params.Credential = $Credential }
                    $result = Invoke-Command @params
                }

                $result
            }
            catch {
                # Step 5 -- Error handling
                [PSCustomObject]@{
                    ComputerName     = $computer
                    DriveLetter      = $null
                    Error            = $_.Exception.Message
                    CollectionStatus = 'Failed'
                    CollectionTime   = (Get-Date).ToString('dd-MM-yyyy HH:mm:ss')
                }
            }
        }
    }
}