404 Tech Support

Scripting printer installation through a dynamic shutdown script

Many would think that using scripts to install printers might be a thing of the past thanks to Group Policy Preferences and other methods of deploying the beasts. However, the versatility of using a script and not having to establish a print server is still quite useful in many environments. Part of that project I mentioned previously had me querying printers throughout the organization. The next part of the project was to create a script that would be able to install the replacement printers as hands-off as possible.

To make the script flexible and simple, I wanted the printer installation to be based on Active Directory group membership. No printer would be installed on all computers and all computers could get a variety of printers installed. Therefore, I needed the script to determine the local computer’s machine name and its group membership in Active Directory. It then goes through each group to see if it is one of our printer groups. If it is, the script creates the printer port, installs the correct driver, and then creates the printer. I didn’t want the printer install taking place every time, so I used a check against the Registry to see if the port still exists. Checking this would allow the user to safely rename the printer as they prefer but does have some quirks as Windows XP leaves the printer port if the printer is manually deleted while Windows 7 removes it. If the user deletes the printer and doesn’t want it reinstalled, they just need to request their computer to be removed from the group.

The script was found to be most reliable running as a shutdown script, assigned to all computers through Group Policy.

To extend the functionality of the script to work for more printers, it is simply a matter of copy+pasting the Case statements below and setting the correct variables.

I investigated other methods of installing the printer like the Rundll32 printui.dll,PrintUIEntry method and utilizing the included printer management scripts that come with Windows. Ultimately, I found the WMI method to be the most consistent without needing elevation or other hurdles to be jumped. You can see my implementation of the script I’m using below.

'Jason Hamilton
 '404 Tech Support http://www.404techsupport.com/
 'Printer script that checks AD group membership and then installs the appropriate printers, including drivers and IP ports
 'Components of this script borrowed from many places across the web.
 Dim objRootDSE, strDNSDomain, objTrans, strNetBIOSDomain, wshShell
 Dim objFSO, strFile, objFile, objUser
 Dim strComputer, strComputerDN, strComputerName
Const ForReading = 1
 ' Constants for the NameTranslate object.
 Const ADS_NAME_INITTYPE_GC = 3
 Const ADS_NAME_TYPE_NT4 = 3
 Const ADS_NAME_TYPE_1779 = 1
' Determine DNS name of domain from RootDSE.
 Set objRootDSE = GetObject("LDAP://RootDSE")
 strDNSDomain = objRootDSE.Get("defaultNamingContext")
' Use the NameTranslate object to find the NetBIOS domain name from the
 ' DNS domain name.
 Set objTrans = CreateObject("NameTranslate")
 objTrans.Init ADS_NAME_INITTYPE_GC, ""
 objTrans.Set ADS_NAME_TYPE_1779, strDNSDomain
 strNetBIOSDomain = objTrans.Get(ADS_NAME_TYPE_NT4)
 ' Remove trailing backslash.
 strNetBIOSDomain = Left(strNetBIOSDomain, Len(strNetBIOSDomain) - 1)
Set wshShell = WScript.CreateObject( "WScript.Shell" )
 strComputer = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" )
' Skip blank lines.
 If (strComputer <> "") Then
 ' Use Set method to specify NT format name.
 ' sAMAccountName of computer is NetBIOS name with "$" appended.
 ' Trap error if computer not found.
 On Error Resume Next
 objTrans.Set ADS_NAME_TYPE_NT4, strNetBIOSDomain & "" & strComputer & "$"
 If (Err.Number <> 0) Then
 'Wscript.Echo "Computer " & strComputer & " not found"
 On Error GoTo 0
 Else
 On Error GoTo 0
 ' Retrieve Distinguished Name.
 strComputerDN = objTrans.Get(ADS_NAME_TYPE_1779)
 'Wscript.Echo "Computer: " & strComputer & ", DN: " & strComputerDN
 End If
 End If
groupmembership(strComputerDN)
Function groupmembership(strComputerDN)
'Begin group lookup
 Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D
Dim GroupObj, ldappath, GroupName
ldappath = "LDAP://" & strComputerDN
Set objUser = GetObject(ldappath)
For Each GroupObj In objUser.Groups
 'Force upper case comparison of the group names, otherwise this is case sensitive.
 GroupName = LCase(GroupObj.Name)
 GroupName = Mid(GroupName, 4)
Dim IPaddr, DriverName, PrinterName, DriverPath32, DriverPath64, PortType, PortNumber, Regpath
Select Case GroupName
 'Check for group memberships and take needed action
 'Group name must be lowercase below in case matching
 'Groups must be global security groups
Case "print_kmc360_dept1"
IPaddr = "10.10.18.50"
 Regpath = "IP_" & IPaddr
 DriverName = "KONICA MINOLTA C360SeriesPS"
 PrinterName = "Konica Minolta c360 Dept2 Rm 225"
 DriverPath32 = "\server.fqdnshareprintersdriverskmc360PSENWin_x86KOAZ8A__.INF"
 DriverPath64 = "\server.fqdnshareprintersdriverskmc360PSENWin_x64KOAZ8A__.INF"
 '1 = RAW, 2 = LPR
 PortType = 2
 'Common: 9100 for RAW, 515 for LPR
 PortNumber = 515
