Local to Mapped Printer Migration

May 18, 2008


Sorry I haven't been posting regularly, I've been really really busy at work
lately and the little time I've had off I didn't want to do ANYTHING
sysadmin related. Hopefully things will be a bit slower now and I can post
some of the stuff I've been saving up. On to the topic...

I love terminal servers. Unfortunately they can be a bit fragile at times,
especially under higher loads. One load-inducing problem that can occur is the existence of local printer queues on the server itself. Locally mapping a user's printer to the terminal server may seem like a good idea, but typically it isn't the best way to do things. Local printers cause a lot of I/O traffic on your local disks, the drivers take up memory (some drivers will load a nice hunk of memory for each logged in user), and the spooler service will also take up resources that will affect other users. Throw in memory leaks that some printer drivers may have and you'll end up with a pretty good issue as your users pile into the server.

On my network I ran into this exact issue with one of my oldest stand-alone terminal servers. I'm running Windows Server 2003, older but still decent hardware, and about 50-60 heavy users all getting a full desktop. Since I didn't know the issues with local printers when I first deployed the server, I ended up with over 30 local printers, most of which were mapped to printers over slower WAN links. Users started complaining about the server being slow. Investigating, I found a memory leak in the spooler service (restarting it helps a little), and two printer drivers taking up 5 megs each in every user session. (That's about 500 megs of ram wasted when I've got 50 users on the server). At this point I decided to do something about it.

There are two options at this point. I could either manually move each user to a mapped printer on another server (which involved contacting each user, taking over their session, and migrating them), or I could script the move (and the users won't know the difference). I chose the scripting method, especially since I don't like to track down and interrupt the users when it can be avoided.

First things first, set up each printer queue on a print server. This is necessary in either option. Hopefully you have a standard naming convention, because scripting is a lot easier if you don't have to change the printer names. (I'll show you a way to handle printer renames later in this post, but it gets messy if there's more than just a few exceptions).

Next, write up your script. Here's what I've done:

'Find the default locally mapped printer, move it to a print server mapping.
Dim ws, dflt, nCount, to_server
Dim WshNet, WshShell
Dim objNetwork
Set objNetwork = CreateObject("WScript.Network")

' The server where the new print queues are hosted.
to_server = "\\printserver01"

' Find the default printer from the registry, strip out unneeded text.
Set ws = WScript.CreateObject("WScript.Shell")
dflt = ws.RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows\Device")
nCount = instr(dflt,",") -1
dflt = left(dflt,nCount)

' Create the printer mapping for the client.
On error Resume Next
objNetwork.AddWindowsPrinterConnection to_server+"\"+dflt

' Set the created printer mapping as the default.
On error Resume Next
objNetwork.SetDefaultPrinter to_server+"\"+dflt

To map to a renamed printer queue, add this to the script, right before the part of the script where the printer mapping occurs. If the printer name matches what you specify, it will rename it to the new, renamed print queue.

' Specific mapping for Joe Blow's printer. Change dflt to the printer you want to match.
if dflt="JoeBlow_HP4000" then
' Change dflt to the renamed print queue.
dflt = "Site2_HP4100-JoeBlow"
to_server = "\\printserver02"
End if

Also, if you have a client that uses more than one printer, use the below script to determine this, map them to all their printers, and then set their default printer. The script will try to compare their mapped printer with any one of the printers they need to activate this section of script. This script does an Exit Function, which assumes that this migration piece is part of a function.

' Specific mappings for store 3's printers. This will see if dflt is the name of either of this store's printers
if dflt="str3_HP4250-1" or dflt="str3_HP4250-2" then
' Map both printer that the store employee's use.
objNetwork.AddWindowsPrinterConnection "\\printserver01\str3_HP4250-1"
objNetwork.AddWindowsPrinterConnection "\\printerserver01\str3_HP4250-2"
' Set the default printer to the default the user previously had.
objNetwork.SetDefaultPrinter to_server+"\"+dflt
Exit Function
End if

Personally, I incorporated this code as part of my login script, which runs each time a user logs in. After a few days, all of my users should have their printers mapped to the print server and I'll delete the local print queues and remove the printer drivers.