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.
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
The function includes parameter help, as so almost all of my v2 scripts, so that you can get help and examples.
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 for all users or a user from all volumes on a server or a single volume. .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 https://jdhitsolutions.com/blog/2009/11/get-disk-quota/ .Link Get-WmiObject .Notes NAME: Get-DiskQuota VERSION: 1.1 AUTHOR: Jeffery Hicks LASTEDIT: 12/8/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 ) #end Parameter declaration 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 filter query" $query=$query + " AND QuotaVolume=""Win32_LogicalDisk.DeviceID='$volume'""" } elseif ($Username) { Write-Verbose "Building username filter 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 Set-Alias gdq Get-DiskQuota #end Script # help gdq
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.
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
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.
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