Case "print_kmc360_dept2"
 IPaddr = "10.10.19.50"
 Regpath = "IP_" & IPaddr
 DriverName = "KONICA MINOLTA C360SeriesPS"
 PrinterName = "Konica Minolta c360 Dept3 Rm 301"
 DriverPath32 = "\server.fqdnshareprintersdriverskmc360PSENWin_x86KOAZ8A__.INF"
 DriverPath64 = "\server.fqdnshareprintersdriverskmc360PSENWin_x64KOAZ8A__.INF"
 '1 = RAW, 2 = LPR
 PortType = 2
 'Common: 9100 for RAW, 515 for LPR
 PortNumber = 515
 Case "print_kmc360_dept3"
 IPaddr = "10.10.20.50"
 Regpath = "IP_" & IPaddr
 DriverName = "KONICA MINOLTA C360SeriesPS"
 PrinterName = "Konica Minolta Tuxedo Series"
 DriverPath32 = "\server.fqdnshareprintersdriverskmc360PSENWin_x86KOAZ8A__.INF"
 DriverPath64 = "\server.fqdnshareprintersdriverskmc360PSENWin_x64KOAZ8A__.INF"
 '1 = RAW, 2 = LPR
 PortType = 2
 'Common: 9100 for RAW, 515 for LPR
 PortNumber = 515
 End Select
If IPaddr <> "" Then
 If RegKeyExists(Regpath) = False Then
 CreatePort IPaddr, PortType, PortNumber
 DriverPath = InstallDrivers (DriverName, DriverPath32, DriverPath64)
 CreatePrinter DriverName, IPaddr, PrinterName, DriverPath
 End If
 End If
IPaddr = ""
 Regpath = ""
 DriverName = ""
 PrinterName = ""
 DriverPath32 = ""
 DriverPath64 = ""
Next
End Function
Function RegKeyExists(Key)
 Dim oShell, entry, FullKey
 On Error Resume Next
 'Using port name instead of printer name so people can rename their printers.
 'Removing device in Win7 removes printer port. Not true in Windows XP.
 'Port location in registry:
 'HKLMSYSTEMCurrentControlSetControlPrintMonitorsStandard TCP/IP PortPortsIP_10.10.xx.xx
 'Printer name location in registry:
 'HKLMSYSTEMCurrentControlSetControlPrintPrintersKonica Minolta DeptX rm XX
FullKey = "HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlPrintMonitorsStandard TCP/IP PortPorts" & Key & ""
 Set oShell = CreateObject("WScript.Shell")
 entry = oShell.RegRead(FullKey)
 If Err.Number <> 0 Then
 Err.Clear
 RegKeyExists = False
 Else
 Err.Clear
 RegKeyExists = True
 End If
 End Function
Sub CreatePort (IPaddy, PortType, PortNumber)
 'Create printer port with portname = IP Address
 Dim strComputer
 strComputer = "."
 Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\" & strComputer & "rootcimv2")
 Set objNewPort = objWMIService.Get("Win32_TCPIPPrinterPort").SpawnInstance_
objNewPort.Name = "IP_" & IPaddy
 objNewPort.Protocol = PortType
 objNewPort.HostAddress = IPaddy
 objNewPort.PortNumber = PortNumber
 'objNewPort.SNMPEnabled = true
 objNewPort.Put_
End Sub
Function InstallDrivers (DriverName, DriverPath32, DriverPath64)
 'Determine OS architecture for drivers
 ' detect 32/64-bit systems
 Dim objFSO, objProcEnv, strSourceFolder, system_architecture, process_architecture, objShell, objWMIService, objNewPort
 Set objShell = CreateObject("WScript.Shell")
 Set objProcEnv = objShell.Environment("Process")
 process_architecture= objProcEnv("PROCESSOR_ARCHITECTURE")
 If process_architecture = "x86" then
 system_architecture = objProcEnv("PROCESSOR_ARCHITEW6432")
 If system_architecture = "" then
 system_architecture = "x86"
 End If
 Else
 system_architecture = process_architecture
 End If
If process_architecture = "x86" And system_architecture = "x86" Then
 ' Find 32-bit drivers
 'Set Driver Path
 Dim DriverPath
 DriverPath = DriverPath32
 ElseIf process_architecture = "AMD64" And system_architecture = "AMD64" Then
 ' Find 64-bit drivers
 DriverPath = DriverPath64
 End If
'Install printer driver with driver name and path determined by OS architecture
 Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\" & strComputer & "rootcimv2")
 objWMIService.Security_.Privileges.AddAsString "SeLoadDriverPrivilege", True
 Set objDriver = objWMIService.Get("Win32_PrinterDriver")
objDriver.Name = DriverName
 objDriver.Infname = DriverPath
 intResult = objDriver.AddPrinterDriver(objDriver)
InstallDrivers = DriverPath
 End Function
Sub CreatePrinter (DriverName, IPaddy, PrinterName, DriverPath)
 'Create printer with PortName and DriverName
 Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\" & strComputer & "rootcimv2")
 Set objPrinter = objWMIService.Get("Win32_Printer").SpawnInstance_
IPaddy = "IP_" & IPaddy
objPrinter.DriverName = DriverName
 objPrinter.PortName = IPaddy
 objPrinter.DeviceID = PrinterName
 objPrinter.Location = PrinterName
 objPrinter.Network = True
 objPrinter.Put_
End Sub
Wscript.quit

I am certainly open to suggestions. If you spot something that could be improved in a way, feel free to let me know.