Hey, Scripting Guy! Question

Hey, Scripting Guy! In a logon script, how can I report back disk quota information (like amount of space used and amount of space remaining) for the logged-on user?

-- HY

SpacerHey, Scripting Guy! AnswerScript Center

Hey, HY. You know, today is shaping up as another tough day for the Scripting Guy who writes this column. For example, even as we speak he’s busy sipping a cappuccino in a sidewalk café on the Piazza San Marco in Venice, perusing an Italian newspaper and waiting for the family to begin their visit to the Basilica di San Marco. From there, they’ll take a backstage tour of the Doge’s Palace. After that the Scripting Guy who writes this column will likely eat some gelato (Italian ice cream), take a leisurely cruise along the Grand Canal, then eat some more gelato. Definitely going to be a tough day. Oh, waiter: un altro cappuccino, per favore.

Note. As usual, we know what you’re thinking. You’re thinking, “But the Scripting Guys work really hard, don’t they? Therefore, the Scripting Guy who writes this column truly deserves a vacation like this, doesn’t he?” To answer your questions: no they don’t, and no he doesn’t. But he’s taking advantage of it anyway.

Needless to say, the Scripting Guy who writes this column is finding it hard to write this column. But that’s OK: after all, Venice is a tourist trap, there’s no doubt about that. And that’s good, because it means there are street vendors everywhere, street vendors happy to sell you just about anything, including a logon script that reports back disk quota information for the logged-on user:

strComputer = "."

Set objNetwork = CreateObject("Wscript.Network")
strUser = objNetwork.UserName
strDomain = objNetwork.UserDomain

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

Set colDisks = objWMIService.ExecQuery _
    ("Select * from Win32_QuotaSetting Where State = 1")

