Hey, Scripting Guy! Question

Hey, Scripting Guy! We have this problem at work. Users seem to always be losing their mapped drives. It is a real mess, and we spend a couple hours every morning trying to help the users remap their drives. We have more than 160 logon scripts, and it seems that one logon script maps a drive, and then another remaps the drive somewhere else. To make matters worse, when we go to the workstation and make the drive-mapping persistent, they seem to disappear for no reason. I am at my wit’s end. It would really help, if we just had a log of what drives are mapped for the workstation. We could then ask the user when things last worked and see what their drive-mapping is supposed to be. Is there anything you can do to help?

- SN

SpacerHey, Scripting Guy! Answer

Hi SN,

It seems as if you have a mess on your hands. One hundred and sixty logon scripts? I won’t ask! Sounds as if a little Microsoft Operations Framework (MOF) Training would not hurt your organization. As a matter of fact, we just came out with MOF version 4.0, which covers the IT management life cycle through three distinct phases: planning, delivery, and operations. 

Part of the problem is that you are making changes to your environment without having achieved a reliable, stable situation. Without knowing what the desktop environment is, it is hard to plan and deploy changes in an organized fashion. This is leading to increased problem management and customer service issues. Go ahead and download the free documentation.

Of course, you know you have a problem, or you would not have asked for a tool to help you with the first step: Document what you actually have. What is the drive-mapping on a user’s machine when they are not having problems? Then document what the drive-mapping is on a user’s computer when they are having problems, and figure out what was changed that may be causing the problem.

