Migrating printers while merging and removing queues – client side

Scenario:

You aquire a new network, and the naming schemes (yes, plural) are… not consistant. Also, the servers are running on Windows 2000/2003 still. So, you upgrade the servers to 2012 R2 and get their naming standardized. But what about those printers? To make matters worse, several departments cross-share printer usage in case of outages, and some managers have a printer or two dedicated just to themselves… as do some of the regular staff. You have been authorized to consolidate some of these, and just remove others. But with 500+ users on this network, you need to script it (or hire a small army of college students, but let’s leave that for another discussion).

Solution:

Now, your first task is to identify all the printers, and their ports. Then you assign new names to these printers matching your naming scheme. I like to put it all in an Excel spreadsheet, so I can see it and line things up easier. Once this is done, actually create and test each of the printers on the new server.

Once this is done, you need to point the users to these new queues. Start by exporting the columns for old and new printer names to two text files (I used “oldprinters.txt” and “newprinters.txt”), as shown below:

printer-1a
printer A
prn-2nd-from-left
prt by Jane
Mikes-printer
Bob
Timmy-queue
prn-maint-bw
prn-acct-bw
prn-acct-bw
REMOVE
prn-sales-bw
prn-sales-clr
prn-mgmt-clr
prn-dev-bw
prn-maint-bw

*Make sure that the columns line up, so that each line in the new column matches up with the column you want to change from in the old column!

Now, every script out there that I can find won’t handle changing print queue names, or removing/consolidating certain printers. We need to just delete the old printers from the users, and we don’t want to add multiple copies of the same printer. So I came up with this solution.


' --Initialize script variables--
strComputer    = "."
DefaultPrinter = 4
ReadOnlyFile   = 1
NumPosition    = 0
AddThisPrinter = "Yes"
'Change these four variables to match your environment
strOldServer   = "\\Print-Srv\"
strNewServer   = "\\SiteA-FPS-W12\"
strOldPrinters = "oldprinters.txt"
strNewPrinters = "newprinters.txt"

' --Initialize script objects--
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set objFSO = CreateObject("Scripting.FileSystemObject")

' Wait until the user is really logged in...
strUserName = ""
While strUserName = ""
   WScript.Sleep 100 ' 1/10 th of a second
   Set objNetwork = WScript.CreateObject("WScript.Network")
   strUserName = objNetwork.UserName
Wend

' --Generate a list of printers that this user has installed--
Set colInstalledPrinters = objWMIService.ExecQuery _
    ("Select * From Win32_Printer Where Network = True")

' --Generate a list of printer names from the old server--
Set objOldPrintersFile = objFSO.OpenTextFile(strOldPrinters, ReadOnlyFile)
strContents = objOldPrintersFile.ReadAll
colOldPrinterList = Split(strContents,vbNewLine)

' --Generate a list of printer names from the new server--
Set objNewPrintersFile = objFSO.OpenTextFile(strNewPrinters, ReadOnlyFile)
strContents = objNewPrintersFile.ReadAll
colNewPrinterList = Split(strContents,vbNewLine)

' --Iterate through the list of printers that this user has installed--
For Each objPrinter in colInstalledPrinters
'   --Since For Each doesn't track index, keep track of it yourself--
    NumPosition = 0
'   --Iterate through all printer names located on old server--
    For Each strOldPrinter in colOldPrinterList
'       --We fould a match, so update it--
        strOldPrinterID = strOldServer & strOldPrinter
        If (objPrinter.DeviceID = strOldPrinterID) Then
            Set objNetwork = CreateObject("WScript.Network")
'           --Use that index we made to assign the new printer to a variable--
            strNewPrinterUNC = strNewServer & colNewPrinterList(NumPosition)
'           --Test to see if the new printer has already been set up (since we are many-to-one)--
'           --Also skip to delete if new printer is marked REMOVE--
            If (colNewPrinterList(NumPosition) <> "REMOVE") Then
                Set colComparePrinters = objWMIService.ExecQuery _
                    ("Select * From Win32_Printer Where Network = True")
                AddThisPrinter = "Yes"
                For Each objExistingPrinter in colComparePrinters
                    If (objExistingPrinter.DeviceID = strNewPrinterUNC) Then
                        AddThisPrinter = "No"
                        Exit For
                    End If
                Next
'           --Since printer doesn't exist, let's add it now--
                If (AddThisPrinter = "Yes") Then
                    Set objNetwork = CreateObject("WScript.Network") 
                    objNetwork.AddWindowsPrinterConnection strNewPrinterUNC
                End If
'           --We want to assign the equivalent printer as default after updating it--
                If objPrinter.Attributes And DefaultPrinter Then
                    objNetwork.SetDefaultPrinter strNewPrinterUNC
                End If
            End If
'           --Now delete the old printer, since that server is going away--
            objNetwork.RemovePrinterConnection objPrinter.DeviceID, true, true
        End If
'       --And increment our index into the new printer names table
        NumPosition = NumPosition + 1
    Next
Next

“Awesome! That worked perfectly, Jim!… Wait… It worked when I tested it as Administrator. But when I put it in the logon script, the screen goes black for like 10 minutes and it doesn’t work!”.

You’ve just run into a little “feature” of the User Account Control (UAC) feature of Windows 7+. Users need elevated privileges to add a printer driver. Now, I obviously don’t think we should make all the users admins just to be able to run a script! So, I found a write-up by someone over on MSDN who explains how to work around this, in detail.