Getting the Free Disk Space of Remote Computers Revisited
April 18th, 2017 by Charlie Russel and tagged Calculated Column, Comment-Based Help, Disk Free Space, ErrorAction, Format-Table, Formatting output, Pipeline, PowerShell, Process{}, WMI
Several years ago, I wrote a fairly simplistic script to get the free disk space of remote computers. It wasn’t all that sophisticated, but it got the job that I needed done, so I shared it here on my blog, since I thought others might find it useful. Which, based on the number of hits here, and the comments, they did. However, based on some of those comments, it had a problem for some users.
The problem was that I used Write-Host in it. That was fine for me, because I only used it to write to my screen. But it’s a bad practice to be using Write-Host unless you really need to manipulate screen colours. The reason it’s a bad practice is that it prevents any sort of redirection! This meant that those users who wanted to capture the result of the script in a file were horked, because Write-Host will ALWAYS write to ( … wait for it… )
The Host. You can’t redirect it. The fix, of course, is easy — use Write-Object instead, which is what I should have done in the first place.
While I was in the process of making that change, I thought it would be nice to add in a basic Get-Help page for it, which was trivial. But then it occurred to me that I really should let it handle pipeline input, allowing me to use other PowerShell commands to select the group of machines I wanted the free disk space on, and then pipe that result directly to Get-myFreeSpace.
Seemed like a good idea, but it turned out I had to almost completely rewrite the script to use the Begin{}/Process{}/End{} syntax. Accepting pipeline input is not as simple as just saying you do in the Parameter statement, you need to actually process that input. The result is the new, improved version of Get-myFreeSpace.ps1 shown below. (If you care about how I got to this script in the first place, do check out the original post, here. There’s some useful information there about the whole process. )
<# .Synopsis Gets the disk utilization of one or more computers .Description Get-myFreeSpace queries an array of remote computers and returns a nicely formatted display of their current disk utilization and free space. The output can be redirected to a file or other output option using standard redirection. .Example Get-myFreeSpace Gets the disk utilization and free space of all drives on the local host. .Example Get-myFreeSpace -ComputerName Server1,Server2 Gets the disk utilization and free space of all drives on the Server1 and Server2 .Example (Get-VM -Name "*server*" | Where State -eq 'Running' ).Name | Get-myFreeSpace PS C:\>(Get-VM -Name "*server*" | Where-Object {$_.State -eq 'Running').Name | Get-myFreeSpace Gets a list of running VMs with Server in their name, and passes it to Get-myFreeSpace to process for their current disk utilization. The first version of this example uses PowerShell v5 syntax, while the second version uses the older syntax that works on earlier versions. .Parameter ComputerName An array of computer names from which you want the disk utilization .Inputs [string[]] .Notes Author: Charlie Russel Copyright: 2017 by Charlie Russel : Permission to use is granted but attribution is appreciated Initial: 26 Nov, 2014 (cpr) ModHist: 29 Sep, 2016 -- Changed default to array of localhost (cpr) : 18 Apr, 2017 -- Changed to use Write-Output,accept Pipeline,added man page, (cpr) : #> [CmdletBinding()] Param( [Parameter(Mandatory=$False,Position=0,` ValueFromPipeline=$True,` ValueFromPipelineByPropertyName=$True,` ValueFromRemainingArguments=$True)] [alias("Name","Computer")] [string[]] $ComputerName = @("localhost") ) 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 ` | 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} } #EndForEach } #EndProcessBlock End { # Reset ErrorActionPreference to original value $ErrorActionPreference = $eap }
And there you have it. A new and improved version of one of the most popular scripts I’ve ever posted here. You can use it to get the disk utilization on your current machine, or any list of remote computers to which you have the rights to run WMI against.
I hope you find this script useful, and I’d love to hear comments, suggestions for improvements, or bug reports as appropriate. As always, if you use this script as the basis for your own work, please respect my copyright and provide appropriate attribution.
Posted in Annoyances, Network Administration, PowerShell, Windows Server, WMI | 4 Comments »
April 20th, 2017 at 5:32 pm
Hello Charlie,
In its current form, the output does not readily lend itself to being piped to other cmdlets nor to exporting the results. it also has interesting results when piped to Get-Member. If you get a chance, please take a look at the following revised code and let me know what you think:
function Get-MyFreeSpace
{
(Get-VM -Name “*server*” | Where-Object {$_.State -eq ‘Running’).Name | Get-myFreeSpace
Gets a list of running VMs with Server in their name, and passes it to Get-myFreeSpace to process for
their current disk utilization. The first version of this example uses PowerShell v5 syntax, while
the second version uses the older syntax that works on earlier versions.
.Parameter ComputerName
An array of computer names from which you want the disk utilization
.Inputs
[string[]]
.Notes
Original Author: Charlie Russel
Secondary Author: Robert Carlson
Copyright: 2017 by Charlie Russel
: Permission to use is granted but attribution is appreciated
Initial: 26 Nov, 2014 (cpr)
ModHist: 29 Sep, 2016 — Changed default to array of localhost (cpr)
: 18 Apr, 2017 — Changed to use Write-Output,accept Pipeline,added man page, (cpr)
: 20 Apr, 2017 — Changed output to pscustomobject rather than string, etc.
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False,Position=0,`
ValueFromPipeline=$True,`
ValueFromPipelineByPropertyName=$True,`
ValueFromRemainingArguments=$True)]
[alias(“Name”,”Computer”)]
[string[]]
$ComputerName = @(“localhost”)
)
Begin
{
if ($Input)
{
$ComputerName = @($Input)
}
}
Process
{
ForEach ( $Computer in $ComputerName )
{
$volumes = Get-WmiObject -ComputerName $Computer -Class Win32_Volume -ErrorAction SilentlyContinue
foreach ($volume in $volumes)
{
$volumeData = [pscustomobject]@{ComputerName=$Computer
Drive=$volume.DriveLetter
VolumeLabel=$volume.Label
VolumeSize=”{0:N0}” -f ($volume.Capacity / 1GB)
FreeSpace=”{0:N0}” -f ($volume.FreeSpace/1GB)}
if ($volume.Capacity)
{
$percentage = “{0:P0}” -f ($volume.FreeSpace / $volume.Capacity)
$volumeData | Add-Member -NotePropertyName “PercentageFree” -NotePropertyValue $percentage
}
else
{
$volumeData | Add-Member -NotePropertyName “PercentageFree” -NotePropertyValue “n/a”
}
Write-Output $volumeData
}
}
}
}
May 4th, 2017 at 10:31 am
Thanks, Robert. I really appreciate this contribution, and, with your permission, I’ve turned this in to a Guest Post on the blog. After a bit of formatting and cleanup since this comment editor doesn’t give you many options!
August 21st, 2018 at 6:05 am
Will this work on Windows servers that do not have powershell installed? Also, would the Windows machine I run this script from have to be on the same network as my servers?
August 21st, 2018 at 9:52 am
I already answered this on the earlier post, but the short answer is, every (supported) Windows Server has PowerShell installed. And this doesn’t, actually, use PowerShell to connect to the remote server, but WMI. It only uses PowerShell on the originating machine as the WMI interface. However, WMI doesn’t cross router boundaries, so far as I’m aware, so yes, they need to be on the same network.