Monday, March 24, 2008

Better Drive Mapping

As a network administrator, we often use vbscripts to map network shares.  Our end users are constantly wanting new shares with specific drive letters in many different locations.  So as administrators, it is our job to script a drive mapping solution for those users.

In this article, I'm going to talk about drive mapping for end users.  First, we have to specify a drive letter (i.e. T: ) when mapping.  This is typically supplied by the user(s) requesting the drive mapping.   Next we need the server and share (i.e. \\myserver\sharename).  Finally, we write the code to map the share to that drive letter and pop it into their script.  We also use other logic to determine who maps what drive, but we'll save those techniques for another article.

First, let's look at the basics of drive mapping.  In order to map drives, we use the Wscript.Network object.  From here, we can access all kinds of things like enumerating all the current network drive mappings, disconnecting drives, and mapping drives.  There are several other methods available using the link above.

Set WshNet = WScript.CreateObject("WScript.Network")

Now that we have our object, we can disconnect drives using the RemoveNetworkDrive method and we can map drives using the MapNetworkDrive method:

WshNet.MapNetworkDrive "X:", "\\myserver\sharename" 'Connect drive
WshNet.RemoveNetworkDrive "X:", "\\myserver\sharename" 'Disconnect drive

Easy huh?  But what if a user already has a different share mapped to drive X:?

As administrators, we have the power to force users to map certain shares to certain drive mappings.  Often times, we enforce this by disconnecting anything on the requested drive mapping before we map it.  This ensures we have the correct mapping on the drive letter.  This seems like overkill in my opinion.  Why waste time disconnecting and reconnecting a drive if we don't need to? 

To get around this, we can use the EnumNetworkDrives method.  This returns an array of drive letters and server shares.  We can then iterate through the list and compare to see if they exist.   Let's add this capability and create a nice function we can reuse:

 

Function MapDrive(strDrive,strPath)
'Input: strDrive = Drive letter - ex. "x:"
'Input: strPath = Path to server/share - ex. "\\server\share"
'Output: bln = True or False
 
    Err.clear
    MapDrive = False
    
    Set WshNet = WScript.CreateObject("WScript.Network")
    
    'Step 1: Get the current drives
    Set oDrives = WshNet.EnumNetworkDrives
    If Err.Number <> 0 Then
        'Code here for error logging
        Err.Clear
        MapDrive = False
        Exit Function 
    End If
    
    'Step 2: Compare drive letters to the one requested
    blnFound = False
    For i = 0 To oDrives.Count - 1 Step 2
        If UCase(strDrive) = UCase(oDrives.Item(i)) Then
            WScript.Echo "Drive letter " & strDrive & " mapped, checking connection"
            blnFound = True
            'Drive letter was found.  Now see if the network share on it is the same as requested
            If UCase(strPath) = UCase(oDrives.Item(i+1)) Then
                'Correct mapping on the drive
                MapDrive = True
            Else
                'Wrong mapping on drive.  Disconnect and remap
                WshNet.RemoveNetworkDrive strDrive, true, True 'Disconnect drive
                If Err.Number <> 0 Then
                    'Code here for error logging
                    Err.clear
                    MapDrive = False
                    Exit Function 
                End If
                
                WshNet.MapNetworkDrive strDrive, strPath 'Connect drive
                If Err.Number <> 0 Then
                    'Code here for error logging
                    Err.clear
                    MapDrive = False
                    Exit Function 
                End If
                
                MapDrive = True
                
            End If
        End If
        
    Next'Drive in the list
    
    'Ok.  If blnFound is still false, the drive letter isn't being used.  So let's map it.
    If Not blnFound Then
        WshNet.MapNetworkDrive strDrive, strPath
        If Err.Number <> 0 Then
            'Code here for error logging
            Err.clear
            MapDrive = False
            Exit Function 
        End If
        
        MapDrive = True
    End If
    
    Set WshNet = Nothing
    Set oDrives = Nothing
 
End Function

 

Looks long huh?  In summary, the function enumerates all the network drive mappings first.  Then it looks through the list for the requested drive letter to see if it's in use.  If so, it checks the share mapping on that drive letter.  If it's not correct, it disconnects and connects it proper.  If the drive letter is not in use, it then maps the share to the requested drive letter.

To use:

MapDrive("X:",\\myserver\sharename)

And because it is a function that returns True or False, we can use the results as well:

If MapDrive("X:","\\myserver\sharename") Then
    Wscript.Echo "Drive mapped successfully"
Else
    Wscript.Echo "Drive not mapped successfully"
End If 

There you have it.  A better drive mapping solution.  It's not perfect by no means, but it's better than just disconnecting wildly.  I plan on adding more logic to search for shares mapped on other letters to ensure we don't get double mappings and to migrate people to the correct ones in times were we need to change the drive letter (and they will, end users are fickle).

 

Till next time!

-Corey

Have a burning VBScript question to be answered?  Send it in and you may just be featured in our next article... and get your question answered.  :)

