15

I have this query which scans all logical disks information :

Write-Host "Drive information for $env:ComputerName"

Get-WmiObject -Class Win32_LogicalDisk |
    Where-Object {$_.DriveType -ne 5} |
    Sort-Object -Property Name | 
    Select-Object Name, VolumeName, VolumeSerialNumber,SerialNumber, FileSystem, Description, VolumeDirty, `
        @{"Label"="DiskSize(GB)";"Expression"={"{0:N}" -f ($_.Size/1GB) -as [float]}}, `
        @{"Label"="FreeSpace(GB)";"Expression"={"{0:N}" -f ($_.FreeSpace/1GB) -as [float]}}, `
        @{"Label"="%Free";"Expression"={"{0:N}" -f ($_.FreeSpace/$_.Size*100) -as [float]}} |
    Format-Table -AutoSize

The output is :

enter image description here

However - I'm after the physical disks information and their partitions / volume information :

So - for physical disks I have this command :

Get-Disk

Result :

enter image description here

Question :

I want to combine between those 2 commands . I want to see the Disk , and below each disk - its logical disk information :

  • Disk Number 1 : ....(info)
    >Its logical disks info.....
  • Disk Number 2 : ....(info)
    >It's logical disks info.....
  • Disk Number 3 : ....(info)
    >It's logical disks info.....
  • etc...

How can I combine between those 2 queries ?

mklement0
  • 382,024
  • 64
  • 607
  • 775
Royi Namir
  • 144,742
  • 138
  • 468
  • 792
  • I had a similar question a while back, dunno if this answer helps: http://serverfault.com/a/571669/822 – Kev Jun 27 '15 at 16:32
  • @Kev Thanks but it's not helping me much. Drive letters are product of partition. I dont want to go from drive letter to its properties. I want to go from physical Disk's -----> their volumes – Royi Namir Jun 27 '15 at 17:00

3 Answers3

36

You need to query several WMI classes to get all information you want.

Partitions can be mapped to their disks using the Win32_DiskDriveToDiskPartition class, and drives can be mapped to their partitions via the Win32_LogicalDiskToPartition class.

Get-WmiObject Win32_DiskDrive | ForEach-Object {
  $disk = $_
  $partitions = "ASSOCIATORS OF " +
                "{Win32_DiskDrive.DeviceID='$($disk.DeviceID)'} " +
                "WHERE AssocClass = Win32_DiskDriveToDiskPartition"
  Get-WmiObject -Query $partitions | ForEach-Object {
    $partition = $_
    $drives = "ASSOCIATORS OF " +
              "{Win32_DiskPartition.DeviceID='$($partition.DeviceID)'} " +
              "WHERE AssocClass = Win32_LogicalDiskToPartition"
    Get-WmiObject -Query $drives | ForEach-Object {
      New-Object -Type PSCustomObject -Property @{
        Disk        = $disk.DeviceID
        DiskSize    = $disk.Size
        DiskModel   = $disk.Model
        Partition   = $partition.Name
        RawSize     = $partition.Size
        DriveLetter = $_.DeviceID
        VolumeName  = $_.VolumeName
        Size        = $_.Size
        FreeSpace   = $_.FreeSpace
      }
    }
  }
}
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Excellent. Exactly what I needed.Thank you. – Royi Namir Jun 27 '15 at 19:19
  • Last question : I've managed to display the info in GB. so the number is fine. but I want to add postfix of text ("`(GB)`") like `123.45(GB)`. but I probably missing something. what should I change ? look here : http://i.imgur.com/95QyaI4.png – Royi Namir Jun 27 '15 at 19:32
  • 1
    If you want a number displayed as `x GB` you must convert it to a string, e.g. `'{0:d} GB' -f [int]($_.Size / 1GB)` – Ansgar Wiechers Jun 27 '15 at 20:09
  • Is it possible to do this with wmic commands from the system prompt? – simgineer Dec 19 '16 at 01:48
  • Probably. But writing the queries for `wmic` would be a pain in the rear, so I can't be bothered. – Ansgar Wiechers Dec 19 '16 at 12:24
  • Can I trust that WMI is always working on every machine? – karliwson Feb 17 '18 at 19:29
  • @karliwson Under normal circumstances yes. Beware of firewall issues for remote WMI connections, though. – Ansgar Wiechers Mar 04 '18 at 12:44
  • How do you change the order of items in the PSCustomObject ? For instance order them by name? – Rakha May 04 '18 at 15:32
  • 1
    figured it out! If you want your list of items ORDERED, use " [PSCustomObject][Ordered]@{" instead of " New-Object -Type PSCustomObject -Property @{" – Rakha May 04 '18 at 15:42
  • How is it possible to change the output `DriveLetter : W:` to `Drive Letter : W:` therefore formatting the `DriveLetter, RawSize, VolumeName, FreeSpace, DiskModel` parts so there's a space like my example? – Ste Aug 02 '20 at 16:44
  • I got my answers for that here in case anyone wants. https://stackoverflow.com/questions/63219836/combine-get-disk-info-and-logicaldisk-info-in-powershell-but-with-formatted Also `Win32_LogicalDisk` gets all disks including network drives. This code misses out on that. Is there a way to add all drives such as the network drives too? – Ste Aug 04 '20 at 21:18
5

How about like this...

Get-CimInstance Win32_Diskdrive -PipelineVariable disk |
Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -pv partition |
Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk |
Select-Object @{n='Disk';e={$disk.deviceid}},
@{n='DiskSize';e={$disk.size}},
@{n='DiskModel';e={$disk.model}},
@{n='Partition';e={$partition.name}},
@{n='RawSize';e={$partition.size}},
@{n='DriveLetter';e={$_.DeviceID}},
VolumeName,Size,FreeSpace

Output:

Disk        : \\.\PHYSICALDRIVE0
DiskSize    : 128034708480
DiskModel   : SAMSUNG MZ7PC128HAFU-000L5
Partition   : Disk #0, Partition #0
RawSize     : 128034595328
DriveLetter : C:
VolumeName  : DISK
Size        : 128034594816
FreeSpace   : 7023042560

Unfortunately this doesn't work with more than one drive. Get-cimassociatedinstance blocks like sort-object, and only the latest pipeline variable is set. Here's a workaround:

Get-CimInstance Win32_Diskdrive -PipelineVariable disk |
% { Get-CimAssociatedInstance $_ -ResultClass Win32_DiskPartition -pv partition}|
% { Get-CimAssociatedInstance $_ -ResultClassName Win32_LogicalDisk } |
Select-Object @{n='Disk';e={$disk.deviceid}},
@{n='DiskSize';e={$disk.size}},
@{n='DiskModel';e={$disk.model}},
@{n='Partition';e={$partition.name}},
@{n='RawSize';e={$partition.size}},
@{n='DriveLetter';e={$_.DeviceID}},
VolumeName,Size,FreeSpace

Disk        : \\.\PHYSICALDRIVE0
DiskSize    : 128034708480
DiskModel   : SAMSUNG MZ7PC128HAFU-000L5
Partition   : Disk #0, Partition #0
RawSize     : 128034595328
DriveLetter : C:
VolumeName  : DISK
Size        : 128034594816
FreeSpace   : 4226514944

Disk        : \\.\PHYSICALDRIVE1
DiskSize    : 7797565440
DiskModel   : USB Flash Memory USB Device
Partition   : Disk #1, Partition #0
RawSize     : 7801405440
DriveLetter : E:
VolumeName  : WINPE
Size        : 7784628224
FreeSpace   : 7222669312
js2010
  • 23,033
  • 6
  • 64
  • 66
  • Well, this doesn't actually work for me on PS 7; it seems to be retaining the initial value of all the fields until `DriveLetter` for multiple results so basically, the first 2 commands are operating on one result specifically. To fix this I made it call the piped commands 2 and 3 for each result of the first, and that fixed it. `% { $_ | Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -PipelineVariable partition } | % { $_ | Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk } |` – Alex Fanat Nov 24 '21 at 01:32
  • @AlexFanat Works for me in PS 7.2. – js2010 Nov 24 '21 at 02:18
  • For 1 result it works, for multiple, with different physical drives, it does not, and shows same information above `DriveLetter` for all results. Try in a configuration with at least 2 physical drives with one of them having 2 partitions. Using same PS as you, 7.2.0. – Alex Fanat Nov 24 '21 at 09:17
  • 1
    @AlexFanat You're right. Get-cimassociatedinstance seems to block until the pipe is done like sort-object, so only the latest pipeline variable is set. – js2010 Nov 24 '21 at 15:14
2

Inspired js2010's answer, with some enhancements:

For instance, the Manufacturer field seems to have some a placeholder value when retrieved from the Win32_DiskDrive instance, but has the proper value when using the Get-Disk commandlet.

function Get-Drive {

  foreach($disk in Get-CimInstance Win32_Diskdrive) {

    $diskMetadata = Get-Disk | Where-Object { $_.Number -eq $disk.Index } | Select-Object -First 1

    $partitions = Get-CimAssociatedInstance -ResultClassName Win32_DiskPartition -InputObject $disk

    foreach($partition in $partitions) {

      $drives = Get-CimAssociatedInstance -ResultClassName Win32_LogicalDisk -InputObject $partition

      foreach($drive in $drives) {

        $totalSpace = [math]::Round($drive.Size / 1GB, 3)
        $freeSpace  = [math]::Round($drive.FreeSpace / 1GB, 3)
        $usedSpace  = [math]::Round($totalSpace - $freeSpace, 3)

        $volume     = Get-Volume |
                      Where-Object { $_.DriveLetter -eq $drive.DeviceID.Trim(":") } |
                      Select-Object -First 1

        [PSCustomObject] @{
                            DriveLetter   = $drive.DeviceID
                            Number        = $disk.Index

                            Label         = $volume.FileSystemLabel
                            Manufacturer  = $diskMetadata.Manufacturer
                            Model         = $diskMetadata.Model
                            SerialNumber  = $diskMetadata.SerialNumber.Trim() 
                            Name          = $disk.Caption

                            FileSystem    = $volume.FileSystem
                            PartitionKind = $diskMetadata.PartitionStyle

                            TotalSpace    = $totalSpace
                            FreeSpace     = $freeSpace
                            UsedSpace     = $usedSpace

                            Drive         = $drive
                            Partition     = $partition
                            Disk          = $disk
        }

      }
    }
  }
}           

Example Usages:

Get-Drive | Format-List
Get-Drive | Where-Object { $_.DriveLetter -eq 'G:' }

Output:

DriveLetter   : G:
Number        : 5
Label         : SE-LXY2-298GB
Manufacturer  : Seagate
Model         : FreeAgent Go
SerialNumber  : 2GE45CK2
Name          : Seagate FreeAgent Go USB Device
FileSystem    : NTFS
PartitionKind : MBR
TotalSpace    : 298.089
FreeSpace     : 297.865
UsedSpace     : 0.224
Drive         : Win32_LogicalDisk: G: (DeviceID = "G:")
Partition     : Win32_DiskPartition: Disk #5, Partition #0 (DeviceID = "Disk #5, Partition #0")
Disk          : Win32_DiskDrive: Seagate FreeAgent Go USB Device (DeviceID = "\\.\PHYSICALDRIVE5")

Raghu Dodda
  • 1,505
  • 1
  • 21
  • 28