You might not know it, but Recycle Bins could be taking up a considerable amount of space on your Windows servers. This might not be a concern on newer beefed-up servers built with 10GB to 12GB OS partitions. However, if you have older servers with 4GB OS partitions for instance, regaining even a few hundred megabytes of space could be the difference between a functional system and an unstable one. Even if you have a lot of space on your servers, you probably don’t want to leave Recycle Bins full of deleted files, especially if the files might contain sensitive information (e.g., administrators', executives', managers', or HR staff members' Recycle Bins).
To monitor the space consumption of Recycle Bins, I wrote RecycleBinInfo.vbs. This script produces a Microsoft Excel spreadsheet that reports on how much disk space is being used by the Recycle Bins on a server and how much space is available on that server's local hard drive. The script doesn’t just report on the Recycle Bins located on the C drives; it reports on all local hard drives. So for example, if there’s a Recycle Bin on a server's F drive, you’ll also receive information about that Recycle Bin. Before I describe how to use RecycleBinInfo.vbs and how it works, you need to know about the temporary SID database that the script creates.
The SID Database
When a user deletes a file, the file is stored in the Recycle Bin on the user's local computer. The server that users log on to contains the Recycler folder, which contains each user's Recycle Bin. In the Recycler folder, Recycle Bins are named after users' SIDs rather than their user IDs, which makes it difficult to identify the users that the Recycle Bins belong to. So, RecycleBinInfo.vbs uses ActiveX Data Objects (ADO) disconnected recordsets to create a temporary SID database. This database is an XML file that contains SIDs, user IDs, and SID types that have been obtained from a domain controller (DC). RecycleBinInfo.vbs uses this database to get the user IDs associated with the SIDs. If there isn't a user ID associated with a SID, the amount of disk space used will still appear for that SID in the report. The SID just won’t have a user ID associated with it.
Two situations can result in a missing user ID:
- As you probably know, SIDs are used for not only domain accounts but also local accounts. However, because the SID database contains only domain accounts and any local accounts on the DC used to populate the database, the user ID will be blank if the SID refers to a local account on a member server. Most systems administrators restrict the creation of local accounts on member servers, so when the report is produced, you’ll probably see only a few SIDs with no associated user IDs. One account that will undoubtedly be blank will belong to the local administrator of the member server, which you can easily identify because any SID ending in -500 refers to the Local Administrator account.
- When a SID belongs to a deleted account, the user ID will be missing. There's an advantage to this situation: The script provides you with a means of identifying old and unnecessary folders that you can delete.
RecycleBinInfo.vbs uses the same SID database used by the UpdateSIDdb.vbs script, which I discuss in "Turn On, Tune In, and Disconnect, Part 1" and "Turn On, Tune In, and Disconnect, Part 2." If you currently use UpdateSIDdb.vbs to maintain a SID database, you can have RecycleBinInfo.vbs use that database. (I explain how in the "How to Use RecycleBinInfo.vbs" section.)
How to Use RecycleBinInfo.vbs
You can download RecycleBinInfo.vbs by clicking the Download the Code Here button near the top of the page. You need to run this script with a Domain Admins–level account on a machine running Excel. If you're running the script on Windows Vista, see the sidebar "Vista's UAC Can Cause Problems When Writing and Running Scripts." RecycleBinInfo.vbs is set up to use the C:\Temp folder as the location for the SID database. You can modify the script to use another location, but you need to make sure that all references to C:\Temp in the script are changed to the path you selected. Before you run RecycleBinInfo.vbs, you need to do the following:
- Create a text file that contains a list of the servers on which you want to check the Recycler folder. This input file should contain just the names of the servers. Each server name needs to be on a separate line. In RecycleBinInfo.vbs, the input file's path and filename are hard-coded as C:\Temp\RecyclerMasterList.txt, but you can change the filename and store the file in any folder you want.
- Customize the MasterComputerSIDSource variable. In the line
- Customize the RecyclerServerList variable. In the line
MasterComputerSIDSource = "DomainControllerName"
replace DomainControllerName with the name of your closest DC. If the SID database already exists in your network because you regularly run UpdateSIDdb.vbs, replace DomainControllerName with the name of the DC that you specified in UpdateSIDdb.vbs.
RecyclerServerList = "C:\Temp\RecyclerMasterList.txt"
replace C:\Temp\RecyclerMasterList.txt with your input file's path and filename. If you named your input file RecyclerMasterList.txt and you stored it in the C:\Temp folder you can leave this line as is.
How RecycleBinInfo.vbs Works
Let’s take a look at RecycleBinInfo.vbs to see how the code comes together. Listing 1 shows the main block of code in this script. I begin RecycleBinInfo.vbs by declaring and setting the variables and constants and disabling error handling. One of the variables being set is FileString, which contains the SID database's path and filename. Instead of naming the SID database solely after the DC name stored in the MasterComputerSIDSource variable, I opted to add a little more description to the database's filename by adding DRS- (short for disconnected recordset) in front of the DC's name and -SID after the DC's name. So, the database's filename is DRS-DomainControllerName-SID.xml, where DomainControllerName is your DC's name. As the line
FileString = "C:\Temp\DRS-" & MasterComputerSIDSource & "-SID.xml"
shows, the database resides in the C:\Temp folder.
Next, the script ascertains the name of the user's domain and assigns it the DomName variable. This variable is used later to search the database for specific SIDs. You might be wondering why the domain is needed. After all, SIDs are supposed to be unique, so why not just search for the SID? After doing some tests, I found that SIDs aren’t necessarily unique when it comes to member servers. In my domain, I found two identical SIDs on two member servers—and those SIDs referred to different users.
To prevent the script from finding the wrong user ID, I added code that first attempts to find a user ID that contains the same local computer name as the target SID. If nothing is found, the script searches for a domain user ID associated with that particular SID. This second search differs slightly from the first one in that the second search looks for a user ID that contains the domain name instead of the computer name. Admittedly, I shouldn’t have to search for anything more than the SID in this second search because domain SIDs are always unique. However, I figured it wouldn’t hurt to refine the second search. Plus, the refinement makes debugging easier.
After setting up the DomName variable, the script opens the SID database by calling the OpenXML subroutine, which Listing 2 shows. OpenXML checks for the database's existence. If the database exists, the subroutine opens it with the code
If the database doesn't exist, the subroutine has the CreateDRS subroutine in Listing 3 create a temporary SID database. If you look at callout A in Listing 3, you’ll see that CreateDRS uses Windows Management Instrumentation (WMI) to access the DC referenced in the MasterComputerSIDSource variable, then uses WMI's Win32_Account class to gather account information from that DC. CreateDRS retrieves the values of the Win32_Account class's Caption, SID, and SIDType properties and writes those values to the database, as callout B shows.
The Caption property contains the domain name and user ID in the format DomainName\UserID if it's a domain account or the server name and user ID in the format ServerName\UserID if it's a local account. The Caption property's value is stored in the database's "User" column. The SID property holds the user's SID, which is stored in the database's "SID" column. The SIDType property contains a numeric value that represents the type of account (e.g., 1 for user, 2 for group). This value is stored in the database's "SIDType" column. Although the Excel report doesn't include the SIDType values, I chose to keep them in the SID database so that the database code in RecycleBinInfo.vbs is consistent with the database code in UpdateSIDdb.vbs.
Now that the SID database exists and is open, the code at callout A in Listing 1 opens the text file that contains the server list. If the file doesn’t exist, the script displays a brief error message, then quits. Otherwise, the script displays a message that lets the user know that the process could take a while depending on how many servers are in the list.
The code after callout A creates an Excel spreadsheet and adds column headings. The script then reads the input file's server names into an array. The main processing section that follows is where the script iterates through all the servers in the array and reports on the Recycle Bins found on each machine.
For each server name in the array, the script uses WMI to connect to that server. Assuming the connection succeeds, the script performs the following tasks:
Determines the server's OS version (callout B). Using WMI's Win32_OperatingSystem class, the script determines the server's OS version. This information is necessary because the Recycler folder's name is $recycle.bin in version 6.0 Windows OSs (which includes Windows Server 2008 and Vista) and recycler in version 5.0 Windows OSs (which includes Windows Server 2003, Windows XP, and Windows 2000). The OS version is stored in the MajorOSVer variable.
Finds the server's local drives (callout C). The script uses WMI's Win32_LogicalDisk class to query the server for drive information. It uses the SELECT statement
SELECT * from Win32_LogicalDisk WHERE DriveType='3'
to limit the returned collection of drives to DriveType 3, which are local drives.
Obtains the collection of Recycle Bins in the Recycler folder on each local drive (callout D). For each local drive, the script uses WMI's Win32_Directory and Win32_Subdirectory classes in an ASSOCIATORS OF query to obtain the collection of Recycle Bins from the $recycle.bin folder (version 6.0 OSs) or recycler folder (version 5.0 OSs).
Obtains the data for each Recycle Bin and writes that data to the Excel spreadsheet (callout E). As The Excel spreadsheet contains five columns with the headings "Computer," "SID," "RecyclerBin Size(Mb)," "User," and "Drive FreeSpace(Mb)." The script obtains the data for the "Computer" column by referencing the strComputer variable, which contains the server name. The data for the "SID" column is obtained by retrieving the value stored in the Recycle Bin's Name property (objfolder.name), which is actually the user's SID. The data for the other three columns is gathered through individual function calls:
- To get the data for the "RecyclerBin Size(Mb)" column, the script calls the GetFolderSize function in Listing 4. This function returns the size (in megabytes) of the user's Recycle Bin.
- To get the data for the "User" column, the script calls the FindSID function in Listing 5. This function first clears any error conditions that might exist and makes sure it's accessing the first record in the database. FindSID then performs the first of the two possible searches I described earlier to find the user ID associated with a particular SID. In the first search, FindSID takes the SID that was passed to it and constructs a filter string using the code
ComputerUserLookup = "SID = '" & sidval & "' AND User Like '*" & strComputer & "*'"Simply put, this filter is looking for a record that meets two conditions: the SID being passed in is equal to the Recycle Bin's SID and the value in the database's "User" column contains the server name. The next line
DRS.Filter = ComputerUserLookupapplies the filter just constructed to the database. Afterward, the function checks to see if an error occurred (an error means you entered an invalid filter string) and checks to see if the record pointer is at the beginning or end of the database file. If an error didn't occur and the record pointer isn't at the beginning or end of the file, it means the record was found, in which case FindSID retrieves the value in the found record's "User" column and writes that value to the spreadsheet's "User" column. If a record wasn't found, a second search attempt is made. The function constructs a similar filter string, except this time the filter looks for the domain name rather than the server name in the value stored in database's "User" column. The second filter is then applied to the database. If a record is found, the value in that record's "User" column is retrieved and written to the spreadsheet.
- To get the data for the "Drive FreeSpace(Mb)" column, the script calls the GetFreeSpace function in Listing 6. This function uses WMI's Win32_LogicalDisk class to obtain the amount of free space (in megabytes) available on the drive being checked.
After processing all the local drives on all the servers, RecycleBinInfo.vbs uses CloseIt subroutine to close the database, then displays a message letting you know it's done. You'll then have an easy to read report that tells you whether users' Recycle Bins are wasting a lot of space on your Windows servers.