# disk-inventory.ps1 — every physical disk + every volume. # Sends: SMART health, bus/media type, drive letters, per-partition free space. # Pair with capture.value to alert on any of these paths. $Url = "YOUR_URL/disk" $Disks = @{} $Volumes = @{} $AnyUnhealthy = $false foreach ($d in (Get-Disk -ErrorAction SilentlyContinue)) { $diskKey = "disk_$($d.Number)" # Match the PhysicalDisk row for MediaType (SSD/HDD/Unspecified) and a # second opinion on health. DeviceId on PhysicalDisk is a string version # of the disk number. $pd = Get-PhysicalDisk -ErrorAction SilentlyContinue | Where-Object { "$($_.DeviceId)" -eq "$($d.Number)" } | Select-Object -First 1 $partitions = @{} $letters = @() foreach ($p in (Get-Partition -DiskNumber $d.Number -ErrorAction SilentlyContinue)) { $partKey = "p$($p.PartitionNumber)" $partInfo = @{ number = [int]$p.PartitionNumber size_gb = [math]::Round($p.Size / 1GB, 2) type = "$($p.Type)" letter = $null label = $null filesystem = $null size_gb_fs = $null free_gb = $null used_gb = $null free_pct = $null } if ($p.DriveLetter) { $letter = "$($p.DriveLetter)" $partInfo.letter = $letter $letters += $letter $vol = Get-Volume -DriveLetter $p.DriveLetter -ErrorAction SilentlyContinue if ($vol -and $vol.Size -gt 0) { $size = [double]$vol.Size $rem = [double]$vol.SizeRemaining $partInfo.label = "$($vol.FileSystemLabel)" $partInfo.filesystem = "$($vol.FileSystem)" $partInfo.size_gb_fs = [math]::Round($size / 1GB, 2) $partInfo.free_gb = [math]::Round($rem / 1GB, 2) $partInfo.used_gb = [math]::Round(($size - $rem) / 1GB, 2) $partInfo.free_pct = [math]::Round(($rem / $size) * 100, 1) # Also publish at top level under volumes. so capture.value # paths can stay short: "volumes.C.free_gb" reads better than # "disks.disk_0.partitions.p3.free_gb". $Volumes[$letter] = @{ disk_number = [int]$d.Number partition = [int]$p.PartitionNumber label = "$($vol.FileSystemLabel)" filesystem = "$($vol.FileSystem)" size_gb = [math]::Round($size / 1GB, 2) free_gb = [math]::Round($rem / 1GB, 2) used_gb = [math]::Round(($size - $rem) / 1GB, 2) free_pct = [math]::Round(($rem / $size) * 100, 1) } } } $partitions[$partKey] = $partInfo } $health = if ($pd) { "$($pd.HealthStatus)" } else { "$($d.HealthStatus)" } $operational = if ($pd) { "$($pd.OperationalStatus)" } else { "$($d.OperationalStatus)" } if ($health -and $health -ne 'Healthy') { $AnyUnhealthy = $true } $Disks[$diskKey] = @{ number = [int]$d.Number model = "$($d.FriendlyName)" serial = "$($d.SerialNumber)" bus_type = "$($d.BusType)" # USB, SATA, NVMe, SCSI, RAID, ... media_type = if ($pd) { "$($pd.MediaType)" } else { 'Unknown' } # SSD, HDD, Unspecified size_gb = [math]::Round($d.Size / 1GB, 2) health = $health operational = $operational drive_letters = @($letters) partition_count = $partitions.Count partitions = $partitions } } $Body = (@{ hostname = $env:COMPUTERNAME disk_count = $Disks.Count volume_count = $Volumes.Count any_unhealthy = $AnyUnhealthy disks = $Disks volumes = $Volumes }) | ConvertTo-Json -Compress -Depth 10 Invoke-RestMethod -Uri $Url -Method Post -Body $Body ` -ContentType "application/json" -TimeoutSec 30 | Out-Null