Getting the Free Disk Space of Remote Computers

November 26th, 2014 by and tagged , , , ,

(For an updated version of this post and script, see Getting the Free Disk Space of Remote Computers Revisited.)

This started out as a simple script to try to get the free space on one of my servers, but I quickly discovered that using WMI’s Win32_LogicalDisk could only give me part of the solution. The catch is that Win32_LogicalDisk doesn’t return the information about volumes that aren’t assigned drive letters. Which is a problem if what you really need to know is how close to out of space your backup disk is! Because Windows Server Backup takes total control of the backup target disk, and doesn’t mount it as a drive letter, you need to use a different WMI Class to get the information you need. After asking some friends (PowerShell MVPs ROCK!), I was pointed to Win32_Volume, which returns all sorts of information about disk volumes whether they are assigned drive letters or not.

The next issue was how to get the actual information I wanted, and then to format it so that it actually made some sense. For example:

(Get-WmiObject –ComputerName Server1 –Class Win32_Volume).FreeSpace
21654667264
103541030912
75879378944
142417367040
5500928
565375053824
PSH>

 

This doesn’t really cut it. OK, let’s try at least getting it into a table:

Get-WmiObject –ComputerName Server1 –Class Win32_Volume | ft –auto DriveLetter,Label,FreeSpace

DriveLetter Label                               FreeSpace 
----------- -----                               --------- 
C:                                            21655351296 
D:          DATA                             103541030912 
E:          EXCHANGE                          75879378944 
F:          FILES                            142417367040 
Y:          New Volume                            5500928 
            Server1 2014_10_15 10:57 DISK_03 565375053824

 

Well, that’s a bit more useful, but frankly, that number for the backup volume seems big, but is it 500 GB, or 50 GB? At first glance, I have no idea. And if it’s 50 GB, I’m in trouble, but if it’s 500 GB, we’re fine. So, we need to do a bit of manipulation to the output from Format-Table, and the tool for this is to create an Expression that allows you to calculate and format a result in a way that makes more sense. For this, we use an expression as the property to display. So, for example, to display that “565375053824” as Gigabytes, we use:

