Public/Get-StorageVolume.ps1
<#
.SYNOPSIS Enumerates all of the local volumes of the system. .DESCRIPTION Enumerates all of the local volumes of the system. .NOTES This function is pulled directly from the real Microsoft Windows Admin Center PowerShell scripts use rights (according to Microsoft): We grant you a non-exclusive, royalty-free right to use, modify, reproduce, and distribute the scripts provided herein. ANY SCRIPTS PROVIDED BY MICROSOFT ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS OR A PARTICULAR PURPOSE. .ROLE Readers .PARAMETER VolumeId This parameter is OPTIONAL. TODO .EXAMPLE # Open an elevated PowerShell Session, import the module, and - PS C:\Users\zeroadmin> Get-StorageVolume #> function Get-StorageVolume { param ( [Parameter(Mandatory = $false)] [String] $VolumeId ) ############################################################################################################################ # Global settings for the script. ############################################################################################################################ $ErrorActionPreference = "Stop" Set-StrictMode -Version 3.0 Import-Module CimCmdlets Import-Module Microsoft.PowerShell.Management Import-Module Microsoft.PowerShell.Utility Import-Module Storage ############################################################################################################################ # Helper functions. ############################################################################################################################ <# .Synopsis Name: Get-VolumePathToPartition Description: Gets the list of partitions (that have volumes) in hashtable where key is volume path. .Returns The list of partitions (that have volumes) in hashtable where key is volume path. #> function Get-VolumePathToPartition { $volumePaths = @{} foreach($partition in Get-Partition) { foreach($volumePath in @($partition.AccessPaths)) { if($volumePath -and (-not $volumePaths.Contains($volumePath))) { $volumePaths.Add($volumePath, $partition) } } } $volumePaths } <# .Synopsis Name: Get-DiskIdToDisk Description: Gets the list of all the disks in hashtable where key is: "Disk.Path" in case of WS2016 and above. OR "Disk.ObjectId" in case of WS2012 and WS2012R2. .Returns The list of partitions (that have volumes) in hashtable where key is volume path. #> function Get-DiskIdToDisk { $diskIds = @{} $isDownlevel = [Environment]::OSVersion.Version.Major -lt 10; # In downlevel Operating systems. MSFT_Partition.DiskId is equal to MSFT_Disk.ObjectId # However, In WS2016 and above, MSFT_Partition.DiskId is equal to MSFT_Disk.Path foreach($disk in Get-Disk) { if($isDownlevel) { $diskId = $disk.ObjectId } else { $diskId = $disk.Path } if(-not $diskIds.Contains($diskId)) { $diskIds.Add($diskId, $disk) } } return $diskIds } <# .Synopsis Name: Get-VolumeWs2016AndAboveOS Description: Gets the list of all applicable volumes from WS2012 and Ws2012R2 Operating Systems. .Returns The list of all applicable volumes #> function Get-VolumeDownlevelOS { $volumes = @() foreach($volume in (Get-WmiObject -Class MSFT_Volume -Namespace root/Microsoft/Windows/Storage)) { $partition = $script:partitions.Get_Item($volume.Path) # Check if this volume is associated with a partition. if($partition) { # If this volume is associated with a partition, then get the disk to which this partition belongs. $disk = $script:disks.Get_Item($partition.DiskId) # If the disk is a clustered disk then simply ignore this volume. if($disk -and $disk.IsClustered) {continue} } $volumes += $volume } $volumes } <# .Synopsis Name: Get-VolumeWs2016AndAboveOS Description: Gets the list of all applicable volumes from WS2016 and above Operating System. .Returns The list of all applicable volumes #> function Get-VolumeWs2016AndAboveOS { $volumes = @() $applicableVolumePaths = @{} $subSystem = Get-CimInstance -ClassName MSFT_StorageSubSystem -Namespace root/Microsoft/Windows/Storage| Where-Object { $_.FriendlyName -like "Win*" } foreach($volume in @($subSystem | Get-CimAssociatedInstance -ResultClassName MSFT_Volume)) { if(-not $applicableVolumePaths.Contains($volume.Path)) { $applicableVolumePaths.Add($volume.Path, $null) } } foreach($volume in (Get-WmiObject -Class MSFT_Volume -Namespace root/Microsoft/Windows/Storage)) { if(-not $applicableVolumePaths.Contains($volume.Path)) { continue } $volumes += $volume } $volumes } <# .Synopsis Name: Get-VolumesList Description: Gets the list of all applicable volumes w.r.t to the target Operating System. .Returns The list of all applicable volumes. #> function Get-VolumesList { $isDownlevel = [Environment]::OSVersion.Version.Major -lt 10; if($isDownlevel) { return Get-VolumeDownlevelOS } Get-VolumeWs2016AndAboveOS } ############################################################################################################################ # Helper Variables ############################################################################################################################ $script:fixedDriveType = 3 $script:disks = Get-DiskIdToDisk $script:partitions = Get-VolumePathToPartition ############################################################################################################################ # Main script. ############################################################################################################################ $resultantVolumes = @() $volumes = Get-VolumesList foreach($volume in $volumes) { $partition = $script:partitions.Get_Item($volume.Path) if($partition -and $volume.DriveType -eq $script:fixedDriveType) { $volume | Add-Member -NotePropertyName IsSystem -NotePropertyValue $partition.IsSystem $volume | Add-Member -NotePropertyName IsBoot -NotePropertyValue $partition.IsBoot $volume | Add-Member -NotePropertyName IsActive -NotePropertyValue $partition.IsActive $volume | Add-Member -NotePropertyName PartitionNumber -NotePropertyValue $partition.PartitionNumber $volume | Add-Member -NotePropertyName DiskNumber -NotePropertyValue $partition.DiskNumber } else { # This volume is not associated with partition, as such it is representing devices like CD-ROM, Floppy drive etc. $volume | Add-Member -NotePropertyName IsSystem -NotePropertyValue $true $volume | Add-Member -NotePropertyName IsBoot -NotePropertyValue $true $volume | Add-Member -NotePropertyName IsActive -NotePropertyValue $true $volume | Add-Member -NotePropertyName PartitionNumber -NotePropertyValue -1 $volume | Add-Member -NotePropertyName DiskNumber -NotePropertyValue -1 } $resultantVolumes += $volume } $resultantVolumes | % { [String] $name = ''; # On the downlevel OS, the drive letter is showing charachter. The ASCII code for that char is 0. # So rather than checking null or empty, code is checking the ASCII code of the drive letter and updating # the drive letter field to null explicitly to avoid discrepencies on UI. if ($_.FileSystemLabel -and [byte]$_.DriveLetter -ne 0 ) { $name = $_.FileSystemLabel + " (" + $_.DriveLetter + ":)" } elseif (!$_.FileSystemLabel -and [byte]$_.DriveLetter -ne 0 ) { $name = "(" + $_.DriveLetter + ":)" } elseif ($_.FileSystemLabel -and [byte]$_.DriveLetter -eq 0) { $name = $_.FileSystemLabel } else { $name = '' } if ([byte]$_.DriveLetter -eq 0) { $_.DriveLetter = $null } $_ | Add-Member -Force -NotePropertyName "Name" -NotePropertyValue $name } $isDownlevel = [Environment]::OSVersion.Version.Major -lt 10; $resultantVolumes = $resultantVolumes | ForEach-Object { $volume = @{ Name = $_.Name; DriveLetter = $_.DriveLetter; HealthStatus = $_.HealthStatus; DriveType = $_.DriveType; FileSystem = $_.FileSystem; FileSystemLabel = $_.FileSystemLabel; Path = $_.Path; PartitionNumber = $_.PartitionNumber; DiskNumber = $_.DiskNumber; Size = $_.Size; SizeRemaining = $_.SizeRemaining; IsSystem = $_.IsSystem; IsBoot = $_.IsBoot; IsActive = $_.IsActive; } if ($isDownlevel) { $volume.FileSystemType = $_.FileSystem; } else { $volume.FileSystemType = $_.FileSystemType; $volume.OperationalStatus = $_.OperationalStatus; $volume.HealthStatus = $_.HealthStatus; $volume.DriveType = $_.DriveType; $volume.DedupMode = $_.DedupMode; $volume.UniqueId = $_.UniqueId; $volume.AllocationUnitSize = $_.AllocationUnitSize; } return $volume; } # # Return results back to the caller. # if($VolumeId) { $resultantVolumes | Where-Object {$_.Path -eq $resultantVolumes} } else { $resultantVolumes } } |