In the WriteMappedDrivesToFile.vbs script, we will define a couple of WMI queries, create a dictionary to store our WMI information, and use FileSystemObject to write our information to a file. (Use of FileSystemObject is covered in chapter 6 of the Microsoft Press book Microsoft VBScript Step by Step. Along the way, we will use the replace function to help us create a file name. I know you can’t wait, so here is the WriteMappedDrivesToFile.vbs script:

strComputer = "."

Const networkdisk = 4 
Const ForWriting = 2
Const ForAppending = 8
wmiQuery="Select DeviceID, ProviderName from Win32_LogicalDisk " & _
"where DriveType =" & networkdisk
wmiQuery1 ="Select Name, UserName from Win32_ComputerSystem"

Set objDictionary = CreateObject("scripting.dictionary")
Set objWMIService = GetObject("winmgmts:\\" & strComputer)
set colDrives = objWMIService.ExecQuery(wmiQuery)
Set colUsers = objWMIService.ExecQuery(wmiQuery1)

For Each user In colUsers
 userName = replace(user.UserName,"\","_")
 dnsHostName = user.Name
Next

if colDrives.count <> 0 Then
 for each drive in colDrives
  objDictionary.add drive.DeviceID, drive.ProviderName
  colKeys = objDictionary.Keys
  colItems = objDictionary.Items
 Next
Else
 objDictionary.add " No Drives Mapped ", " No Drives Mapped "
  colKeys = objDictionary.Keys
  colItems = objDictionary.Items
End if

sublogDrives 

Sub sublogDrives
 LogFile = "C:\FSO\" & username & ".txt"
 Set objFSO = CreateObject("Scripting.FileSystemObject")
 If objFSO.FileExists(LogFile) Then
  Set objFile = objFSO.OpenTextFile(LogFile, ForAppending)
  objfile.writeblanklines(2)
  objFile.Writeline "******** " & Now & "********"
  objFile.writeline "User is: " & username 
  objFile.writeline "computer is: " & dnsHostName
     For Each strKey In colKeys
      objFile.writeline vbtab & strKey & objDictionary.item(strKey)
     Next 
 Else
   Set objFile = objFSO.CreateTextFile(LogFile)
   objFile.Writeline "******** " & Now & "********"
   objFile.writeline "User is: " & username 
   objFile.writeline "computer is: " & dnsHostName
     For Each strKey In colKeys
      objFile.writeline vbtab & strKey & objDictionary.item(strKey)
     Next 
 End If
 objFile.Close
End sub
WScript.echo "all done"

Our script begins by defining a couple of variables and constants. We then create two strings that will serve as our two WMI queries. The first string, called wmiQuery, chooses DeviceID and ProviderName from the Win32_LogicalDisk WMI class. We limit the returned data to only drives that are network drives (in other words, mapped drives). The second WMI query, contained in the variable wmiQuery1(like everything else in VBScript, it is zero based), chooses the name of the computer and the name of the user from theWin32_ComputerSystem WMI class. We will use this information for our file output. These two lines of code are seen here:

wmiQuery="Select DeviceID, ProviderName from Win32_LogicalDisk & _
where DriveType =" & networkdisk
wmiQuery1 ="Select Name, UserName from Win32_ComputerSystem"

Now we need to create a couple of objects. The first is the dictionary object, which we will use to hold the drive-mappings we obtain from WMI. The next object is our connection into WMI, which is followed quickly by the objects returned by executing our two WMI queries. This code can be seen here:

Set objDictionary = CreateObject("scripting.dictionary")
Set objWMIService = GetObject("winmgmts:\\" & strComputer)
set colDrives = objWMIService.ExecQuery(wmiQuery)
Set colUsers = objWMIService.ExecQuery(wmiQuery1)

Because we executed our WMI queries by using the ExecQuery method, the objects that are returned are collections. This means we need to use for each next to iterate through the collections and extract our data. We first want to obtain the name of the computer and the name of the user. So we use the for each next loop and walk through the collection that is held in the colUsers variable. Inside the loop, we use the replace function to replace the “\” that is in the username with an “_” because the “\” will cause us problems with the file name. The first position of the replace function is used to hold the text we want to modify. The second position holds the string we wish to find, and the third position contains the value with which we will perform the replacement. There are three other parameters available for the replace function, but we did not need them here. Once we have the data, we store the values into a couple of variables we will use later:

For Each user In colUsers
 userName = replace(user.UserName,"\","_")
 dnsHostName = user.Name
Next

Now we need to look through the drives that were returned by our query. The first thing we need to do is make sure we actually have some drives. If we do, we will walk through the collection and add the drive letter (deviceID) and share mapping (providerName) to our Dictionary object. However, if there are no drives, we simply add the string, “No Drives Mapped” to both the key and the value of our dictionary. This will help to avoid errors later in the script:

if colDrives.count <> 0 Then
 for each drive in colDrives
  objDictionary.add drive.DeviceID, drive.ProviderName
  colKeys = objDictionary.Keys
  colItems = objDictionary.Items
 Next
Else
 objDictionary.add " No Drives Mapped ", " No Drives Mapped "
  colKeys = objDictionary.Keys
  colItems = objDictionary.Items
End if

Now go into the sublogDrives subroutine. Inside here, we first define the path to our output file. We assume there is a folder namedc:\fso, but we do not assume the log file itself exists. You could use the folderexists method from the FileSystemObject if you wish to check for the existence of the folder. Next, we create the FileSystemObject and use the FileExists method to see if the log file currently exists. If the log file does exist, we begin the process of writing to the file. To write to the file, we first use the OpenTextFilemethod. Because we know the file already exists, we open the file so that we can append to it. We specify this action by using our previously defined ForAppending constant. Because we would like a little bit of space between entries, we use the WriteBlankLinesmethod from the FileSystemObject. Next we use the WriteLine method to print our data to the file. This code is seen here:

Sub sublogDrives
 LogFile = "C:\FSO\" & username & ".txt"
 Set objFSO = CreateObject("Scripting.FileSystemObject")
 If objFSO.FileExists(LogFile) Then
  Set objFile = objFSO.OpenTextFile(LogFile, ForAppending)
  objfile.writeblanklines(2)
  objFile.Writeline "******** " & Now & "********"
  objFile.writeline "User is: " & username 
  objFile.writeline "computer is: " & dnsHostName
     For Each strKey In colKeys
      objFile.writeline vbtab & strKey & objDictionary.item(strKey)
     Next

If the file does not exist, we will want to create the file. To do this, we use the CreateTextFile method from FileSystemObject. We then use the same pattern of writing blank lines and iterating through the dictionary to populate the file. When we are done, we close the text file. This action is important to prevent possible file locking issues. It is simple enough to do: We call the Close method. We are done, and so we print out a friendly “all done” by using Wscript.echo. If you call this from a logon script, you probably will not want the friendly “all done” message. You can comment it out if you wish:

Else
   Set objFile = objFSO.CreateTextFile(LogFile)
   objFile.Writeline "******** " & Now & "********"
   objFile.writeline "User is: " & username 
   objFile.writeline "computer is: " & dnsHostName
     For Each strKey In colKeys
      objFile.writeline vbtab & strKey & objDictionary.item(strKey)
     Next 
 End If
 objFile.Close
End sub
WScript.echo "all done"

Whew! We are done. Here is what the text file looks like:

Mapped drives image 

SN, I hope this script can help you in beginning the long process of weeding out 160 logon scripts, and assist you in gaining a bit of sanity. We don't have any scripts for you that have an explicit sanity output yet, but give us time.

Ed Wilson and Craig Liebendorfer, Scripting Guys