For Each objDisk in colDisks
    strDrive = objDisk.VolumePath
    strDrive = Replace(strDrive, "\", "")

    Set objQuota = objWMIService.Get _
        ("Win32_DiskQuota.QuotaVolume='Win32_LogicalDisk.DeviceID=" & chr(34) & strDrive & chr(34) & "'," & _
            "User='Win32_Account.Domain=" & chr(34) & strDomain & chr(34) & _
                ",Name=" & chr(34) & strUser & chr(34) & "'")

    Wscript.Echo "Drive: " & objDisk.VolumePath
    Wscript.Echo "Disk Space Used: " & Int(objQuota.DiskSpaceUsed / 1048576) & " megabytes"
    Wscript.Echo "Quota Limit: " & Int(objQuota.Limit  / 1048576) & " megabytes"
    Wscript.Echo "Disk Space Remaining: " & Int((objQuota.Limit - objQuota.DiskSpaceUsed) / 1048576) & _
        " megabytes"
    Wscript.Echo
Next

Before we go any further, we should point out that the Scripting Guy who writes this column speaks Italian even worse than he speaks English; that’s making it hard for him to translate the explanation behind this script. (Mario’s – the street vendor we bought the script from – explanation is written in Italian. For some reason, none of the street corner script vendors in Venice seem to speak English.) Nevertheless, we’ll see what we can do.

Note. Mario has managed to communicate to us that this script works only on Windows XP and later versions of Windows. If you need to perform this task on Windows 2000, you can either take a look at the Microsoft Windows 200 Scripting Guide and try to figure out how to do this or let us know, and we’ll see if we can address this in a future column.

Mario also let us know that this script works on Windows Vista only with elevated privileges. That means you should run the script from the command prompt, but when you start the command prompt, right-click and select Run As Administrator.

To begin with, we (or, more correctly, Mario) creates an instance of the Wscript.Network object; he then uses these two lines of code the grab the values of the UserName and UserDomain properties and store them in the variables strUser and strDomain, respectively:

strUser = objNetwork.UserName
strDomain = objNetwork.UserDomain

As you probably know, the UserName returns the logon name of the user (e.g., kenmyer), while the UserDomain returns the domain name for that user (e.g., fabrikam). And that’s good, because when we set out to retrieve disk quota we’re going to need both the user’s name and his or her domain name.

After grabbing the user name and domain name Mario then connects to the WMI service on the local computer. Could we perform this feat against a remote computer? Beats us; we’ll ask Mario.

Mario says, “Certamente.” (Certainly) If you need information about the logged-on user’s disk quota space on a remote computer just assign the name of that remote computer to the variable strComputer:

strComputer = "atl-fs-01"

However, keep one thing in mind: to use WMI remotely requires you to be a local administrator on the remote computer. Because this is designed to be a logon script, that means that the logged-on user (e.g., Ken Myer) must be a local admin on any remote machine the script connects to. So maybe you can get information about disk quotas on a remote computer. Or maybe you can’t.

That brings us to this line of code:

Set colDisks = objWMIService.ExecQuery _
    ("Select * From Win32_QuotaSetting Where State = 1")

What we want to do is check disk quota information for all the disk drives on the local computer. Of course, disk quotas might not even be enabled on all the disk drives; if that’s the case, then our script might return misleading information. (For example, suppose disk quotas are not enabled on a drive. In that case the script will say that the user has 0 available megabytes of quota space. That’s because, technically, no disk quotas means that no one has any disk quota space.) That’s why we added the Where clause WhereState = 1; this filters our data so we get back information only about disk drives where quotas have actually been enabled.

Note. Need more information about working with disk quotas? In that case, Mario recommends that you take a look at this article in the Windows Server 2003 section of the Script Center.

Our next step is to set up a For Each loop that loops us through all the disk drives returned by our WQL query. (That is, all the disk drives where disk quotas have been enabled.) The first thing Mario does inside that For Each loop? Why, he executes these two lines of code, of course:

strDrive = objDisk.VolumePath
strDrive = Replace(strDrive, "\", "")

Why these two lines? Well, to begin with, we need the drive letter of the disk drive in order to retrieve quota information for the logged-on user. (Which makes sense: if you don’t know the drive letter how would the script know which drive to access?) The VolumePath property just happens to contain the drive letter, so we grab the value of that property and stash it in the variable strDrive.

Unfortunately, though, there’s a slight problem here. The VolumePath property stores drive letters like this: C:\. That’s fine, except that the Win32_DiskQuota class needs to work with drive letters in this format: C: (no trailing \). Therefore, in our second line of code we simply use the Replace function to replace any instances of the \ character with, well, nothing:

strDrive = Replace(strDrive, "\", "")

And then … well, and then we run into this:

Set objQuota = objWMIService.Get _
    ("Win32_DiskQuota.QuotaVolume='Win32_LogicalDisk.DeviceID=" & chr(34) & strDrive & chr(34) & "'," & _
        "User='Win32_Account.Domain=" & chr(34) & strDomain & chr(34) & _
            ",Name=" & chr(34) & strUser & chr(34) & "'")

You’re right: this could very well be the scariest-looking WQL query ever devised. (Mario, what were you thinking here!?!) We’re not going to explain this query in any detail today; for an explanation like that, see our article on disk quotas. For now, we’ll simply note that this query binds us to the first disk drive in the collection; more precisely, it binds us to an instance of the Win32_DiskQuota class where the QuotaVolume property happens to be an instance of the Win32_LogicalDisk class where the DeviceID property is equal to the value of the variable strDrive.

Whew. Just a second while we catch our breath.

OK; we’re ready now. And then, after connecting to the appropriate Win32_DiskQuota class, we retrieve quota information for the instance of the Win32_Account class where the Domain property is equal to the user domain and the Name property is equal to the user name.

Thanks, Mario.

We think.

To make a long story short, suppose the first disk drive in our collection of quota-enabled drives is drive C, and suppose the logged-on user is fabrikam\kenmyer. In that case, we’re going to get back disk quota information for user fabrikam\kenmyer for drive C of the local computer. And then we’re going to use these lines of code to echo back that information:

Wscript.Echo "Drive: " & objDisk.VolumePath
Wscript.Echo "Disk Space Used: " & Int(objQuota.DiskSpaceUsed / 1048576) & " megabytes"
Wscript.Echo "Quota Limit: " & Int(objQuota.Limit  / 1048576) & " megabytes"
Wscript.Echo "Disk Space Remaining: " & Int((objQuota.Limit - objQuota.DiskSpaceUsed) / 1048576) & _
        " megabytes"

Granted, some of these lines of code look a little scary, too. That’s because we’re doing a little bit of math and a little formatting before echoing back our property values. By default, disk quota information is returned as bytes; thus you’ll learn that Ken Myer has used 543271897 bytes of disk space. What the heck does that mean? We have no idea; that’s why we take the value of the DiskSpaceUsed property and divide it by 1048576, converting the byes to megabytes:

objQuota.DiskSpaceUsed / 1048576

That’s going to give us back a value similar to this:

518.10445499420166015625

Because we don’t really need to see all those decimal points we use the Int function to take the result of our calculation and return just the integer portion (518). We then do the same thing for the Limit property, which returns information about the user’s disk quota limit.

So then how do we know how much disk space is still available to Ken Myer? That’s easy: we simply take the quota space given to him (Limit) and subtract the amount of disk space he’s already used (DiskSpaceUsed). That’s what we do here:

Wscript.Echo "Disk Space Remaining: " & Int((objQuota.Limit - objQuota.DiskSpaceUsed) / 1048576) & _
        " megabytes"

From there we loop around and repeat the process with the next disk drive in the collection.

That should do it, HY. If you encounter any problems, just let us know.

Wait, check that: let Mario know. As for the Scripting Guy who writes this column, it’s just about time to learn about the “Secret Itineraries” of the Doge’s Palace. The tour sounds pretty interesting, using secret passages to move throughout the palace. Most likely, however, the Scripting Guy who writes this column will skip the Torture Room. Not because he’s squeamish, mind you. But, after all, he is on vacation, and walking into the Torture Room will remind him too much about walking into his manager’s office for their weekly one-on-one sessions.