Collect basic and dynamic disk details from remote computers

In this post we will discuss some PowerShell scripts to get the details like, used space, free space, total size, etc. of all the drives on remote computers. The list of remote computers could be fed as a text file to the script. Lets discuss each scripts one by one.

Using Win32_LogicalDisk

$CSVFile = "C:\Disk space report.csv"
"Computer Name;Volume Name;Drive Name;Size(GB);Free(GB);Used(GB);Used%(GB)" | out-file -FilePath $CSVFile -append

#domain admin's credentials
$cre = get-credential
$machines = get-content "C:\serverset.txt"
foreach ($machine in $machines) 
{
$disk=Get-WmiObject Win32_LogicalDisk -filter "DriveType=3" -computer $machine -Credential $cre| Select SystemName,DeviceID,VolumeName,Size,freespace
foreach ($d in $disk)
{
#to set language pack for current thread as en-US
[threading.thread]::CurrentThread.CurrentCulture = 'en-US'
$DeviceID=($d.DeviceID).Replace(":", "")

$VolumeName=$d.VolumeName
#Round(($d.size/1gb), 2)) will round the value to two decimal places
$size=('{0:N2}' -f [Math]::Round(($d.size/1gb), 2))

$freespace=('{0:N2}' -f [Math]::Round(($d.freespace/1gb), 2))
#we have to define the values stored in $d.size and $d.freespace as integer
$usespace=('{0:N2}' -f ([Math]::Round($d.size/1GB,2 -as [int])) - ([Math]::Round($d.freespace/1GB,2 -as [int])))

$usespacepc=('{0:N2}' -f [Math]::Round((($usespace/$size)*100), 2))

$exporttocsv =$machine+";"+$DeviceID+";"+$d.VolumeName+";"+$size+";"+$freespace+";"+$usespace+";"+$usespacepc
$exporttocsv | Out-File -Append $CSVFile
  }
}

Now where on one hand we can get free space quite easily, we need to perform calculations to get used space.

Using Get-PSDrive

Now we will try to do the same thing, but this time we won’t do any calculation. As a result this can lead to higher speed and accuracy. We’ll try to make use of get-psdrive command to get used space and free space directly. However we still need to use get-wmiobject command to get total size of drive and volumename.

$CSVFile = "C:\Disk space report.csv"
"Computer Name;Drive Name;Volume Name;Size(GB);Free(GB);Used(GB);Used%(GB)" | out-file -FilePath $CSVFile -append
WriteHost"ComputerName;VolumeName;DriveName;Size(GB);Free(GB);Used(GB);Used(GB)"

$cre = get-credential
$machines = get-content "C:\serverset.txt"
foreach ($machine in $machines) {
#=================================================== 
#---To get Volume names and Total size, we will-----
#---use Win32_LogicalDisk, and to get Used Space----
#---and free space, we will use Get-PSDrive---------
#===================================================
$disk=Get-WmiObject Win32_LogicalDisk -filter "DriveType=3" -computer $machine -Credential $cre | Select Size,DeviceID,VolumeName

$disk1=Invoke-Command -ComputerName $machine {Get-PSDrive} -Credential $cre | Select-Object PSComputerName,Name,Used,Free,Provider|
where-object Provider -match "FileSystem"

foreach ($d in $disk)
{
[threading.thread]::CurrentThread.CurrentCulture = 'en-US'
$deviceid=($d.DeviceID).Replace(":", "")

foreach ($d1 in $disk1)
{

if ($d1.Name -match $deviceid){
$VolumeName = $d.VolumeName
[threading.thread]::CurrentThread.CurrentCulture = 'en-US'
$size=('{0:N2}' -f [Math]::Round(($d.size/1gb), 2))

$freespace=('{0:N2}' -f [Math]::Round(($d1.free/1gb), 2))

$usespace=('{0:N2}' -f [Math]::Round(($d1.used/1gb), 2))

$usespacepc=('{0:N2}' -f [Math]::Round((($usespace/$size)*100), 2))

$exporttocsv =$d1.PSComputerName+";"+$deviceid+";"+$VolumeName+";"+$size+";"+$freespace+";"+$usespace+";"+$usespacepc
$exporttocsv
$exporttocsv| Out-File -Append $CSVFile
Break }
    }
  }
}

Using Win32_Volume

Now we will see how we can make use of Win32_Volume to get the desired output.

$CSVFile = "C:\Disk space report.csv"
"Computer name,Drive name,Size(GB),Free(GB),Used(GB),% Used" | out-file -FilePath $CSVFile -append
$machines = get-content "C:\Users\umurarka\Desktop\servers.txt"
$cre = Get-Credential
foreach ($machine in $machines) 

{
$disk = Get-WmiObject - -ComputerName $machine -Class Win32_Volume -ErrorAction SilentlyContinue
foreach ($d in $disk)
{

$size=('{0:N2}' -f [Math]::Round(($d.Capacity/1gb), 2))

$freespace=('{0:N2}' -f [Math]::Round(($d.freespace/1gb), 2))

$usespace=('{0:N2}' -f ([Math]::Round($d.Capacity/1GB,2 )) - ([Math]::Round($d.freespace/1GB,2)))

$usespacepc=('{0:N2}' -f [Math]::Round((($usespace/$size)*100), 2))

$exporttocsv ="acrdefrps01"+" "+$d.DriveLetter+" "+$size+" "+$freespace+" "+$usespace+" "+$usespacepc
$exporttocsv| Out-File -Append $CSVFile
  }
}

