Getting the Free Disk Space of Remote Computers Revisited

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.

4 Responses to Getting the Free Disk Space of Remote Computers Revisited

  • Robert Carlson says:

    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
    }
    }
    }
    }

    • Charlie Russel says:

      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!

  • Shivem says:

    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?

    • Charlie Russel says:

      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.

Leave a Reply

Your email address will not be published. Required fields are marked *