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.