Combine `Get-Disk` info and `LogicalDisk` info in PowerShell?
Asked Answered
S

4

16

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 ?

Synovitis answered 27/6, 2015 at 12:47 Comment(2)
I had a similar question a while back, dunno if this answer helps: serverfault.com/a/571669/822Baltic
@Baltic 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 volumesSynovitis
O
37

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
      }
    }
  }
}
Oehsen answered 27/6, 2015 at 18:21 Comment(10)
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 : i.imgur.com/95QyaI4.pngSynovitis
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)Oehsen
Is it possible to do this with wmic commands from the system prompt?Breeze
Probably. But writing the queries for wmic would be a pain in the rear, so I can't be bothered.Oehsen
Can I trust that WMI is always working on every machine?Political
@Political Under normal circumstances yes. Beware of firewall issues for remote WMI connections, though.Oehsen
How do you change the order of items in the PSCustomObject ? For instance order them by name?Robbirobbia
figured it out! If you want your list of items ORDERED, use " [PSCustomObject][Ordered]@{" instead of " New-Object -Type PSCustomObject -Property @{"Robbirobbia
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?Palaver
I got my answers for that here in case anyone wants. #63220336 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?Palaver
R
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
Rosetterosewall answered 24/8, 2018 at 19:20 Comment(4)
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 } |Vicenta
@AlexFanat Works for me in PS 7.2.Rosetterosewall
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.Vicenta
@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.Rosetterosewall
C
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")

Crossstaff answered 6/6, 2021 at 5:26 Comment(0)
S
0

I realize this question is pretty old, but I found it helpful but needed to go a different direction. If you have disks that don't have drive letters and are instead mounted as NTFS mount points or something like that, then the methods here won't find them. You do see them with the built-in PowerShell commands like Get-Volume,Get-Partition, and Get-Disk but those don't support the -computername param to get remote devices without using invoke-command and other methods. However, each of them is based on a ciminstance class in the "ROOT/Microsoft/Windows/Storage" namespace and you can get the same info using those classes

So I came up with this to find volumes on servers with low disk space (I actually have it in a function, but I'll just share the gist, you can remove the lines using the percent to just get all disks). Hopefully someone else finds it helpful

[string[]]$serverName = 'list','of','server','names' # you might use get-adcomputer with a filter to ou's with servers here
[decimal]$percentFreeThreshold = 9.99
$percent = $percentFreeThreshold/100;
$reports = New-Object -TypeName System.collections.generic.List['system.object']
$serverName | ForEach-Object {
    $server = $_;
    Write-Verbose "getting report for $server"
    #get volumes with specified percent threshold or less free space
    #Get the volumes with the ciminstance that get-volume uses
    $report = Get-CimInstance -className "MSFT_Volume" -Namespace "ROOT/Microsoft/Windows/Storage" -ComputerName $server;
    #the drive type returned seems to vary on how the command is run, it's either an id or a string, match either or for best results
    $report = $report | Where-Object { ($_.Drivetype -eq 3) -or ($_.DriveType -eq 'Fixed') }
    #get the volumes that are within the threshold
    $report = $report | Where-Object {($_.SizeRemaining/$_.size) -le $percent}
    #get the partitions of the server to fill the Partition value identifying the disk number and partition, the disk number helps identify what disk to expand in vctr
    #we find the matching partition based on the size of the volume
    # $partitions = Get-CimInstance -classname "win32_DiskPartition" -ComputerName $server;
    $partitions = Get-CimInstance -className "MSFT_Partition" -Namespace "ROOT/Microsoft/Windows/Storage" -ComputerName $server;

    
    # $Report = Get-CimInstance -className win32_logicaldisk -ComputerName $server -Filter "Drivetype=3" -ErrorAction SilentlyContinue | Where-Object {($_.freespace/$_.size) -le $percent}
    #select the important pieces of the report and convert the number values to human-readable GigaBytes
    if ($report) {
        Write-Verbose "$server has low disk space, adding the report"
        $view = $report | select-object -property `
            @{N='server';E={$server}},
            @{N='DriveLetter';E={if ($null -ne $_.DriveLetter) {$_.DriveLetter} else {'N/A'}}},
            @{N='Friendly Name';E={$_.FileSystemLabel}},
            @{N='Partition';E={
                $part = ($partitions | Where-Object AccessPaths -Contains $_.path);
                "Disk # $($part.DiskNumber) Partition # $($part.PartitionNumber)"
            }},
            @{N="Total Size (GB)";E={[math]::round($_.size/1GB,2)}},
            @{N='FreeSpace (GB)';E={[math]::round($_.SizeRemaining/ 1GB,2)}},
            @{N='% Free';E={[math]::round(($_.SizeRemaining/$_.size)*100,2)}}
        $reports.add($view);
    } 
}
        

Example output

server          : someFileServer
DriveLetter     : N/A
Friendly Name   : SomeDiskName
Partition       : Disk # 2 Partition # 2
Total Size (GB) : 249.98
FreeSpace (GB)  : 18.38
% Free          : 7.35

You could also take that resulting report and turn it into an html table like this

$Body = "<html><body><div>$($report | ConvertTo-Html -Fragment)</br></br></div><div>Some Message describing the low disk space report table</div></body></html>"

and use a different PowerShell module like Mailozaurr to send the report in an email to your ticketing system or something like that.

I realize I didn't include all the disk information that exists in above examples. You could add that to the report the same was I added the partition info by finding the matching partition from the volume accesspath (as in the example) then taking the diskPath in the partition object and finding the matching diskpath in the MSFT_DISK class ciminstance and that object would have serial number, make/model, partition style etc. I just don't utilize that info in my report, but you could easily add it with the patterns shown here.

Staffan answered 8/3 at 15:28 Comment(1)
Invoke-Command is the preferred method for script remoting in all versions of PowerShell released after Windows PowerShell 5.1.Aboutship

© 2022 - 2024 — McMap. All rights reserved.