18 comments:

Anonymous said...

So how would you handle a network share name with a space in the share name? EG: \\myserver\my share

Cem Tezer said...

Thank you for usefull and well documented article.

I can map network drive for backup, but when its job is done, I want to remove it.

Is there a way of doing this ? Not after a time of period, I want to watch network traffic over that mounted drive and when I catch inactivty for 2 minutes, I want to remove it.

Corey Thomas said...

Simple. You just need 3 lines of code from the function:

Set WshNet = WScript.CreateObject("WScript.Network")
strDrive = "C:" '==> Example
WshNet.RemoveNetworkDrive strDrive, true, True

Cem Tezer said...

Thanks again for exp.

That helps, but I want to unmap it after time of period inactivity.

This 3 lines of codes helps much, on the other hand if I can do it automaticly that will help much.

Best greetings from Turkey.

Unknown said...

I have no idea where I am missing this piece - but where in the script do you specify the drive letter and path to be mapped? I am planning a server migration and the DNS at our company is not the best, so I want to change drive mapping to use a new IP address, but I cannot get this script to work.
I have entered

strDrive - "G:"
strPath = "\\server\directory"
at the start of this script and I get no error messages, no script boxes, nothing. Clearly this is my failing, but I am strugging to see where.

Thanks

Anonymous said...

What would be the best method to provide a check to make sure the share exists to avoid mapping to a resource that might be unavailable for whatever reason?

buy xanax said...

Cool stuff here!

viagra generic said...

Wow, nice post,there are many person searching about that now they will find enough resources by your post.Thank you for sharing to us.Please one more post about that..

cialis tadalafil said...

Wow, nice post,there are many person searching about that now they will find enough resources by your post

viagra sale said...

Great post. I think one of the basic things that we should know know is that we must always make sure that you are safe in every transactions you wanted to indulge with.

Anonymous said...

Something to consider is "remembered connections". I am managing a common logon script for 40,000 users, and just ran into a problem with this. your code is not enumerating remembered connections, neither is mine at this point, and this is causing the script to abort. I am working through resolving this now. Drop me a mail, and I will share the solution with you, once I have found it :)

Rick Childers
rick@geeksforge.com

Anonymous said...

I tried this on a Windows 7 PC and something strange is happening. If the drive exists already, and I am just changing the path to a new path, it works and points to the new path, but the path displayed on the drive in "My Computer" is still the old path. Hopefully after a restart it will update, I haven't tried that yet.

Corey Thomas said...

Regarding remembered connections, this script was created from a systems administrator perspective where drive mappings are required on a group basis.

However, I do see how it's possible that on a much larger basis you might allow an end user to use that drive letter for another purpose. If that's the case, I would instead add an option to force drive mappings or not. If true, the subroutine would map over any existing connections. If false, it would check for persistence.

If any of you would like to contribute to making this subroutine better by adding that, please let me know and I will be happy to post with credit to you.

-Corey

Anonymous said...

How would you modify this script to be network aware? What I mean by that is have it map one set of drives when it is connected to one network, but none (or map different servers) when connected to a different network?

Corey Thomas said...

re: "How would you modify this script to be network aware? What I mean by that is have it map one set of drives when it is connected to one network, but none (or map different servers) when connected to a different network?"

This is something that should be handled outside of this function. Keep in mind that the whole point of a function is to handle one specific task. This keeps your code modular which makes it easier to reuse in other scripts.

To do what you are suggesting, it would depend on how you are detecting network differences. If we are talking about IP segments, I would pull the IP information from Win32_NetworkAdapterConfiguration and compare it to a predefined list. If you are talking about wired vs wireless, you can also determine that via WMI too.

Once you determined the connection, I would call the function with the parameters you want for that specific network.

Corey Thomas said...

For those of you having errors, be sure you turn off "on error resume next" in your code so you can catch errors. Also, if you need help, be sure to include your contact info in the post (instead of posting as anonymous), or send me a message directly. I'd be glad to help.

Unknown said...

Hi,

Is there a way to map a couple of drive letters from a local machine to the network using windows authentication? Let me know.
Alex

Corey Thomas said...

Sanchea12,

You certainly can. The function above was designed for a logon script and as such, uses the currently logged on user credentials. The real magic of the script is the MapNetworkDrive method, and it just so happens to give you the ability to pass a username and password to use.

You could add two new parameters to the function and use those when calling MapNetworkDrive.

For example:
WshNet.MapNetworkDrive strDrive, strPath, true, strUsername, strPassword 'Connect drive

In this example, you are mapping a drive (ex: T:), a path to the share, a Boolean value for persistence (whether or not the drive mapping returns on logon), the user ID (domain\user), and the user's password.

This will use the supplied credentials.

One word of warning: If you are planning on doing this, remember that any password saved in a script is a potential security problem. Even if you "encrypt" a script, it is not truly encrypted and anyone can easily "decrypt" it to see your password.