In the latest version, v1.2 as of 2015-03-01, it also maps the physical disk index to the mount points and/or drive letters. This was not entirely trivial - and a PowerShell solution didn't exist in an easily accessible, public form until this early March / late February 2015 weekend. Read more in the "change log" in the downloads section (except the formatting is messed up, sorry...).
Note added 2022-02-28: I plan to rewrite this with a proper design. Looks like I put it off for 7 years so far. Will I make 8? The code does work, but it is not well-designed. The latched-on, monkey-patched PSRemoting to add the disk class is ugly.
This has a few side-effects, such as filtering out the default "System Reserved" partition entry (introduced in Win 7 / Server 2008 R2, I think) that's listed if you only look at the Win32_Volume class and filter on something like the DriveLetter property not being set.
It uses WMI against remote computers with the -ComputerName parameter. Specify the name "localhost" for data about the local host. You can specify multiple servers separated by commas. I'll look into writing a CIM version.I also added a parameter called -IncludeRootDrives which allows you to look at regular disk drive letters on the desired servers as well, in the same way.
The parameter -IncludeDiskInfo (v1.2+) requires PSRemoting to work.Output is by default sorted on free available space in percent, then descendingly on drive size (if the percentage is exactly the same), then by the label property and finally by caption.
I implemented it as functions, but didn't package it as a module. You can dot-source the ps1 file you download to get the functions in the currently available scope, or put it in your PowerShell profile. It is also possible to name the file Get-MountPointData.psm1 and put it in a subfolder of one of your $env:PSModulePath folders; if so, you would later import it with Import-Module, or have it autoloaded with PSv3 and up. You can also import it from a relative path with Import-Module.The script is written so it's fairly easy to add other properties that you might want to list.
''2015-03-01: Quickly spun around and added -Credential support for the Invoke-Command that's used when you supply the -IncludeDiskInfo parameter. The rest is WMI. I should look into a CIM version.''
''2014-11-07: Uploaded a new version I wrote months ago when I needed math operations on the space. Use the parameter -NoFormat for that. Also added support for specifying credentials with -Credential or -PromptForCredentials''
PS C:\> . .\scripts\Get-MountPointData.ps1 PS C:\> Get-MountPointData -ComputerName file_server01 | Format-Table -AutoSize Computer Label Caption FileSystem Size (GB) Free space Percent free -------- ----- ------- ---------- --------- ---------- ------------ file_server01 Dept0001 E:\foo\Dept0001 NTFS 250.00 37.73 15.09 file_server01 Dept0002 E:\foo\Dept0002 NTFS 500.00 83.13 16.63 file_server01 Dept0003 E:\foo\Dept0003 NTFS 350.00 73.38 20.97 file_server01 Dept0004 E:\bar\Dept0004 NTFS 150.00 50.37 33.58 file_server01 Dept0005 E:\bar\Dept0005 NTFS 150.00 56.89 37.93 file_server01 Dept0006 E:\bar\Dept0006 NTFS 300.00 124.42 41.47 file_server01 Dept0007 E:\baz\Dept0007 NTFS 100.00 45.10 45.11 file_server01 Dept0008 E:\baz\Dept0008 NTFS 70.00 31.90 45.57 file_server01 Dept0009 E:\foo\Dept0009 NTFS 300.00 157.08 52.36 file_server01 Dept0010 E:\foo\Dept0010 NTFS 200.00 109.37 54.68 file_server01 Dept0011 E:\foo\Dept0011 NTFS 300.00 191.93 63.98
PS C:\> Get-MountPointData -Comp filesrv2 -IncludeRootDrives | ft -a Computer Label Caption FileSystem Size (GB) Free space Percent free -------- ----- ------- ---------- --------- ---------- ------------ filesrv2 H:\ 0.00 0.00 filesrv2 USER D:\ NTFS 1,945.59 157.36 8.09 filesrv2 User3 F:\ NTFS 711.99 85.11 11.95 filesrv2 User4 G:\ NTFS 711.99 115.16 16.17 filesrv2 User2 E:\ NTFS 549.99 137.36 24.97 filesrv2 C:\ NTFS 48.83 22.24 45.54
"H:\" is an optical drive. The drives are sorted descendingly after percentage of free space by default, but you can use -Noformat and sort on size, free space, multiple values, or whatever you want.
To sort by drive letter, you can add "Sort Computer, Caption" to the pipeline before Format-Table -AutoSize. If you only target a single server, you can omit the "Computer" property and sort by caption only.
PS C:\> Get-MountPointData -Comp filesrv2 -IncludeRootDrives | Sort Computer, Caption | ft -a Computer Label Caption FileSystem Size (GB) Free space Percent free -------- ----- ------- ---------- --------- ---------- ------------ filesrv2 C:\ NTFS 48.83 22.24 45.54 filesrv2 USER D:\ NTFS 1,945.59 157.36 8.09 filesrv2 User2 E:\ NTFS 549.99 137.36 24.97 filesrv2 User3 F:\ NTFS 711.99 85.11 11.95 filesrv2 User4 G:\ NTFS 711.99 115.16 16.17 filesrv2 H:\ 0.00 0.00
### Copyright (c) 2012-2014, Svendsen Tech ### Author: Joakim Svendsen ### Get-MountPointData v1.2 # "Change history": 1.0 -> 1.1 = -Credential, -PromptForCredentials and -NoFormat (the latter allows math operations) # --- " ---: 1.1 -> 1.2 = -IncludeDiskInfo to show physical disk index number (this was not so trivial). # Convert from one device ID format to another. function Get-DeviceIDFromMP { param([Parameter(Mandatory=$true)][string] $VolumeString, [Parameter(Mandatory=$true)][string] $Directory) if ($VolumeString -imatch '^\s*Win32_Volume\.DeviceID="([^"]+)"\s*$') { # Return it in the wanted format. $Matches[1] -replace '\\{2}', '\' } else { # Return a presumably unique hashtable key if there's no match. "Unknown device ID for " + $Directory } } # Thanks to Justin Rich (jrich523) for this C# snippet. # https://jrich523.wordpress.com/2015/02/27/powershell-getting-the-disk-drive-from-a-volume-or-mount-point/ $STGetDiskClass = @" using System; using Microsoft.Win32.SafeHandles; using System.IO; using System.Runtime.InteropServices; public class STGetDisk { private const uint IoctlVolumeGetVolumeDiskExtents = 0x560000; [StructLayout(LayoutKind.Sequential)] public struct DiskExtent { public int DiskNumber; public Int64 StartingOffset; public Int64 ExtentLength; } [StructLayout(LayoutKind.Sequential)] public struct DiskExtents { public int numberOfExtents; public DiskExtent first; } [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern SafeFileHandle CreateFile( string lpFileName, [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess, [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, IntPtr lpSecurityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)] private static extern bool DeviceIoControl( SafeFileHandle hDevice, uint IoControlCode, [MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer, uint nInBufferSize, ref DiskExtents OutBuffer, int nOutBufferSize, ref uint pBytesReturned, IntPtr Overlapped ); public static string GetPhysicalDriveString(string path) { //clean path up path = path.TrimEnd('\\'); if (!path.StartsWith(@"\\.\")) path = @"\\.\" + path; SafeFileHandle shwnd = CreateFile(path, FileAccess.Read, FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero); if (shwnd.IsInvalid) { //Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error()); Exception e = Marshal.GetExceptionForHR(Marshal.GetLastWin32Error()); } uint bytesReturned = new uint(); DiskExtents de1 = new DiskExtents(); bool result = DeviceIoControl(shwnd, IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, ref de1, Marshal.SizeOf(de1), ref bytesReturned, IntPtr.Zero); shwnd.Close(); if (result) return @"\\.\PhysicalDrive" + de1.first.DiskNumber; return null; } } "@ try { Add-Type -TypeDefinition $STGetDiskClass -ErrorAction Stop } catch { if (-not $Error[0].Exception -like '*The type name * already exists*') { Write-Warning -Message "Error adding [STGetDisk] class locally." } } function Get-MountPointData { [CmdletBinding( DefaultParameterSetName='NoPrompt' )] param( [Parameter(Mandatory=$true)][string[]] $ComputerName, [Parameter(ParameterSetName='Prompt')][switch] $PromptForCredentials, [Parameter(ParameterSetName='NoPrompt')][System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [switch] $IncludeRootDrives, [switch] $NoFormat, [switch] $IncludeDiskInfo ) foreach ($Computer in $ComputerName) { $WmiHash = @{ ComputerName = $Computer ErrorAction = 'Stop' } #if ($PromptForCredentials -and $Credential.Username) { # Write-Warning "You specified both -PromptForCredentials and -Credential. Prompting overrides." #} if ($PSCmdlet.ParameterSetName -eq 'Prompt') { $WmiHash.Credential = Get-Credential } elseif ($Credential.Username) { $WmiHash.Credential = $Credential } try { # Collect mount point device IDs and populate a hashtable with IDs as keys $MountPointData = @{} Get-WmiObject @WmiHash -Class Win32_MountPoint | Where-Object { if ($IncludeRootDrives) { $true } else { $_.Directory -NotMatch '^\s*Win32_Directory\.Name="[a-z]:\\{2}"\s*$' } } | ForEach-Object { $MountPointData.(Get-DeviceIDFromMP -VolumeString $_.Volume -Directory $_.Directory) = $_.Directory } $Volumes = @(Get-WmiObject @WmiHash -Class Win32_Volume | Where-Object { if ($IncludeRootDrives) { $true } else { -not $_.DriveLetter } } | Select-Object Label, Caption, Capacity, FreeSpace, FileSystem, DeviceID, @{n='Computer';e={$Computer}} ) } catch { Write-Error "${Computer}: Terminating WMI error (skipping): $_" continue } if (-not $Volumes.Count) { Write-Error "${Computer}: No mount points found. Skipping." continue } if ($PSBoundParameters['IncludeDiskInfo']) { $DiskDriveWmiInfo = Get-WmiObject @WmiHash -Class Win32_DiskDrive } $Volumes | ForEach-Object { if ($MountPointData.ContainsKey($_.DeviceID)) { # Let's avoid dividing by zero, it's so disruptive. if ($_.Capacity) { $PercentFree = $_.FreeSpace*100/$_.Capacity } else { $PercentFree = 0 } $_ | Select-Object -Property DeviceID, Computer, Label, Caption, FileSystem, @{n='Size (GB)';e={$_.Capacity/1GB}}, @{n='Free space';e={$_.FreeSpace/1GB}}, @{n='Percent free';e={$PercentFree}} } } | Sort-Object -Property 'Percent free', @{Descending=$true;e={$_.'Size (GB)'}}, Label, Caption | Select-Object -Property $(if ($NoFormat) { @{n='ComputerName'; e={$_.Computer}}, @{n='Label'; e={$_.Label}}, @{n='Caption'; e={$_.Caption}}, @{n='FileSystem'; e={$_.FileSystem}}, @{n='Size (GB)'; e={$_.'Size (GB)'}}, @{n='Free space'; e={$_.'Free space'}}, @{n='Percent free'; e={$_.'Percent free'}}, $(if ($PSBoundParameters['IncludeDiskInfo']) { @{n='Disk Index'; e={ try { $ScriptBlock = { param($GetDiskClass, $DriveString) try { Add-Type -TypeDefinition $GetDiskClass -ErrorAction Stop } catch { #Write-Error -Message "${Computer}: Error creating class [STGetDisk]" return "Error creating [STGetDisk] class: $_" } return [STGetDisk]::GetPhysicalDriveString($DriveString) } if ($Credential.Username) { $PhysicalDisk = Invoke-Command -ComputerName $Computer -Credential $Credential -ScriptBlock $ScriptBlock -ArgumentList $STGetDiskClass, $(if ($_.Caption -imatch '\A[a-z]:\\\z') { $_.Caption } else { $_.DeviceID.TrimStart('\?') }) } else { $PhysicalDisk = Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -ArgumentList $STGetDiskClass, $(if ($_.Caption -imatch '\A[a-z]:\\\z') { $_.Caption } else { $_.DeviceID.TrimStart('\?') }) } if ($PhysicalDisk -like 'Error*') { "Error: $PhysicalDisk" } else { ($DiskDriveWmiInfo | Where-Object { $PhysicalDisk } | Where-Object { $PhysicalDisk.Trim() -eq $_.Name } | Select-Object -ExpandProperty Index) -join '; ' } } catch { "Error: $_" } } # end of disk index expression } # end of if disk index hashtable }) # end of if includediskinfo parameter subexpression and if } else { @{n='ComputerName'; e={$_.Computer}}, @{n='Label'; e={$_.Label}}, @{n='Caption'; e={$_.Caption}}, @{n='FileSystem'; e={$_.FileSystem}}, @{n='Size (GB)'; e={$_.'Size (GB)'.ToString('N')}}, @{n='Free space'; e={$_.'Free space'.ToString('N')}}, @{n='Percent free'; e={$_.'Percent free'.ToString('N')}}, $(if ($PSBoundParameters['IncludeDiskInfo']) { @{n='Disk Index'; e={ try { $ScriptBlock = { param($GetDiskClass, $DriveString) try { Add-Type -TypeDefinition $GetDiskClass -ErrorAction Stop } catch { #Write-Error -Message "${Computer}: Error creating class [STGetDisk]" return "Error creating [STGetDisk] class: $_" } return [STGetDisk]::GetPhysicalDriveString($DriveString) } if ($Credential.Username) { $PhysicalDisk = Invoke-Command -ComputerName $Computer -Credential $Credential -ScriptBlock $ScriptBlock -ArgumentList $STGetDiskClass, $(if ($_.Caption -imatch '\A[a-z]:\\\z') { $_.Caption } else { $_.DeviceID.TrimStart('\?') }) } else { $PhysicalDisk = Invoke-Command -ComputerName $Computer -ScriptBlock $ScriptBlock -ArgumentList $STGetDiskClass, $(if ($_.Caption -imatch '\A[a-z]:\\\z') { $_.Caption } else { $_.DeviceID.TrimStart('\?') }) } if ($PhysicalDisk -like 'Error*') { "Error: $PhysicalDisk" } else { ($DiskDriveWmiInfo | Where-Object { $PhysicalDisk } | Where-Object { $PhysicalDisk.Trim() -eq $_.Name } | Select-Object -ExpandProperty Index) -join '; ' } } catch { "Error: $_" } } # end of disk index expression } # end of if disk index hashtable }) # end of if includediskinfo parameter subexpression and if }) # end of if $NoFormat } }Powershell Windows All Categories
Minimum cookies is the standard setting. This website uses Google Analytics and Google Ads, and these products may set cookies. By continuing to use this website, you accept this.