During a recent class I was teaching, a student asked about a way to get disk quota reports from Windows file servers.  I knew there was a WMI class, Win32_DiskQuota, and had some old VBScript files. However, they were pretty basic and not as robust as I would have liked. So I worked up PowerShell v2 function called Get-DiskQuota.

The function includes parameter help, as so almost all of my v2 scripts, so that  you can get help and examples.

captured_Image.png

The function takes a computername as a parameter, although it defaults to the local computer. You can retrieve quota information for all volumes and users on the server, or only for a particular volume and/or user. Because the function uses WMI, it also supports PSCredentials. Here’s a sample using an alias, gdq, for the Get-DiskQuota function.

PS C:\> gdq -Computername jdhit-dc01 -Volume e: -credential $jdhit

Volume           : E:
Computername     : JDHIT-DC01
Username         : jdhitsolutions\jhicks
UsedSpaceMB      : 73.228515625
PercentUsed      : 1.43 %
LimitGB          : 5
LimitRemainingGB : 4.93
WarningGB        : 3
OverWarning      : False
OverLimit        : False

The PSCredential must be passed as an object. I created the $jdhit object earlier with Get-Credential.

Here’s the code for you to look at and then I’ll go over a few key steps.

Function Get-DiskQuota {

#Requires -version 2.0


<#

.Synopsis

    Retrieve quota usage information.

.Description

    This function connects to the specified computer, default is the localhost, and returns quota

    usage information. Quota information will NOT be returned for the Domain Admins group or the

    Administrator account. It will also exclude entries without a quota limit. Quota information for

    all volumes is returned by default. Otherwise you can filter by volume, by user or by volume

    and user. The Domain parameter is used to more accurately define the user account. If you want

    to filter by domain alone, then run the function without any user filtering and pipe it to

    Where-Object where you can filter by the domain name.


    This function uses WMI so you must have RPC connectivity and appropriate credentials. You do

    not need PowerShell installed on the remote computer.


.Parameter Computername

    The name of the computer to query.

.Parameter Volume

    Return quota information for a specific volume on the remote server such as E:.

.Parameter Username

    Return quota information for a specific user. Use the SAMAccountname.

.Parameter Domain

    The NETBIOS domain name of the user account you are querying. The default is the domain of the

    current user running the function.

.Parameter Credential

    A PSCredential object to use for alternate credential authentication. This must be a previously

    created object.


.Example

    PS C:\> get-diskquota server02


    Get quota usage for all users on all volumes on SERVER02.

.Example

    PS C:\> get-diskquota server01 -volume F: -cred $admin


    In this example, quota information for all users on volume F: is returned. The query is made

    using alternate credentials, previously saved as $admin.


.Example

    PS C:\> get-diskquota "File01","File02" -user jfrost -domain MyCompany


    Query servers File01 and File02 for all quota entries for user mycompany\jfrost on all volumes.


.Example

    PS C:\> get-content servers.txt | get-diskquota -user Jhicks | Export-csv jhicks-quota.csv


    This example will take every server name in servers.txt and pipe it to the Get-Diskquota function

    which searches all volumes for the jhicks user account. The quota usage information is then piped

    to a CSV file using Export-CSV.


.Inputs

    Accepts strings as pipelined input

.Outputs

    A custom object with quota entry information


.Link

http://jdhitsolutions.com/blog


.Link

   Get-WmiObject


.Notes

 NAME:      Get-DiskQuota

 VERSION:   1.0

 AUTHOR:    Jeffery Hicks

 LASTEDIT:  11/18/2009



#>


[CmdletBinding()]


Param (

    [Parameter(

    ValueFromPipeline=$True, Position=0, Mandatory=$False,

    HelpMessage="The computername to query")]

    [string[]]$Computername=$env:computername,



    [Parameter(

    ValueFromPipeline=$False,Mandatory=$False,

    HelpMessage="The volume name on the remote server. Use a value like E:.")]

    [string]$Volume,


    [Parameter(

    ValueFromPipeline=$False,Mandatory=$False,

    HelpMessage="The samAccountname of a user such as JDOE")]

    [string]$Username,


    [Parameter(

    ValueFromPipeline=$False,Mandatory=$False,

    HelpMessage="The domain name of the queried user such as MyCompany")]

    [string]$Domain=$env:Userdomain,



    [Parameter(

    ValueFromPipeline=$False,

    Mandatory=$False,

    HelpMessage="A previously created PSCredential object.")]

    [Parameter(ParameterSetName="Set2")]

    [management.automation.pscredential]$credential


     )



BEGIN {

    Write-Verbose "Starting function"


    if ($Username) { Write-Verbose "Looking for entries for $domain\$username"}


    #only retrieve quota entries that are limited

      $query="Select * from win32_diskquota where limit < 18446744073709551615"


    if ($Volume -AND $Username) {

        Write-Verbose "Building volume and username query"

        $query=$query + " AND QuotaVolume=""Win32_LogicalDisk.DeviceID='$volume'"" AND User =""Win32_Account.Domain='$domain',Name='$Username'"""


    }

    elseif ($Volume) {

        Write-Verbose "Building volume query"

         $query=$query + " AND QuotaVolume=""Win32_LogicalDisk.DeviceID='$volume'"""



    }

    elseif ($Username) {

        Write-Verbose "Building username query"

        $query=$query + " AND User =""Win32_Account.Domain='$domain',Name='$Username'"""

    }

    else {

        Write-Verbose "Getting all quota entries"

    }


    Write-Verbose "Query = $query"

} #end BEGIN


PROCESS {



    $Computername | foreach {



        #define an error handling trap in the same scope

        Trap {

            Write-Warning "There was an exception retrieving quota information from $computer)"

            Write-Warning $_.Exception.Message

            #bail out

            Return

        }


        $computer=$_.ToUpper()

        Write-Verbose "Querying $computer"


        if ($credential) {

            $quotas=get-wmiobject -query $query -computer $computer -Credential $credential -ea Stop

        }

        else {

            $quotas=get-wmiobject -query $query -computer $computer  -ea Stop

           }


        #get quota entries and filter off local user accounts, administrator and domain admins

        $entries = $quotas | where {$_.user -notmatch "$computer|Administrator|Domain Admins"}


        if ($entries) {

            Write-Verbose "Found $($entries.count) entries"

            foreach ($entry in $entries) {

              Write-Verbose "Processing entry"

              #remove quotes from user property and split the user entry at the comma

              #assuming no commas in the domain or username


              $replaced=($entry.user).Replace('"',"")

              Write-Verbose "`$replace = $replaced"


              $userdata=$replaced.split(",")


              #item 0 will be the domain and item 1 will be the user. Each item needs

              #to be further split.  The domain and username values should be the same as any

              #values passed as paramters.  I'll override the default domain parameter value. 

              #Otherwise, if it isn't specified and the admin running the script isn't in the domain

              #you'll get the wrong domain listed.

               $domain=$userdata[0].Split("=")[1]

               $username=$userdata[1].Split("=")[1]


               Write-Verbose "Parsed out $domain and $username"


              #parse QuotaVolume and strip off quotes

              $Volume=($entry.quotavolume.split("=")[1]).Replace('"',"")

              $Volume=$Volume.ToUpper()

              Write-Verbose "`$volume=$volume"


              #calculate % of quota used

              $percentUsed="{0:P2}" -f ([double]$entry.DiskSpaceUsed/[double]$entry.Limit)

              Write-Verbose "`$percentused = $percentused"


              #calculate limit remaining space

              [double]$limitRemaining="{0:N2}" -f (([double]$entry.limit - [double]$entry.DiskSpaceUsed)/1GB)

              Write-Verbose "`$limitRemaining = $limitRemaining"


              #is user over the warning limit?

              if ($entry.diskspaceused -gt $entry.WarningLimit) {

                $Warning=$True

               }

              else {

                $Warning=$False

              }

              Write-Verbose "`$Warning=$warning"


             #is user over the hard limit?

              if ($entry.diskspaceused -gt $entry.Limit) {

                $OverLimit=$True

               }

              else {

                $OverLimit=$False

              }

              Write-Verbose "`$overlimit=$overlimit"

              Write-Verbose "Creating blank object"


              #create a blank object

              $obj = New-Object PSObject

              Write-Verbose "Adding properties to the object"


              #add some properties to the object

              $obj | Add-Member -membertype NoteProperty -Name Volume -Value $Volume

              $obj | Add-Member -membertype NoteProperty -Name Computername -Value $computer

              $obj | Add-Member -membertype NoteProperty -Name Username -Value "$domain\$username"

              $obj | Add-Member -membertype NoteProperty -Name UsedSpaceMB -Value (($entry.DiskSpaceUsed/1MB) -as [double])

              $obj | Add-Member -membertype NoteProperty -Name PercentUsed -Value $percentUsed

              $obj | Add-Member -membertype NoteProperty -Name LimitGB -Value (($entry.Limit/1GB) -as [int])

              $obj | Add-Member -membertype NoteProperty -Name LimitRemainingGB -Value $limitRemaining

              $obj | Add-Member -membertype NoteProperty -Name WarningGB -Value (($entry.WarningLimit/1GB) -as [int])

              $obj | Add-Member -membertype NoteProperty -Name OverWarning -Value $Warning

              $obj | Add-Member -membertype NoteProperty -Name OverLimit -Value $OverLimit


              #write object to the pipeline

              Write-Verbose "Writing the object to the pipeline"

              write $obj


            } #end ForEach 


        } #end if


        else {

          Write-Warning "No quota entries found that match your query on $computer"

          Write-Warning $query

        }

    } #end ForEach Computername

} #end PROCESS


END {

    Write-Verbose "Exiting function"

} #end END


} #end Function

