functions/Get-DbaDiskSpace.ps1

Function Get-DbaDiskSpace
{
<#
.SYNOPSIS
Displays Disk information for all local drives on a server
 
.DESCRIPTION
Returns a custom object with Server name, name of disk, label of disk, total size, free size, percent free, block size and filesystem.
 
By default, this funtion only shows drives of type 2 and 3 (removable disk and local disk).
 
Requires: Windows administrator access on SQL Servers
 
.PARAMETER ComputerName
The SQL Server (or server in general) that you're connecting to. The -SqlInstance parameter also works.
 
.PARAMETER Unit
Display the disk space information in a specific unit. Valid values include 'Bytes', 'KB', 'MB', 'GB', 'TB', and 'PB'. Default is GB.
 
.PARAMETER CheckForSql
Check to see if any SQL Data or Log files exists on the disk. Uses Windows authentication to connect by default.
 
.PARAMETER SqlCredential
Allows you to login to servers using SQL Logins as opposed to Windows Auth/Integrated/Trusted.
 
.PARAMETER CheckFragmentation
Includes a check for fragmentation in all filesystems. This will increase the runtime of the function, as a fragmentation check can take seconds or even minutes for a single volume.
 
.PARAMETER AllDrives
Without this switch, the function will only return information about drivetype 2 and 3 (removable disk and local disk).
When this switch is used, data from all types of disks are returned:
 
 Value Type of disk
   0 Unknown
   1 No Root Directory
   2 Removable Disk
   3 Local Disk
   4 Network Drive
   5 Compact Disk
   6 RAM Disk
 
https://msdn.microsoft.com/en-us/library/aa394515.aspx
 
.PARAMETER Detailed
Includes information about filesystem (FAT32, NTFS, ReFS, etc.), as well as the information provided by -CheckForSql and -AllDrives.
Also includes volumes normally excluded, such as '\\?\Volume*' volumes.
 
.PARAMETER WhatIf
Shows what would happen if the command were to run. No actions are actually performed.
 
.PARAMETER Confirm
Prompts you for confirmation before executing any changing operations within the command.
 
.NOTES
Tags: Storage
Author: Chrissy LeMaire (clemaire@gmail.com) & Jakob Bindslet (jakob@bindslet.dk)
 
dbatools PowerShell module (https://dbatools.io, clemaire@gmail.com)
Copyright (C) 2016 Chrissy LeMaire
 
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
 
.LINK
https://dbatools.io/Get-DbaDiskSpace
 
.EXAMPLE
Get-DbaDiskSpace -ComputerName srv0042 | Format-Table -AutoSize
 
Get diskspace for the server srv0042
 
Server Name Label SizeInGB FreeInGB PercentFree BlockSize
------ ---- ----- -------- -------- ----------- ---------
srv0042 C:\ System 126,45 114,12 90,25 4096
srv0042 E:\ Data1 97,62 96,33 98,67 4096
srv0042 F:\ DATA2 29,2 29,2 100 16384
 
 
.EXAMPLE
Get-DbaDiskSpace -ComputerName srv0042 -Unit MB | Format-Table -AutoSize
 
Get diskspace for the server srv0042, display in MB
 
Server Name Label SizeInMB FreeInMB PercentFree BlockSize
------ ---- ----- -------- -------- ----------- ---------
srv0042 C:\ System 129481 116856,11 90,25 4096
srv0042 E:\ Data1 99968 98637,56 98,67 4096
srv0042 F:\ DATA2 29901 29900,92 100 16384
 
 
.EXAMPLE
Get-DbaDiskSpace -ComputerName srv0042, srv0007 -Unit TB | Format-Table -AutoSize
 
Get diskspace from two servers, display in TB
 
Server Name Label SizeInTB FreeInTB PercentFree BlockSize
------ ---- ----- -------- -------- ----------- ---------
srv0042 C:\ System 0,12 0,11 90,25 4096
srv0042 E:\ Data1 0,1 0,09 98,67 4096
srv0042 F:\ DATA2 0,03 0,03 100 16384
srv0007 C:\ System 0,07 0,01 11,92 4096
 
 
.EXAMPLE
Get-DbaDiskSpace -ComputerName srv0042 -Detailed | Format-Table -AutoSize
 
Get detailed diskspace information
 
Server Name Label SizeInGB FreeInGB PercentFree BlockSize IsSqlDisk FileSystem DriveType
------ ---- ----- -------- -------- ----------- --------- --------- ---------- ---------
srv0042 C:\ System 126,45 114,12 90,25 4096 False NTFS Local Disk
srv0042 E:\ Data1 97,62 96,33 98,67 4096 False ReFS Local Disk
srv0042 F:\ DATA2 29,2 29,2 100 16384 False FAT32 Local Disk
srv0042 \\?\Volume{7a31be94-b842-42f5-af71-e0464a1a9803}\ Recovery 0,44 0,13 30,01 4096 False NTFS Local Disk
srv0042 D:\ 0 0 0 False Compact Disk
 
#>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [Alias('ServerInstance', 'SqlInstance', 'SqlServer')]
        [String[]]$ComputerName,
        [ValidateSet('Bytes', 'KB', 'MB', 'GB', 'TB', 'PB')]
        [String]$Unit = 'GB',
        [Switch]$CheckForSql,
        [PSCredential]$SqlCredential,
        [Switch]$Detailed,
        [Switch]$CheckFragmentation,
        [Switch]$AllDrives
    )
    
    BEGIN
    {
    $FunctionName = (Get-PSCallstack)[0].Command
        Function Get-AllDiskSpace
        {
            $alldisks = @()
            $driveTypeName = @{
                '0' = 'Unknown';
                '1' = 'No Root Directory';
                '2' = 'Removable Disk';
                '3' = 'Local Disk';
                '4' = 'Network Drive';
                '5' = 'Compact Disk';
                '6' = 'RAM Disk'
            }
            
            if ($Detailed -or $AllDrives)
            {
                $driveTypes = 0 .. 6
            }
            else
            {
                $driveTypes = 2, 3
            }
            
            if ($Unit -eq 'Bytes')
            {
                $measure = '1'
            }
            else
            {
                $measure = "1$unit"
            }
            
      try
      {
          $disks = Get-WmiObject -Class Win32_Volume -Namespace 'root\CIMV2' -ComputerName $ipaddr | Where-Object DriveType -in ($driveTypes)
        if ($CheckFragmentation)
        {
          ## $disks = Get-CimInstance -Class Win32_Volume -Namespace 'root\CIMV2' -ComputerName $ipaddr | Where-Object DriveType -in (2,3)
          $disks = $disks | Select-Object SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label, BlockSize, @{ Name = 'FilePercentFragmentation'; Expression = { "$($_.defraganalysis().defraganalysis.FilePercentFragmentation)" } }
        }
        else
        {
          #$query = "Select SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label, BlockSize from Win32_Volume where DriveType = 2 or DriveType = 3"
          ## $disks = Get-CimInstance -ComputerName $ipaddr -Query $query | Sort-Object -Property Name
          $disks = $disks | Select-Object SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label, BlockSize
        }
      }
      catch
      {
        Write-Warning "$FunctionName - Cannot connect to WMI on $server"
        return
      }
            
            if ($CheckForSql -or $Detailed)
            {
                $SqlInstances = @()
                $FailedToGetServiceInformation = $false
                try
                {
                    $sqlservices = Get-Service -ComputerName $ipaddr | Where-Object { $_.DisplayName -like 'SQL Server (*' }
                }
                catch
                {
                    Write-Verbose "$FunctionName - Cannot retrieve service information from $server using Get-Service. Trying WMI"
                    try
                    {
                        $sqlservices = Get-WmiObject Win32_Service -ComputerName $ipaddr | Where-Object { $_.DisplayName -like 'SQL Server (*' }
                    }
                    catch
                    {
                        Write-Warning "$FunctionName - Cannot retrieve service information from $server using Get-Service or WMI."
                        $FailedToGetServiceInformation = $true
                    }
                }
                
        foreach ($service in $sqlservices)
        {
          $instance = $service.DisplayName.Replace('SQL Server (', '')
          $instance = $instance.TrimEnd(')')
                    
          if ($instance -eq 'MSSQLSERVER')
          {
            $SqlInstances += $server
            Write-Verbose "$FunctionName - Instance resolved as $server"
          }
          else
          {
            $SqlInstances += "$server\$instance"
            Write-Verbose "$FunctionName - Instance resolved as $server\$instance"
          }
        }
            }
            
            foreach ($disk in $disks)
            {
                $diskname = $disk.Name
                if ($CheckForSql -or $Detailed)
                {
                    $sqldisk = $false
                    if ($FailedToGetServiceInformation)
                    {
                        $sqldisk = 'unknown'
                    }
                    else
                    {
                        foreach ($SqlInstance in $SqlInstances)
                        {
                            try
                            {
                                Write-Verbose "$FunctionName - Checking disk $diskname on $SqlInstance"
                                $smoserver = Connect-SqlInstance -SqlInstance $SqlInstance -SqlCredential $SqlCredential
                                $sql = "Select count(*) as Count from sys.master_files where physical_name like '$diskname%'"
                                $sqlcount = $smoserver.Databases['master'].ExecuteWithResults($sql).Tables[0].Count
                                if ($sqlcount -gt 0)
                                {
                                    $sqldisk = $true
                                    break
                                }
                            }
                            catch
                            {
                                Write-Warning "$FunctionName - Can't connect to $server ($SqlInstance)"
                                continue
                            }
                        }
                    }
                }
                
                if (!$diskname.StartsWith('\\') -or $Detailed)
                {
                    if ($disk.capacity -eq 0 -or [string]::IsNullOrEmpty($disk.capacity))
                    {
                        $total = 0
                        $free = 0
                        $percentfree = 0
                    }
                    else
                    {
                        $total = [math]::round($disk.Capacity / $measure, 2)
                        $free = [math]::round($disk.Freespace/$measure, 2)
                        $percentfree = [math]::round(($disk.Freespace / $disk.Capacity) * 100, 2)
                    }
                    
                    $diskinfo = [PSCustomObject]@{
                        Server = $server
                        Name = $diskname
                        Label = $disk.Label
                        "SizeIn$unit" = $total
                        "FreeIn$unit" = $free
                        PercentFree = $percentfree
                        BlockSize = $disk.BlockSize
                    }
                    
                    if ($CheckForSql -or $Detailed)
                    {
                        Add-Member -Force -InputObject $diskinfo -MemberType Noteproperty IsSqlDisk -value $sqldisk
                    }
                    
                    if ($Detailed)
                    {
                        Add-Member -Force -InputObject $diskinfo -MemberType Noteproperty FileSystem -value $disk.FileSystem
                        Add-Member -Force -InputObject $diskinfo -MemberType Noteproperty DriveType -value $driveTypeName["$($disk.DriveType)"]
                    }
                    
                    if ($CheckFragmentation)
                    {
                        Add-Member -Force -InputObject $diskinfo -MemberType Noteproperty PercentFragmented -value $disk.FilePercentFragmentation
                    }
                    $alldisks += $diskinfo
                }
            }
            return $alldisks
        }
        
        
        $processed = New-Object System.Collections.ArrayList
    }
    
    PROCESS
    {
        
        foreach ($server in $ComputerName)
        {
            if ($server -match '\\')
            {
                $server = $server.Split('\')[0]
            }
            
            if ($server -notin $processed)
            {
                $null = $processed.Add($server)
                Write-Verbose "$FunctionName - Connecting to $server"
            }
            else
            {
                continue
            }
            
            Write-Verbose "$FunctionName - Resolving computername"
            try
            {
                $ipaddr = ((Test-Connection -ComputerName $server -Count 1 -ErrorAction SilentlyContinue).Ipv4Address).IPAddressToString
            }
            catch
            {
                Write-Warning "$FunctionName - Can't resolve $server address"
                return
            }
            
            $data = Get-AllDiskSpace $server
            
            if ($data.Count -gt 1)
            {
                $data.GetEnumerator() | ForEach-Object { $_ }
            }
            else
            {
                $data
            }
        }
    }
}