A Fast Way of getting disk space report through UNC paths

Using above methods sometimes come with problems like access denied. To overcome these challenges, we can use UNC paths to solve our problem. Also it has many times higher speed than any of the above method.

#===================================================
#---function for geeting disk space from UNC--------
#===================================================
[threading.thread]::CurrentThread.CurrentCulture = 'en-US'
function getDiskSpaceInfoUNC($p_UNCpath, $p_unit = 1GB, $p_format = '{0:N1}')
{
    # unit, one of --> 1kb, 1mb, 1gb, 1tb, 1pb
    $l_typeDefinition = @' 
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
        [return: MarshalAs(UnmanagedType.Bool)] 
        public static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, 
            out ulong lpFreeBytesAvailable, 
            out ulong lpTotalNumberOfBytes, 
            out ulong lpTotalNumberOfFreeBytes); 
'@
    $l_type = Add-Type -MemberDefinition $l_typeDefinition -Name Win32Utils -Namespace GetDiskFreeSpaceEx -PassThru

    $freeBytesAvailable     = New-Object System.UInt64 # differs from totalNumberOfFreeBytes when per-user disk quotas are in place
    $totalNumberOfBytes     = New-Object System.UInt64
    $totalNumberOfFreeBytes = New-Object System.UInt64

    $l_result = $l_type::GetDiskFreeSpaceEx($p_UNCpath,([ref]$freeBytesAvailable),([ref]$totalNumberOfBytes),([ref]$totalNumberOfFreeBytes)) 

    $totalBytes     = if($l_result) { $totalNumberOfBytes    /$p_unit } else { '' }
    $totalFreeBytes = if($l_result) { $totalNumberOfFreeBytes/$p_unit } else { '' }

    New-Object PSObject -Property @{
        Path      = $p_UNCpath
        Total     =  [math]::round($totalBytes , 2 , 1)
        Used      =  [math]::round($totalBytes -  $totalFreeBytes , 2 , 1)
        Used_percent      = [math]::round((($totalBytes -  $totalFreeBytes)/$totalBytes)*100 , 2 , 1)
        Free      = [math]::round($totalFreeBytes , 2 , 1)
    } 
}

#===================================================
$CSVFile = "C:\Disk space report.csv"
$machines = get-content "C:\servers.txt"
foreach ($machine in $machines) 
 
{
#===================================================
#---With the help of Win32_Share, we will filter----
#---and refine our search results to include only---
#---the drive letters, e.g. C$----------------------
#===================================================
$ds = get-WmiObject -class Win32_Share -computer $machine -ErrorAction SilentlyContinue|
? {($_.description -NotLike "*printer*") -and ($_.name -notlike "SYSVOL") -and ($_.name -like "?$")} |
 select Name

 foreach ($d in $ds) {
 $p = join-path \\$machine\ $d.name

[threading.thread]::CurrentThread.CurrentCulture = 'en-US'
getDiskSpaceInfoUNC $p |ft -AutoSize
getDiskSpaceInfoUNC $p |ft -AutoSize -HideTableHeaders| Out-File -Append $CSVFile

  }
}

What to do if computer has some dynamic disks also?

Above powershell commands will only list out basic disks in the system. But if the system also has dynamic disks, then we are left with only a small set of options, described below. This is part of Microsoft strategy of phasing out dynamic disks as can be seen in the link Tip of the Day: Dynamic Disks and Windows PowerShell

Win32_DiskPartition

Get-WmiObject Win32_DiskPartition -filter "Type='Logical Disk Manager'" |select *|ft -autosize

A more detailed query.

[threading.thread]::CurrentThread.CurrentCulture = 'en-US'
Get-WMIObject Win32_DiskPartition | `
Sort-Object DiskIndex, Index | `
Select-Object -Property `
@{Expression = {$_.DiskIndex};Label="Disk"},`
@{Expression = {$_.Index};Label="Partition"},`
@{Expression = {$_.Caption};Label="Caption"},`
@{Expression = {$_.Type};Label="Type"},`
@{Expression = {Get-DriveLetter($_.__PATH)};Label="Drive"},`
@{Expression = {$_.BootPartition};Label="BootPartition"},`
@{Expression = {"{0:N3}" -f ($_.Size/1Gb)};Label="Size_GB"},`
@{Expression = {"{0:N0}" -f ($_.BlockSize)};Label="BlockSize"},`
@{Expression = {"{0:N0}" -f ($_.StartingOffset/1Kb)};Label="Offset_KB"},`
@{Expression = {"{0:N0}" -f ($_.StartingOffset/$_.BlockSize)}; Label="OffsetSectors"},`
@{Expression = {IF (($_.StartingOffset % 64KB) -EQ 0) {" Yes"} ELSE {" No"}};Label="64KB"}|ft -AutoSize|out-file "C:\output.txt"

To distinguish between basic and dynamic disks in the output, note the entry under heading “Type”

Basic disk is the one with entry “Installable File System”

Dynamic disk is the one with entry “Logical Disk manager”

Diskpart

Diskpart command: List Disk can give the output of all disks attached to the system.

But to output the same to a text file, we might need a workaround.

Step1> Create a text file (e.g. script.txt) in notepad and save it in C:\ drive, with the following command written inside.

List Disk

Step2> In a command prompt, execute below command to get the output.

diskpart /s C:\script.txt > C:\output.txt
Advertisements