Functions/Get-SdtVolumeInfo.ps1

Function Get-SdtVolumeInfo
{
    [CmdletBinding()]
    Param (
        [Parameter(ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)]
        [Alias('ServerName','MachineName')]
        [String[]]$ComputerName = $env:COMPUTERNAME,

        [Parameter(Mandatory=$false)]
        [ValidateSet('All','SQL','NonSQL')]
        [Alias('VolumeType')]
        [String]$Type = 'All',

        [Switch]$IncludeAllAttributes
    )

    BEGIN 
    {
        $Result = @();
        Add-Type -TypeDefinition @"
using System;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Runtime.InteropServices;
  
public class GetDisk
{
 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());
 }
  
 var bytesReturned = new uint();
 var 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;
 }
}
  
"@

    }
    PROCESS 
    {
        if ($_ -ne $null)
        {
            $ComputerName = $_;
            Write-Verbose "Parameters received from PipeLine.";
        }
        foreach ($Computer in $ComputerName)
        {
            Write-Debug "Get-SdtVolumeInfo Inside computer loop"

            if($Type -ne 'All') {
                $sqlServices = Get-Service -ComputerName $Computer -Name mssql* | Where-Object {$_.DisplayName -like 'SQL Server (*)'}
                $sqlServicesOffline = @()
                $sqlServicesOffline += $sqlServices | Where-Object {$_.Status -ne 'RUNNING'}
                $sqlServicesOnline = @()
                $sqlServicesOnline += $sqlServices | Where-Object {$_.Status -eq 'RUNNING'}
                if($sqlServicesOffline.Count -gt 0) {
                    Write-Warning "Some sql server engine services are not online on $Computer"
                }

                "$Computer => $($sqlServicesOnline -join ', ')" | Write-Verbose
                if($sqlServicesOnline.Count -gt 0) {
                    $sqlInstancesOnComputer = $sqlServicesOnline | ForEach-Object {
                                                        $instName = 'MSSQLSERVER'; 
                                                        $instSplitName = ($_.Name -split '\$')[1]; 
                                                        if(-not [String]::IsNullOrEmpty($instSplitName)) {
                                                            $instName = $instSplitName
                                                        };
                                                        if($instName -eq 'MSSQLSERVER') {$Computer} else {"$Computer\$instName"}
                                              }
                    "$Computer => $($sqlInstancesOnComputer -join ', ')" | Write-Verbose
                    $dbFileDirectories = @()
                    foreach($inst in $sqlInstancesOnComputer) {
                        try {
                            $dbFileDirectories += (Invoke-Sqlcmd -ServerInstance $inst -Query "select distinct '$Computer' as ServerName, LEFT(physical_name,LEN(physical_name)-CHARINDEX('\',REVERSE(physical_name))+1) as physical_name_directory from sys.master_files")
                        }
                        catch {
                            "Could not connect to SqlInstance '$inst'" | Write-Warning
                        }
                    }
                }
            }

            $Disks = @();
            $Volumes = @();
            $Partitions = @();
            $DiskVolumeInfo = @();

            if($IncludeAllAttributes) {
                $Volumes =  Get-WmiObject -Class win32_volume -ComputerName $Computer -Filter "DriveType=3" | Where-Object {$_.Name -notlike '\\?\*'} |
                                Select-Object -Property @{l='Computer';e={$_.PSComputerName}},
                                                        @{l='Volume Name';e={$_.Name}},
                                                        @{l='Capacity(GB)';e={$_.Capacity / 1GB -AS [INT]}},
                                                        @{l='Used Space(GB)';e={($_.Capacity - $_.FreeSpace)/ 1GB -AS [INT]}},
                                                        @{l='Used Space(%)';e={((($_.Capacity - $_.FreeSpace) / $_.Capacity) * 100) -AS [INT]}},
                                                        @{l='FreeSpace(GB)';e={$_.FreeSpace / 1GB -AS [INT]}},
                                                        Label,
                                                        @{l='BlockSize(KB)';e={$_.BlockSize / 1KB -AS [INT]}},
                                                        @{l='DeviceID';e={$($_.DeviceID).Replace("\\?\",'').Replace("\",'')}};
            }
            else {
                $Volumes =  Get-WmiObject -Class win32_volume -ComputerName $Computer -Filter "DriveType=3" | Where-Object {$_.Name -notlike '\\?\*'} |
                                Select-Object -Property @{l='Computer';e={$_.PSComputerName}},
                                                        @{l='Volume Name';e={$_.Name}},
                                                        @{l='Capacity(GB)';e={$_.Capacity / 1GB -AS [INT]}},
                                                        @{l='Used Space(GB)';e={($_.Capacity - $_.FreeSpace)/ 1GB -AS [INT]}},
                                                        @{l='Used Space(%)';e={((($_.Capacity - $_.FreeSpace) / $_.Capacity) * 100) -AS [INT]}},
                                                        @{l='FreeSpace(GB)';e={$_.FreeSpace / 1GB -AS [INT]}},
                                                        Label,
                                                        @{l='BlockSize(KB)';e={$_.BlockSize / 1KB -AS [INT]}}
            }

            if($Type -ne 'All') {
                $sqlVolumes = @()
                $sqlVolumes += $dbFileDirectories | % {$matchedVolumes = @()} {
                            $directory = $_; $matchedVolume = $null; $matchedVolumeLength = 0;
                            foreach($vol in $Volumes) {
                                if($directory.physical_name_directory -like "$($vol.'Volume Name')*" -and $vol.'Volume Name'.Length -gt $matchedVolumeLength) {$matchedVolume = $vol.'Volume Name'}
                            }
                            $matchedVolume
                        } | Select-Object @{l='VolumeName';e={$_}} -Unique
            }
            
            if($IncludeAllAttributes) {
                $Partitions = Get-WmiObject -Class win32_LogicalDiskToPartition -ComputerName $Computer |
                                    Select-Object @{l='DiskID';e={if($_.Antecedent -match "Win32_DiskPartition.DeviceID=`"Disk\s#(?'DiskID'\d{1,3}),\sPartition\s#(?'PartitionNo'\d{1,3})") {$Matches['DiskID']} else {$null} }},
                                                    @{l='PartitionNo';e={if($_.Antecedent -match "Win32_DiskPartition.DeviceID=`"Disk\s#(?'DiskID'\d{1,3}),\sPartition\s#(?'PartitionNo'\d{1,3})") {$Matches['PartitionNo']} else {$null} }},
                                                    @{l='VolumeName';e={if($_.Dependent -match "Win32_LogicalDisk.DeviceID=`"(?'VolumeName'[a-zA-Z]:)`"") {$Matches['VolumeName']+'\'} else {$null} }}

                $Disks = Get-WmiObject -Class win32_DiskDrive -ComputerName $Computer |
                                Select-Object -Property @{l='Is_SAN_Disk';e={if(($_.PNPDeviceID).Split('\')[0] -in @('SCSI')){'No'}else{'Yes'}}}, `
                                                        @{l='DiskID';e={[int32]($_.Index)}}, `
                                                        @{l='DiskModel';e={$_.Model}}, `
                                                        @{l='LUN';e={$_.SCSILogicalUnit}};
           
                $VolPart = Join-SdtObject -Left $Volumes -Right $Partitions -LeftJoinProperty 'Volume Name' -RightJoinProperty VolumeName -Type AllInLeft -RightProperties @{l='DiskID';e={[int32]($_.DiskID)}},PartitionNo;
                $VolPart = $VolPart | % {$vol = $_; if([String]::IsNullOrEmpty($_.DiskID)) { $diskIDRaw = [GetDisk]::GetPhysicalDriveString($_.DeviceID); if(-not [String]::IsNullOrEmpty($diskIDRaw)) {$_.DiskID = [int32]($diskIDRaw).Replace('\\.\PhysicalDrive','')} }; $_}

                $DiskVolumeInfo = Join-SdtObject -Left $VolPart -Right $Disks -LeftJoinProperty DiskID -RightJoinProperty DiskID -Type AllInLeft -RightProperties Is_SAN_Disk, LUN, DiskModel, DiskID
            }
            else {
                $DiskVolumeInfo = $Volumes
            }

            $DiskVolumeInfoFiltered = @();
            if($Type -ne 'All') {            
                if($Type -eq 'SQL') {
                    $DiskVolumeInfoFiltered += $DiskVolumeInfo | Where-Object {($_.'Volume Name' -in ($sqlVolumes.VolumeName))}
                }
                else {
                    $DiskVolumeInfoFiltered += $DiskVolumeInfo | Where-Object {-not ($_.'Volume Name' -in ($sqlVolumes.VolumeName))}
                }
            }
            else {
                $DiskVolumeInfoFiltered = $DiskVolumeInfo
            }

            $Result += $DiskVolumeInfoFiltered 

            <#
            foreach ($diskInfo in $DiskVolumeInfoFiltered)
            {
                $props = [Ordered]@{ 'ComputerName'=$diskInfo.ComputerName;
                                    'VolumeName'= $diskInfo.VolumeName;
                                    'Capacity(GB)'= $diskInfo.'Capacity(GB)';
                                    'Used Space(GB)'= $diskInfo.'Used Space(GB)';
                                    'Used Space(%)'= $diskInfo.'Used Space(%)';
                                    'FreeSpace(GB)'= $diskInfo.'FreeSpace(GB)';
                                    'Label'=$diskInfo.Label;
                                    #'IsSQLDisk' = ($diskInfo.VolumeName -in ($sqlVolumes.VolumeName));
                                    'Block Size(KB)'=$diskInfo.'BlockSize(KB)';
                                    #'Is_SAN_Disk' = $diskInfo.Is_SAN_Disk;
                                    'DiskID'=$diskInfo.DiskID;
                                    'LUN'=$diskInfo.LUN;
                                    'DiskModel'=$diskInfo.DiskModel;
                                  };
 
                $obj = New-Object -TypeName psobject -Property $props;
                $Result += $obj;
            }
            #>

        }
    }
    END 
    {
        Write-Output $Result | Sort-Object -Property Computer, 'Volume Name'
    }
<#
    .SYNOPSIS
      Displays all disk drives for computer(s) passed in pipeline or as value
    .DESCRIPTION
      This function return list of disk drives including mounted volumes with details like total size, free space, % used space etc.
    .PARAMETER ComputerName
      List of computer or machine names. This list can be passed either as computer name or through pipeline.
    .PARAMETER Type
      Default All that fetches all disk volumes. If 'SQL', then returns only volumes utilized by SQL Server database files.
    .PARAMETER IncludeAllAttributes
      Switch when used returns other attributes like disk number, partition no, LUN id etc.
    .EXAMPLE
      $servers = 'Server01','Server02';
      Get-SdtVolumeInfo $servers | ft -AutoSize;
 
      Ouput:-
ComputerName VolumeName Capacity(GB) Used Space(GB) Used Space(%) FreeSpace(GB)
------------ ---------- ------------ -------------- ------------- -------------
Server01 G:\ 559 417 75 142
Server01 C:\ 68 60 88 8
Server01 E:\ 931 273 29 658
Server01 F:\ 4488 2662 59 1826
Server02 C:\ 136 61 45 75
Server02 E:\ 279 77 28 202
Server02 F:\ 1003 863 86 140
Server02 G:\ 674 483 72 191
       
      Server names passed as parameter. Returns all the disk drives for computers Server01 & Server02.
    .EXAMPLE
      $servers = 'Server01','Server02';
      $servers | Get-SdtVolumeInfo -Type SQL | ft -AutoSize;
 
      Output:-
ComputerName VolumeName Capacity(GB) Used Space(GB) Used Space(%) FreeSpace(GB)
------------ ---------- ------------ -------------- ------------- -------------
Server01 G:\ 559 417 75 142
Server01 C:\ 68 60 88 8
Server01 E:\ 931 273 29 658
Server01 F:\ 4488 2662 59 1826
Server02 C:\ 136 61 45 75
Server02 E:\ 279 77 28 202
Server02 F:\ 1003 863 86 140
Server02 G:\ 674 483 72 191
       
      Server names passed through pipeline. Returns all the sql server disk drives for computers Server01 & Server02.
    .LINK
      https://github.com/imajaydwivedi/SQLDBATools
#>

}