site1.erralert.com

← Disk & storage

Disk inventory + free space (Windows) powershell

Enumerates every physical disk (internal + external) and every volume. Reports capacity, free space, SMART health, drive type (SSD/HDD), bus type (USB / SATA / NVMe / SCSI), Windows disk number, drive letters, and per-partition details. Pair with capture.value and pick the path that matters — e.g. <code>volumes.C.free_gb</code>, <code>disks.disk_0.health</code>, or <code>any_unhealthy</code>.

Placeholders only. Before running, replace YOUR_URL with your capture endpoint's POST URL . (Open this page from your capture object to have these auto-filled.)
# 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.<letter> 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

Recommended pairing

Add a capture.value check to this capture object.
Examples: json_path = volumes.C.free_gb, op = <, threshold = 10 (low free space on C:). json_path = any_unhealthy, op = ==, threshold = true (any failing drive, severity: crit). json_path = disks.disk_0.health, op = !=, threshold = Healthy (SMART status of physical disk 0).

What is the filename?

disk-inventory.ps1 — this is the suggested name for the downloaded file. Rename freely if you prefer.