Printing is a function of network infrastructure so basic to daily business operations that users just assume it is going to work the first time/every time. Meanwhile, IT pros have the challenge of making it so.
Although there has been no real change in the way users experience printing, its quality and speed have improved from the earliest days of the PC, when each application had its own set of printer drivers. The evolution of Windows has simplified printing significantly, although the printer server architecture in Windows Server 2019 is little changed from Windows Server 2012.
In Windows, the term printer refers to what is effectively a printer queue. Windows calls the physical printer a print device. When the application prints a document, it sends a print job to a print spooler. This spooler holds the individual print jobs, which may arrive faster than the print device can handle at any given time. The spooler then sends each print job to the printing device via a printer port, which can be either a physical port (such as LPT1:) or a network address.
With Windows Server 2019, the Print Services feature has three components.
This chapter covers only the Print Server feature. Neither the LPD service nor the Internet Printing feature in Windows Server is used widely in today's networks.
In this chapter, you explore the following tasks:
This chapter uses two hosts.
Reskit.Org
domain that you created in Chapter 3, “Managing Active Directory.”Reskit.Org
domain. In this chapter, you use this host and set it up as a print server.This chapter also uses two networked printers, which you install using the chapter's scripts.
Figure 7.1 shows the systems (and printers) in use in this chapter.
Note that all systems used in this chapter need PowerShell 7 loaded before starting. You can do that manually, using the scripts from Chapter 1, or using the GitHub Gist at bit.ly/Pwsh-install-1 (or use the full URL shown in the introduction). Optionally, you can configure VS Code with the GitHub Gist at bit.ly/Pwsh-install-2 (or use the full URL shown in the introduction).
Figure 7.1: Systems used in this chapter
When you install Windows Server, the installation process sets up several printers on each system by default. These enable you to “print” to PDF or to Microsoft's XPS format. Adding applications can also add additional printers. For example, installing the Foxit PDF reader adds the entry
Foxit Reader PDF Printer
. Microsoft's Office product also installs a
OneNote
printer. These additional “printers” tend to be client‐side and print to the respective document types. If you want to print to paper, you need to install, configure, and deploy further physical printers.
Deploying a new print device involves several steps.
This section uses the
PSRV
host, a Windows 2019 server (installed with the full Desktop Experience), with no other features loaded. You should have installed PowerShell 7 on this server. This section makes use of Xerox printer drivers that you can download from the Internet. For the purposes of this chapter, you don't need an actual Xerox print device, as all you need are the driver files (although without the print device you are not going to see any physical output). If you have a different manufacturer's printer on your network, you may be able to modify the scripts to reflect your manufacturer and the driver URL(s) along with changes necessary to reflect how the drivers are packaged by the manufacturer.
Some printer manufacturers create installation programs that do the printer installation via a GUI. In that case, you could run the installation program on the printer server (for example, via RDP). Depending on the manufacturer and how it created the installation files, you may be able to install the drivers in a test machine and then extract the printer drivers and install them as shown here.
You use the
Install‐WindowsFeature
command to install the Print Server feature and the associated management tools.
# 1. Install the Print-Server feature on PSRV plus tools
Import-Module -Name ServerManager -WarningAction SilentlyContinue
Install-WindowsFeature -Name Print-Server -IncludeManagementTools
You can see the output of this code in Figure 7.2.
Figure 7.2: Installing the Print Server feature
In preparation for installing a new printer, you need to create a temporary folder into which you can download the drivers. In this example, they are Xerox drivers. If you install printers from a different manufacturer, be sure to use an appropriate folder name.
# 2. Creating a folder for the Xerox printer drivers
$NIHT = @{
Path = 'C:\Xerox'
ItemType = 'Directory'
Force = $true
ErrorAction = 'Silentlycontinue'
}
New-Item @NIHT | Out-Null
Different printing vendors provide their drives in a variety of channels. Xerox, for example, provides the drivers in a ZIP file that you can download using the Background Intelligent Transfer Service (BITS).
# 3. Downloading printer drivers for Xerox printers
$URL = '
http://bit.ly/XDrivers
'
$Target='C:\Xerox\XDrivers.zip'
Start-BitsTransfer -Source $URL -Destination $Target
In many cases, you may use a web browser to navigate to your printer manufacturer's web site and search for the drivers you need.
In this example, you use a compressed URL to save space. The full URL for the driver package is download.support.xerox.com/pub/drivers/6510/drivers/win10x64/ar/6510_5.617.7.0_PCL6_x64.zip.
In the previous step, you downloaded a ZIP file. In this step, you extract the Xerox drivers to
C:\Xerox\Drivers
, as follows:
# 4. Expand the zip file
$Drivers = 'C:\Xerox\Drivers'
Expand-Archive -Path $Target -DestinationPath $Drivers
Once you have expanded the driver package, you install the drivers it contains. This specific driver package contains drivers for two Xerox printer models (Phaser 6510 and Phaser 6515) using
printui.dll
.
# 5. Installing the drivers
$M1 = 'Xerox Phaser 6510 PCL6'
$P = 'C:\Xerox\Drivers\6510_5.617.7.0_PCL6_x64_Driver.inf\x3NSURX.inf'
rundll32.exe printui.dll,PrintUIEntry /ia /m "$M1" /f "$P"
$M2 = 'Xerox WorkCentre 6515 PCL6'
rundll32.exe printui.dll,PrintUIEntry /ia /m "$M2" /f "$P"
The Print Management module does not provide a command to add printer drivers. You can, however, use the functionality in
printui.dll
to add the printers. This DLL is part of the Print Manager GUI. This GUI's underlying tool,
printmanagement.msc
, uses the DLL to perform printer‐related administration. You use
rundll32.exe
to invoke the DLL, in this case to add drivers to the Windows driver store.
In some cases, you may find that these commands produce an error, calling
rundll32.exe
. If so, just rerun the command to resolve the issue.
The Xerox drivers you downloaded were contained in a ZIP file. Some printer drivers are built into Windows and do not require downloading. Other driver downloads may use other compression formats. Additionally, some drivers are delivered inside an installation program you run on the print server to install the drivers.
In this example, the printer you are going to add is a networked printer. To enable the print spooler on
PSRV
to send data to the printer, you first define a new printer port.
# 6. Adding a new printer port
$PPHT = @{
Name = 'SalesPP'
PrinterHostAddress = '10.10.10.61'
}
Add-PrinterPort @PPHT
In this case, you use the IP address 10.10.10.61. This is a hard‐coded address you assign for the networked printing device. If the printer gets its IP address details via DHCP, ensure you create a reservation. This simplifies adding a new printer on the print server.
With the drivers installed and a printer port created, you can use the
Add‐Printer
command to add the printer to
PSRV
.
# 7. Adding a new printer
$PRHT = @{
Name = 'SalesPrinter1'
DriverName = $M1
PortName = 'SalesPP'
}
Add-Printer @PRHT
Once this has completed, you should be able to print to the printer from
PSRV
.
The steps thus far allow printing only from the print server. To enable users to access the printer remotely, you need to share it.
# 8. Sharing the printer
Set-Printer -Name SalesPrinter1 -Shared $True
Once you share a printer, users can begin to use it to print their documents. You can review the printer setup either using
Net View \\PSRV
(from an elevated PowerShell session) or using the printer commands.
The printer installation and configuration steps you have performed in this section produce no output. Once you have finished the installation of your new Xerox printer, you can use the relevant
Get
commands to review the printer port, printer driver, and printer settings, like this:
# 9. Review printer configuration
Get-PrinterPort -Name SalesPP |
Format-Table -Autosize -Property Name, Description,
PrinterHostAddress, PortNumber
Get-PrinterDriver -Name Xerox* |
Format-Table -Property Name, Manufacturer,
DriverVersion, PrinterEnvironment
Get-Printer -ComputerName PSRV -Name SalesPrinter1 |
Format-Table -Property Name, ComputerName,
Type, PortName, Location, Shared
You can see the output of these commands in Figure 7.3.
Figure 7.3: Reviewing the setup of the new printer
In this section, you set up a new print server, created a printer port, and added/shared a new printer. Your printer is now available, and your users should be able to print to it.
Once you have a printer created and installed, you can also publish it to Active Directory. This helps your users find the printer. You can also specify a physical location for the printer to assist your users in finding the actual print device.
Once you publish the printer, your users can then search for published printers based on location, as well as on capabilities (such as color printers). In larger organizations this can be useful to enable users to find and use printers.
This example uses two hosts:
PSRV
and
DC1
.
DC1
is a domain controller in the
Reskit.Org
domain.
PSRV
is a Windows 2019 server that you configured as a print server in “Installing and Sharing Printers.”
To publish a printer in AD, you first must create a printer object for the printer.
# 1. Get the printer object
Import-Module -Name PrintManagement -WarningAction SilentlyContinue
$Printer = Get-Printer -Name SalesPrinter1
PowerShell 7 does not natively support the Print Management module, but the commands in the module work via the Windows PowerShell compatibility mechanism discussed in Chapter 2, “PowerShell 7 Compatibility with Windows PowerShell.” By setting the
‐WarningAction
parameter to
SilentlyContinue
, you avoid the warning message (warning that you are importing the module via the PowerShell compatibility mechanism).
By default, printers are not published in AD. You can check the initial publication status by examining the printer object you just created.
# 2. Checking the initial publication status
$Printer | Format-Table -Property Name, Published
You can see, in the output in Figure 7.4, that the
SalesPrinter1
printer is not currently shared in AD.
Figure 7.4: Checking on the
SalesPrinter1
printer
Publishing a printer is straightforward. Before you publish the printer, you should use the
Set‐Printer
command to add a location to the printer information both held by
PSRV
and in AD, using the
Set‐Printer
command, like this:
# 3. Publish and share the printer to AD
$Printer | Set-Printer -Location '10th floor 10E4'
$Printer | Set-Printer -Published $true
This code configures your printer with a Location value and then publishes it in AD.
Having set the printer details, you can use
Get‐Printer
to observe the updated configuration.
# 4. View the updated publication status
Get-Printer -Name SalesPrinter1 |
Format-Table -Property Name, Location, DriverName, Published
You can use the
Get‐Printer
command to view the key settings for your new printer, as shown in Figure 7.5.
Figure 7.5: Reviewing the setup of the new printer
As you can see, the Sales printer,
SalesPrinter1
, is now published to the AD.
The Windows print spooler receives print jobs from printer users and sends the jobs to the print device. The print jobs that pass through the print server are stored temporarily in a spool folder on the print server. By default, Windows uses the folder
$Env:SystemRoot\system32\spool\PRINTERS
.
If you have a large print server handling a large number of printers, the potential size of this folder could become quite large. In turn, this could result in the system drive becoming full or full enough to affect the operation of your print server.
To avoid this issue, and as a best practice, you should move the spool file to another folder. This will allow you to keep your eye on the folder and possibly leverage Filesystem Resource Manager (FSRM) to generate reports on this folder. For a large print server, you might consider adding a disk to the server to hold the temporary spool files and updating the spool folder configuration to point to the new folder.
There are no PowerShell commands to enable you to change the spool folder. The .NET Framework includes the
System.Printing.PrintServer
class, which you use to set the spool folder location. By default, PowerShell does not load the
System.Printing
.NET namespace, which is necessary before you can change the spooler folder. Thus, you need to manually load the namespace and then use the classes to update the spool folder.
You can also configure the spool folder via the registry. Both methods work. The second method, via the registry, is probably a little faster than the first approach, but the difference is probably negligible in practice.
This section uses the printer server
PSRV
, which you created in “Installing and Sharing Printers.”
As mentioned, you need to add the
System.Printing
namespace explicitly to load the classes that this namespace contains into the current PowerShell session; you do that using the
Add‐Type
command.
# 1. Loading the System.Printing namespace and classes
Add-Type -AssemblyName System.Printing
Until you add this assembly, part of .NET Core, you cannot access the classes needed. If this assembly and the classes it contains are something you need to use on a regular basis, consider loading it as part of your PowerShell profile.
Assuming you have not yet changed the configuration of the print server, you can use
New‐Object
to observe the current, and default, spool folder.
# 2. Displaying the initial spool folder
New-Object -TypeName System.Printing.PrintServer |
Format-Table -Property Name, DefaultSpoolDirectory
The output from this code, shown in Figure 7.6, shows the default spool folder on the
PSRV
print server.
Figure 7.6: Reviewing existing spool folder
As you can see, the default spool folder is
C:\Windows\system32\spool\PRINTERS
.
To administrate the printer, you have to create a print server object using administrative permissions. To do that, you first create an object that states the required permissions.
# 3. Define the required permissions—that is, the ability to
# administrate the server
$Permissions =
[System.Printing.PrintSystemDesiredAccess]::AdministrateServer
Next, you create a print server object with administrative permissions.
# 4. Create a PrintServer object with the required permissions
$NOHT = @{
TypeName = 'System.Printing.PrintServer'
ArgumentList = $Permissions
}
$PS = New-Object @NOHT # Print Server object (as admin)
To illustrate the effect of changing the spool folder, you can use
New‐Item
to create a new folder.
# 5. Create a new spool folder
$SP = 'C:\SpoolPath'
$NIHT = @{
Path = $SP
ItemType = 'Directory'
Force = $true
ErrorAction = 'SilentlyContinue'
}
New-Item @NIHT | Out-Null
You can now change the default spool folder by updating the appropriate property on the
$PS
object.
# 6. Changing the spool folder path
$PS.DefaultSpoolDirectory = $SP
The updated
$PS
object next needs to be committed to save the change.
# 7. Committing the change
$PS.Commit()
The print server object is in‐memory. To enable the Print Spooler service to use the updated settings, you need to commit the changed printer object as shown here.
Once you have updated the spool folder, use the
Restart‐Service
command to restart the print spooler service. The
PSRV
server now spools all subsequent print jobs to the new spool folder.
# 8. Restart the Spooler Service to use the new folder
Restart-Service -Name Spooler
Depending on your system, you may see some warning messages. As long as the service starts, the messages are benign.
Once you have restarted the spooler, users can now print jobs, and Windows spools the print jobs to the newly specified folder. You can view this new folder:
# 9. Reviewing the spooler folder
New-Object -TypeName System.Printing.PrintServer |
Format-Table -Property Name, DefaultSpoolDirectory
As you can see in the output, shown in Figure 7.7, your new spool folder is now in operation.
Figure 7.7: Reviewing the spool folder
Note that if there were unprinted printer jobs in the older spool folder, changing the folder name does not move those documents. You should ensure that the existing spool folder is empty before changing the spool folder.
As mentioned, there are two mechanisms for modifying the spooler folder, and you've just explored the first of them, using the
System.Printing.PrintServer
class. To demonstrate the second mechanism, using the registry, you need to create another new folder in the filesystem.
# 10. Creating a new/different spool folder
$SPL = 'C:\SpoolViaRegistry' # different spool folder
$NIHT2 = @{
Path = $SPL
Itemtype = 'Directory'
ErrorAction = 'SilentlyContinue'
}
New-Item @NIHT2 | Out-Null
Before updating the registry, use
Stop‐Service
to stop the spooler service.
# 11. Stopping the Spooler service
Stop-Service -Name Spooler
Next, you update the appropriate registry value to configure the new printer spool folder, with
Set‐ItemProperty
.
# 12. Configure the new spooler folder
$RPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Print\Printers'
$IP = @{
Path = $RPath
Name = 'DefaultSpoolDirectory'
Value = $SPL
}
Set-ItemProperty @IP
To complete the configuration of a new spool folder, you restart the spooler service.
# 13. Restarting the Spooler
Start-Service -Name Spooler
As you did previously, you can now review the updated spool directory by viewing the .NET
PrintServer
object.
# 14. Viewing the results
New-Object -TypeName System.Printing.PrintServer |
Format-Table -Property Name, DefaultSpoolDirectory
As you can see in the output, shown in Figure 7.8,
PSRV
is now using the new spooler folder (
C:\SpoolViaRegistry
).
Figure 7.8: Reviewing the spool folder after the change
After deploying a new printer or after changing printer consumables, it can be useful to print a test page. A test page shows that the printer and print server are working and demonstrates the quality of printing performed by the print device.
There is no PowerShell command support for creating a printer test page. However, Windows Management Instrumentation (WMI) provides a mechanism to print a test page.
WMI, discussed in more detail in Chapter 9, provides the IT professional with a great wealth of information via the hundreds of available classes. On a Windows system, the WMI class
WIN32_Printer
contains a WMI object for each printer in the system. This WMI object contains a method,
PrintTestPage
, that you use here to generate a test page.
This section uses the printer server
PSRV
, which you created in “Installing and Sharing Printers.”
You retrieve the printers defined on this system by using WMI (and the
Get‐CimInstance
command).
# 1. Get printer objects from WMI
$Printers = Get-CimInstance -ClassName Win32_Printer
This command returns an array of printer objects—one for each printer defined in the system. The command returns the same printers and the same number of printers you would see calling
Get‐Printer
.
PowerShell 7 (and Windows PowerShell) uses a feature known as cmdlet definition over XML, which enables PowerShell to create commands based on WMI classes, combined with a small amount of XML. This incredibly useful technology enabled Microsoft to create a large number of new commands without a huge amount of effort. These commands, defined by XML contained in CDXML files, act just like cmdlets written in C#. The commands in the Print Management module are implemented using this technology.
The developers of the Print Management module chose which underlying properties and methods to expose. Not all the WMI printer object properties are exposed in the objects produced by
Get‐Printer
. Also, there is no cmdlet to create a printer test page even though the underlying WMI class does provide a method to do that. You use that method in this section to create a test page.
You can view the number of printers available on
PSRV
.
# 2. Display the number of printers defined on PSRV
'{0} Printers defined on this system' -f $Printers.Count
The output of this code, in Figure 7.9, shows seven printers defined on
PSRV
.
Figure 7.9: Displaying the number of printers
To print a test page, you first have to obtain the WMI object representing the
SalesPrinter1
printer, by using
Where‐Object
.
# 3. Get the Sales Group printer WMI object
$Printer = $Printers |
Where-Object Name -eq 'SalesPrinter1'
To view the printer's details, you can display the
$Printer
object.
# 4. Display the printer's details
$Printer | Format-Table -AutoSize
You can view the output of this command in Figure 7.10.
Figure 7.10: Displaying the printer's details
To print the printer test page, you invoke the
PrintTestPage
method.
# 5. Printing a test page
Invoke-CimMethod -InputObject $Printer -MethodName PrintTestPage
WMI objects can contain methods, which are commands that act on the underlying WMI object (or WMI class). You invoke these methods using the
Invoke‐CimMethod
cmdlet. In this case, the printer object has a method,
PrintTestPage
, which creates a test page and sends it to the printer. Invoking the method produces some basic output, which you can see Figure 7.11 (in addition to printing the test page).
Figure 7.11: Printing a test page
In the output shown in Figure 7.11, the CIM method produces a
ReturnValue
of 0. This indicates the WMI method was successful. Of course, the actual test page output as rendered by the print device is the ultimate test. WMI might work successfully, but the printer might have issues preventing it from printing properly. To view the details of the print jobs waiting to be printed, including test pages, you can use the Print Manager GUI,
printmanagement.msc
.
In Windows, a printer pool is a single printer associated with two or more printing devices (each of which is assigned to a different printer port). This can be useful in environments that generate a lot of printed output, for example a large legal practice printing lots of long documents.
This is one of those rare cases where there are no existing commands, WMI classes, or .NET classes that enable you to create a printer pool directly. However, the
printui.dll
library (added to the
PSRV
system when you added the print server feature) contains functionality to create the printer pool. You used this DLL to add a printer driver in “Installing and Sharing Printers.”
This section uses the printer server
PSRV
, which you created in “Installing and Sharing Printers.”
To create a printer pool, you need at least two print devices attached via different printer ports. You created the first port in “Installing and Sharing Printers” and now need to create a second port.
# 1. Add a printer port for the printer
$P = 'SalesPrinter1' # printer name
$PP2 = 'SalesPP2' # new printer port name
Add-PrinterPort -Name $PP2 -PrinterHostAddress 10.10.10.62
To create the printer pool, you call
printui.dll
and tell it the printer and which ports to use.
# 2. Creating the printer pool for SalesPrinter1
$PP1='SalesPP' # first port name
rundll32.exe printui.dll,PrintUIEntry /Xs /n $P Portname $P1,$P2
This code sets up a printer pool. The printer
SalesPrinter1
is now served by two ports and two printing devices.
The
rundll32.exe
program runs the print utility DLL. In effect,
rundll
pretends to be the Printer Management GUI that uses the DLL to carry out some action.
RunDLL
allows you to pass parameters to the DLL and get the DLL to do things, such as creating a printer port.
For more details on how to use
rundll32
with
printui.dll
, see docs.microsoft.com/en-us/windows-server/administration/windows-commands/rundll32-printui.
You can view the details for this printer using
Get‐Printer
.
# 3. Viewing resultant details
Get-Printer $P |
Format-Table -Property Name, Type, DriverName,
PortName, Shared, Published
In the output of this code, shown in Figure 7.12, you can see that the
SalesPrinter1
printer now has two ports associated and is set up in a printer pool.
Figure 7.12: Viewing printer pool details
In this chapter, you have worked with Windows printing, using PowerShell commands, WMI and .NET classes, and a Windows DLL.
You saw that the Printer Management module provided you the means to perform common printer‐related maintenance such as adding a printer. The module, however, does not provide commands for all the activities you might need to carry out, and therefore you need to use .NET objects and other command‐line tools.
This chapter illustrates well that where there are no PowerShell 7 or Window PowerShell commands to perform a task, you can often find other methods of automating your environment.