Get-WmiObject –ComputerName Server1 –Class Win32_Volume `
       | ft –auto DriveLetter,`
                  Label,`
                  @{Label=”Free(GB)”;Expression={'{0:N0}’ –F ($_.FreeSpace/1GB)}}
DriveLetter Label                            Free(GB) 
----------- -----                            -------- 
C:                                           20 
D:          DATA                             96 
E:          EXCHANGE                         71 
F:          FILES                            133 
Y:          New Volume                       0 
            Server1 2014_10_15 10:57 DISK_03 527

Now we’re getting somewhere. But what did we do? We use the @{} to tell Format-Table that we were going to use an expression to define a column of data. The Label=”Free(GB)” creates a new column header, and the Expression={“{0:N0}” –F  means we’re going to have a numeric value (including thousands separators) with no decimal values. The calculated value for the column is ($_.FreeSpace/1GB).

So we now have a useful listing of free space on the remote server. Of course, it might be even more useful to know the percentage free. No problem, for that we use the formatting expression “{0:P0}” to express the column as a percentage, and use the calculation of ($_.FreeSpace/$_.Capacity), letting PowerShell do the work of converting that to a percentage. So:

Get-WmiObject –ComputerName Server1 –Class Win32_Volume `
       | ft –auto DriveLetter,`
                  Label,`
                  @{Label=”Free(GB)”;Expression={“{0:N0}” –F ($_.FreeSpace/1GB)}},`
                  @{Label=”%Free”;Expression={“{0:P0}” –F ($_.FreeSpace/$_.Capacity)}}
DriveLetter Label                         Free(GB)  %Free
----------- -----                         --------  ----- 
C:                                            20      17 %
D:          DATA                              96      48 % 
E:          EXCHANGE                          71      71 % 
F:          FILES                             133     18 % 
Y:          New Volume                        0       58 % 
            Server1 12014_10_15 10:57 DISK_03 527     51 %
<br>

 

Now we almost have it. Next, it would probably be useful to get the total capacity of the disk while we’re at it, and since I have more than one server, we should probably plan on passing this whole thing an array of computer names. So, the final script, at least for this first pass:

# ********************************************* 
# ScriptName: Get-myFreeSpace.ps1 
# 
# Description: Script to get the free disk space
#            : on a remote computer and display it usefully
# 
# ModHist: 26/11/2014 - Initial, Charlie
#        : 18/04/2017 - Change to Write-Output. 
# 
# 
# *********************************************
[CmdletBinding()]
Param ([Parameter(Mandatory=$False,Position=0)]
         [String[]]$ComputerName = "Server1")
Write-Output ""
ForEach ( $Name in $ComputerName ) {
   Write-Output "Disk Utilization for server $Name is: "
   Get-WmiObject  -ComputerName $Name -Class Win32_Volume `
      | Format-Table  -auto `
         @{Label="Drive";`
            Expression={$_.DriveLetter};`
            Align="Right"},`
         @{Label="Free(GB)";`
            Expression={"{0:N0}" -f ($_.FreeSpace/1GB)};`
            Align="Right"},`
         @{Label="% Free";`
            Expression={"{0:P0}" -f ($_.FreeSpace / $_.Capacity)};`
            Align="Right"},`
         @{Label="Size(GB)";`
            Expression={"{0:N0}" -f ($_.Capacity / 1GB)};`
            Align="Right"},`
         @{Label="Volume Label";`
            Expression={$_.Label};`
            Width=25} 
}

You’ll see I tweaked the formatting to right align the calculated expressions, and gave my volume label column some extra space to improve readability. The result is:

Get-myFreeSpace.ps1 –ComputerName “Server1”,”Server2” 
 
Disk Utilization for server Server1 is:

 Drive Free(GB) % Free Size(GB) Volume Label 
----- -------- ------ -------- ------------ 
   C:       20   17 %      120 
   D:       96   48 %      200 DATA 
   E:       71   71 %      100 EXCHANGE 
   F:      133   18 %      750 FILES 
   Y:        0   58 %        0 New Volume 
           527   51 %    1,024 Server1 2014_10_15 10:57 DISK_03 
 
Disk Utilization for server Server2 is:

Drive Free(GB) % Free Size(GB) Volume Label 
----- -------- ------ -------- ------------ 
             0   25 %        0 
   D:    1,697   53 %    3,214 Data 
   C:      484   95 %      512

 

ETA:  Changed to use Write-Output instead of Write-Host. No need for the features of Write-Host, and it was causing issues for those who wanted to redirect the output to a file. This is more flexible.

ETA:  Changed to clean up the problem formatting from the switch in syntax highlighters. Sigh.

 

 

Posted in Network Administration, PowerShell, Windows Server, WMI | 33 Comments »



33 Responses to “Getting the Free Disk Space of Remote Computers”

  1.   frank Says:

    Command are not working
    will only show >>

    •   Charlie Russel Says:

      If you’re seeing “>>” then you’ve missed something in your command. Probably the trailing backtic character which is a line continuation or escape character. If you have a space after the back-tick, it won’t work. The back-tick must be the last character in the line. (It “escapes” the end of line character, allowing the command to continue on the next line as if it were all one continuous command line. )

      Or, it could be an errant mismatch of single and double quotes. An earlier version of this post had unpaired quotes, which has been corrected. It’s easy to get that wrong if you’re typing it in by hand.

  2.   Luis Says:

    Hi Charlie,

    I copy/pasted the code and added my server names. But I am getting this error:

    At line:19 char:7
    + | Format-Table -auto `
    + ~
    An empty pipe element is not allowed.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : EmptyPipeElement

    Regards

    •   Charlie Russel Says:

      That would be because your cut and paste missed the final ` (back-tick) character in the line above.

    •   Charlie Russel Says:

      Same code, slightly different formatting. What caused your problems was that you missed the
      Get-WmiObject -ComputerName $Name -Class Win32_Volume `
      the trailing back tick in that line, or, equally disastrous, you have a space after the back tick. The back-tick MUST be the final character in the line of you’re escaping the end of the line.

  3.   Luis Says:

    I corrected the code and now its working:

    [CmdletBinding()]
    Param ([Parameter(Mandatory=$False,Position=0)]
    [String[]]$ComputerName = “lt-win1064”)
    Write-Host “”
    ForEach ( $Name in $ComputerName ) {
    Write-Host “Disk Utilization for Workstation $Name is: ”
    Get-WmiObject -ComputerName $Name -Class Win32_Volume | FT -auto @{Label=”Drive”;`
    Expression={$_.DriveLetter};`
    Align=”Right”},`
    @{Label=”Free(GB)”;`
    Expression={“{0:N0}” -f ($_.FreeSpace/1GB)};`
    Align=”Right”},`
    @{Label=”% Free”;`
    Expression={“{0:P0}” -f ($_.FreeSpace / $_.Capacity)};`
    Align=”Right”},`
    @{Label=”Size(GB)”;`
    Expression={“{0:N0}” -f ($_.Capacity / 1GB)};`
    Align=”Right”},`
    @{Label=”Volume Label”;`
    Expression={$_.Label};`
    Width=25}
    }

    Regards

    •   Charlie Russel Says:

      As already commented. Your cut and paste either added an extra space after a back tick, or left off a trailing back tick.

      I would note that WordPress has been known to occaisonally add extra blank space where I didn’t want it. so always be careful to verify that the line ends where it should. I try my best, but I’m at the mercy of tools outside of my direct control.

    •   Krishna Says:

      What Parameter should I add to get a list of computer added with this script.

      •   Charlie Russel Says:

        As this script is written, what you see is what you get. An output that is formatted text showing you the disk utilization on one or more computers, with each computer identified as part of the output. Useful for what I needed, but not useful for anything downstream of it. I improved this script substantially when I revisited it here, and if you look at Robert Carlson’s comment to that post, you’ll see a way to extend what I was doing by turning it into a function that creates a custom object. I took and wrote that up as a full blog post. You could then extend that custom object by including the ComputerName property in the output object to get what I think you want.

  4.   Kristian Husby Says:

    This is fantastic. Thank you! Exactly what I was trying to do myself.

  5.   Mani Shankar Says:

    hi we use this Scripts for Multiple servers , I have tries but getting below error .

    PS C:\> .\Get-myFreeSpace.ps1
    Missing expression after ‘,’.
    At C:\Get-myFreeSpace.ps1:5 char:51
    + [String[]]$ComputerName = “BLRMCSTVMH08”, <<<< "BLRMCSKMS")
    + CategoryInfo : ParserError: (,:OperatorToken) [], ParseException
    + FullyQualifiedErrorId : MissingExpressionAfterToken

    •   Charlie Russel Says:

      You appear to have changed the default value to a list of servers with a simple “BLRMCSTVMH08”,”“BLRMCSTVMH08″. Unfortunately, that won’t work. What you need to do is explicitly tell PowerShell that you’re setting a value for the ComputerName parameter that is an array, so use:

      [CmdletBinding()]
      Param ([Parameter(Mandatory=$False,Position=0)]
      [String[]]$ComputerName = @(“BLRMCSTVMH08”, "BLRMCSKMS"))

      That should do what you want.

  6.   mjstonesr Says:

    How to export the entire output to a file? I’ve tried output-file and export-cvs and get disk data in the file but still get the server names only on the console by virtue of the “Write-Host “Disk Utilization for server $Name is: “”. I can’t figure out how to get that part to go to file instead of console. I’ve tried modifying the ‘write-host’, but no luck. I’m struggling. Thanks!

    •   Charlie Russel Says:

      Unfortunately, Write-Host will always write to the console. The solution is to switch to either Out-File or Write-Output. Write-Output allows for redirection, and Out-File always writes to a file. Write-Output is the more flexible of the two solutions, allowing you to redirect as you wish. Frankly, I should have used it for this script, since I had no intention of using some of the advanced console-specific features of Write-Host (such as colouration).

  7.   Paul Arbogast Says:

    This is great, however, I’m unable to get it to run the script. I get the following error
    ‘D:\scripts\SCOM_Scripts\DCO_ToolKit_Scripts\checkdiskspace.ps1’ is not
    recognized as the name of a cmdlet, function, script file, or operable
    program.

    Any ideas why?

    •   Charlie Russel Says:

      You can always force execution with a leading & (ampersand). It could be that .PS1 files are not set to execute. Or something else on your system.

      One trick I use is to create a $home\psbin directory and ensure that it is on my path. Also, it’s a good idea to get in the habit of using PowerShell conventions and syntax. Thus I’d likely rename it on your system to something like: Get-myDiskSpace.ps1 or even Check-myDiskSpace.ps1. (Check is not a standard verb, and I avoid non-standard verbs when I can these days. Though goodness knows I’ve got a few scripts left over from my earliest PowerShell days when I was less disciplined. )

      •   Paul Arbogast Says:

        how does that work exactly, I tried doing D:\scripts\SCOM_Scripts\DCO_ToolKit_Scripts\&checkdiskspace.ps1 in the over ride but just told me the ampersand & character is not allowed and its recovered for future use

        •   Charlie Russel Says:

          No. The Ampersand goes before the entire path. So, in your case:

          & D:\scripts\SCOM_Scripts\DCO_ToolKit_Scripts\&checkdiskspace.ps1

          Assuming that your checkdiskspace.ps1 script is in that directory.

    •   Paul Arbogast Says:

      ok I think I misunderstood what this script was supposed to do, it does work, however the script that I need to run seems require being located on the remote server I’m checking, I copied my script there as a test and it worked as expected. I’m not sure that I will be given permission to copy powershell scripts to all the servers in the environment, and that would require too much upkeep anyway to make sure new servers got copies. is there anyway this can be ran from the management server to pull the info remotely?

      •   Charlie Russel Says:

        No need to copy the script to a remote server. The whole point of this is to run remotely. It accepts an array of computer names with the -ComputerName parameter.

  8.   Britt Says:

    How can you capture the entire output of this script into a text file? Out of the several things I have tried – I was able to grab some of it but it did not capture “Disk Utilization for server $name is: ” string.
    I have added >> C:\MyLog.txt after :
    Write-Host “Disk Utilization for server $Name is: ”
    and also after:

    @{Label=”Volume Label”;`
    Expression={$_.Label};`
    Width=25} >> C:\CADFREEDISK.TXT

    •   Charlie Russel Says:

      Answered earlier in this list of comments, but once again — change the ‘Write-Host’ lines to ‘Write-Output’ and then you can redirect. Alternately, if you ONLY want output to a file, change to use ‘Out-File’. OR, even better, add the output type as a parameter and branch based on it. You could, for example, add a -FilePath parameter and then when present, output to that file. This is PowerShell — USE IT. :)

      Seriously, if I were writing this script today, I’d use Write-Output. When I wrote it, I had no need or desire to do anything except send it to the screen, so I didn’t bother. But it’s a better practice to use Write-Output except in those cases where you need something like colourization, which would require the features of Write-Host.

      •   Balaji Says:

        Hello Charlie,

        I want to output the result to a CSV file. I used this “out-file C:\Drives.csv”. But the output is coming in the single column instead of separate for each value. Can you please let me know how to get the desired output?

        Thanks,
        Balaji

  9.   Safet Grahic Says:

    #>
    [CmdletBinding()]
    Param(
    [Parameter(Mandatory=$False,Position=0,`
    ValueFromPipeline=$True,`
    ValueFromPipelineByPropertyName=$True,`
    ValueFromRemainingArguments=$True)]
    [alias(“Name”,”Computer”)]
    [string[]]
    [string[]]$ComputerName = (Get-Content “c:\temp\pc.txt”)
    )

    Begin {
    if ($Input) {
    $ComputerName = @($Input)
    }
    Write-Output “”
    # Save ErrorActionPreference so we can reset it when we’re done
    $eap = $ErrorActionPreference
    }

    Process {
    $ErrorActionPreference = ‘SilentlyContinue’
    ForEach ( $Computer in $ComputerName ) {
    #Write-Output “Disk Utilization for Computer $Computer is: ”
    Get-WmiObject -ComputerName $Computer -Class Win32_Volume -Filter ‘DriveLetter = “C:”‘`
    | Format-Table -auto `
    @{Label=”Computer”;`
    Expression={$Computer};`
    Align=”Right”},
    @{Label=”Drive”;`
    Expression={$_.DriveLetter};`
    Align=”Right”},`
    @{Label=”Free(GB)”;`
    Expression={“{0:N0}” -f ($_.FreeSpace/1GB)};`
    Align=”Right”},`
    @{Label=”% Free”;`
    Expression={“{0:P0}” -f ($_.FreeSpace / $_.Capacity)};`
    Align=”Right”},`
    @{Label=”Size(GB)”;`
    Expression={“{0:N0}” -f ($_.Capacity / 1GB)};`
    Align=”Right”} >> C:\temp\freedisk.TXT
    #@{Label=”Volume Label”;`
    #Expression={$_.Label};`
    #Width=25}
    } #EndForEach
    } #EndProcessBlock

    End {
    # Reset ErrorActionPreference to original value
    $ErrorActionPreference = $eap
    }

  10.   MisterDD Says:

    This is my sauce :)

    Function Get-LocalDiskInformation {

    [cmdletbinding(DefaultParameterSetName = ‘ComputerName’)]
    param(
    [parameter( Mandatory = $false,
    ParameterSetName = ‘ComputerName’,
    Position = 0,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true )]
    [String[]]$ComputerName = $env:COMPUTERNAME,

    [parameter( Mandatory = $false,
    Position = 0,
    ParameterSetName = ‘FromClipboard’,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true)]
    [switch]$FromClipboard

    )

    if ( $FromClipboard ) {
    $Computers = $( Get-Clipboard -Format Text )
    }
    elseif ( $ComputerName ) {
    $Computers = $ComputerName
    }

    $Computers[0..$Computers.Count] | ForEach-Object {
    $Computer = $_
    if ( Test-Connection $Computer -Count 1 -Quiet ) {
    try {
    $DiskInfo = Get-WmiObject -ComputerName $Computer –Class Win32_Volume -Filter “DriveLetter=’C:'”
    }
    catch {
    if ($error[0].Exception.Message -like ‘*The RPC server is unavailable*’) {
    break
    }
    else { $error[0].Exception.Message }
    }
    $Status = ‘Online’
    }
    else {
    $Status = ‘Not Online’
    }

    if ( $Status -Like ‘Online’ ) {

    [PSCustomObject]@{
    ComputerName = $Computer
    Drive = $DiskInfo.DriveLetter
    ‘Size(GB)’ = “{0:N2}” -f ( $DiskInfo.Capacity / 1GB )
    ‘Free(GB)’ = “{0:N2}” -f ($DiskInfo.FreeSpace / 1GB)
    ‘% Free’ = “{0:P2}” -f ($DiskInfo.FreeSpace / $DiskInfo.Capacity )
    Status = $Status

    }
    }
    elseif ( $Status -Like ‘Not Online’ ) {

    [PSCustomObject]@{
    ComputerName = $Computer
    Drive = $null
    ‘Size(GB)’ = $null
    ‘Free(GB)’ = $null
    ‘% Free’ = $null
    Status = $Status

    }
    }
    }
    }

  11.   Omar Says:

    Worked like a charm and saved me a hell of a lot of time!! Thanks!

  12.   Lars Panzerbjørn Says:

    Probably not what you intended as such, but this is probably the simplest & clearest description of @{}, what it does and is for in this kind of output, that I am directing people here to learn how to use it.
    Thanks, it is way better than how I explain it…

    •   Charlie Russel Says:

      Thank you. I do spend some effort to show not just the result, but how to get there, and it’s nice to hear that it’s noticed and appreciated!

      Do also look at the two updated versions of this script. Not for the @{} formatting, which I didn’t cover in depth because I’d already covered it here, but for coverage of some other issues with this original script. Which definitely showed the “good enough for me” approach I sometimes take.

  13.   Shivem Says:

    Hi will this also work for servers that do not have PowerShell installed? Also, would the Windows machine I run this on have to be on the same network as my servers?

    •   Charlie Russel Says:

      PowerShell is installed on all Windows Servers that are currently in support, so the question doesn’t apply for them. For non-Windows Servers, the answer is a bit more complicated. This script script could be modified to use CIM instead of WMI, and then you should be able to use it, assuming you’ve set permissions correctly and configure your firewalls correctly.

      And yes, they need to be on the same network. I don’t think WMI calls will cross router boundaries.