Getting the Free Disk Space of Remote Computers
November 26th, 2014 by Charlie Russel and tagged Calculated Column, Disk Free Space, Format-Table, Formatting output, WMI
(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 »
July 2nd, 2015 at 5:26 am
Command are not working
will only show >>
July 3rd, 2015 at 10:13 am
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.
September 8th, 2016 at 3:34 pm
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
September 9th, 2016 at 8:52 am
That would be because your cut and paste missed the final ` (back-tick) character in the line above.
September 9th, 2016 at 8:56 am
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.
September 8th, 2016 at 3:56 pm
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
September 9th, 2016 at 8:58 am
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.
April 11th, 2018 at 10:08 pm
What Parameter should I add to get a list of computer added with this script.
April 12th, 2018 at 9:31 am
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.
November 6th, 2016 at 1:21 am
This is fantastic. Thank you! Exactly what I was trying to do myself.
November 10th, 2016 at 12:26 am
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
November 13th, 2016 at 3:22 pm
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.
March 30th, 2017 at 12:46 pm
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!
March 30th, 2017 at 3:06 pm
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).
April 11th, 2017 at 11:00 am
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?
April 11th, 2017 at 11:21 am
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. )
April 11th, 2017 at 12:43 pm
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
April 11th, 2017 at 12:49 pm
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.
April 11th, 2017 at 11:55 am
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?
April 11th, 2017 at 12:04 pm
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.
April 18th, 2017 at 10:15 am
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
April 18th, 2017 at 10:24 am
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.
December 24th, 2017 at 4:23 am
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
June 9th, 2017 at 1:29 am
#>
[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
}
October 16th, 2017 at 8:17 am
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
}
}
}
}
October 16th, 2017 at 8:22 am
And it yields a PSCustomObject, always useful. Thanks.
June 6th, 2018 at 3:05 pm
Worked like a charm and saved me a hell of a lot of time!! Thanks!
June 8th, 2018 at 3:41 pm
Thanks for your comment, Omar, I appreciate it. Do check out the updated versions of this:
https://blogs.msmvps.com/russel/2017/04/18/getting-the-free-disk-space-of-remote-computers-revisited/
https://blogs.msmvps.com/russel/2017/05/04/guest-post-get-myfreespace-revisited/
Both provide an updated look at finding the free disk space, and let’s face it — I’ve learned a few things since this original post. :)
August 8th, 2018 at 3:11 am
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…
August 8th, 2018 at 9:13 am
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.
September 21st, 2018 at 3:47 am
You’re welcome :-)
And “Good Enough” is a good starting point ;-)
August 21st, 2018 at 6:00 am
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?
August 21st, 2018 at 9:50 am
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.