Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I determine which users are using the most disk space on a drive?

-- PW

SpacerHey, Scripting Guy! AnswerScript Center

Hey, PW. Interesting question. In theory, you could probably do this by retrieving a collection of all the files on a drive, checking the owner, determining the file size, and then keeping a running tally of the amount of disk space charged to each user. Like we said, in theory that would work, but it would be a complicated little script, and, depending on the number of files on the drive, you might bring your computer to its knees while the script performs all its machinations.

Therefore, we decided to go with an approach that is much easier, much faster, and much less resource-intensive. The catch? (Oh, come on: you knew there had to be a catch here.) You’ll have to enable disk quotas on the drive in question in order for this to work.

We won’t spend much time talking about enabling and disabling disk quotas in today’s column. If you’re running Windows XP or Windows Server 2003, you can find a fairly extensive article on disk quotas in our Scripting for Windows Server 2003 center. If you’re running Windows 2000, the Microsoft Windows 2000 Scripting Guide includes information on using scripts to enable and disable disk quotas. Keep in mind that when we say you have to enable disk quotas on the drive, that doesn’t mean you have to enforce those quotas: you can enable disk quotas and allow the operating system to tally up the amount of disk space charged to each user without restricting access to the drive in any way. And, of course, you can always enable disk quotas, run your script, and then disable disk quotas again.

Note. If you decide to do the old enable/disable trick, keep in mind that it will likely take several minutes - at least - for the operating system to tally up disk space use after quotas have been enabled. In other words, don’t enable disk quotas and then immediately run the script to determine disk space usage; that’s probably not going to work.

Assuming you have enabled disk quotas on drive C of a computer, and assuming you’re running Windows XP or Windows Server 2003, here’s a script that will return the amount of disk space currently charged to each user:

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

strDrive = "Win32_LogicalDisk.DeviceID=" & chr(34) & "C:" & chr(34)

Set colQuotas = objWMIService.ExecQuery _
    ("Select * from Win32_DiskQuota Where QuotaVolume = '" & strDrive & "'")

For Each objQuota in colQuotas
    Wscript.Echo "User: "& objQuota.User      
    Wscript.Echo "Disk Space Used: "& objQuota.DiskSpaceUsed
Next

As you can see, the script begins simply enough, setting a variable named strComputer to a dot (WMI notation for the local computer) and then connecting to the WMI service. (Although this example connects to the WMI service on the local computer, you can retrieve disk quota information for a remote computer simply by assigning the name of that computer to the variable strComputer.) We then have this interesting line of code:

strDrive = "Win32_LogicalDisk.DeviceID=" & chr(34) & "C:" & chr(34)

As it turns out, the Win32_DiskQuota class - the class we’ll be working with to retrieve information about disk space usage - is a bit different than most WMI classes. For one thing, the QuotaVolume property - which indicates the drive we’re working with - isn’t a typical WMI property; instead, it’s actually an embedded instance of the Win32_LogicalDisk class. That’s fine, but it does make it a bit awkward to include QuotaVolume in a Where clause; that’s because the value of QuotaVolume will look something like this:

Win32_LogicalDisk.DeviceID="C:"

Inside a Where clause, it’s tricky to deal with the double quote marks around the drive letter. Because of that, we use the preceding line of code to create a string value equivalent to Win32_LogicalDisk.DeviceID="C:", using Chr(34) as a way to add in the double quotes. It’s an extra line of code, but it’s much easier to put the variable strDrive into the Where clause than it is to put Win32_LogicalDisk.DeviceID="C:".

Trust us; it is.

Speaking of which, the next thing we do is call the ExecQuery method to return a collection of all the disk quota entries for drive C:

Set colQuotas = objWMIService.ExecQuery _
    ("Select * from Win32_DiskQuota Where QuotaVolume = '" & strDrive & "'")

From there we set up a For Each loop to walk through the collection, echoing back the user name and the amount of disk space (for drive C) currently charged to the user.

Now, that works great on Windows XP or Windows Server 2003, but it won’t do you much good on Windows 2000; that’s because Windows 2000 doesn’t support the Win32_DiskQuota class. So are you out of luck if you’re running Windows 2000? Of course not; you can just use the DiskQuota object instead:

Set colDiskQuotas = CreateObject("Microsoft.DiskQuota.1")
colDiskQuotas.Initialize "C:\", True

For Each objUser in colDiskQuotas
    Wscript.Echo "Logon name: " & objUser.LogonName
    Wscript.Echo "Quota used: " & objUser.QuotaUsed
Next

As you can see, here we create an instance of the Microsoft.DiskQuota.1 object, then use the Initialize method to return the collection of disk quota entries for C:\. (Don’t leave off the \; it’s required.) We then set up a For Each loop and, for each quota entry, echo the values of the LogonName and the QuotaUsed properties.

And you’re right: this script is a lot easier than the one we showed you for Windows XP and Windows Server 2003. So why didn’t we just show you this script and call it good? Well, the catch here - oh, come on: you knew there had to be a catch here, too - is that the DiskQuota object can’t be created remotely; that means the latter script can run only on the local computer. The WMI script might be a tad bit more complicated, but it has the advantage of working against remote computers as well.