The function only returns quota information for volumes which are configured with limits.

$query=”Select * from win32_diskquota where limit < 18446744073709551615″

The function builds a query based on the parameter values you pass. The filtering for user account is a little tricky because WMI retrieves an associated Win32_Account object

$query=$query + ” AND User =”"Win32_Account.Domain=’$domain’,Name=’$Username’”"”

The function defaults to the current domain, but you can specify  a domain. The Domain parameter is used to more accurately define the user account. If you want to filter by domain alone, then run the function without any user filtering and pipe it to Where-Object where you can filter by the domain name.

All of the query string building in accomplished in the BEGIN script block. The query is executed in the PROCESS script block. You can pipe a collection of server names to the function.

PS C:\> get-content servers.txt | get-diskquota -user Jhicks | Export-csv jhicks-quota.csv

The function takes the resulting WMI information, parses and massages and writes a custom object to the pipeline. The END script block isn’t used, unless you include the –Verbose common parameter. The function will display progress information which can be helpful when debugging or troubleshooting. I encourage you to use code like this in your PowerShell v2 scripts and function.

Thanks to Wes Stahler, Aubrey Moren and Arnaud Petitjean for kicking my code around but of course any problems or bugs are mine and I hope you’ll let me know.

Download Get-DiskQuota

4 Responses to “Get Disk Quota”

  1. Hmmm, would it be possible to pipe in a list of DirectoryEntry objects from Active Directory? One potentially useful scenario for this function, would be the ability to pass in a list of Directory objects straight from AD, and then have the function be intelligent enough to strip out just the system name.

    Just an idea for enhancement :)

    Cheers,
    -Trevor Sullivan

    • Jeffery Hicks
      12:32, 23.11.2009

      All you need is code to retrieve a computername and pipe it to the function or pass it as a parameter. So sure, it shouldn’t be too difficult. Using the free Quest cmdlets is the easiest approach.

  2. Jeffery Hicks
    12:35, 23.11.2009

    You don’t want to bog down PowerShell functions with too many bells and whistles, I prefer to keep them focused on single tasks. So create a function to write a computername to the pipeline then pipe it to Get-DiskQuota.

    Get-MyComputers | Get-DiskQuota | Export-CSV QuotaReport.csv

  3. Free eBook – Windows PowerShell v.1 TFM…

    SAPIEN Technologies has released their Windows PowerShell v.1 TFM eBook (18.4 MB), by Don Jones and Jeff…