Deploying printers in a Windows environment used to be tedious. It isn’t hard, especially if you are using Group Policy Preferences to make the connections. But it does involve a lot of separate consoles. In our environment, we have to reserve the IP, create the printer on a print server, create a group policy printer preference, and create an Active Directory group for Item level Targeting.
Let’s automate that entire process, including creating the Group Policy Preference, with PowerShell. First, the script – see you at the bottom!
#Description
write-host "Creates printer on printserver, creates AD group, creates Group Policy Preference, reserves IP, runs Klist/Gpupdate on computers." -ForegroundColor Cyan
#Specify Servers and Paths
$PrintServer = "PrintServe.Test.local"
$PrintServerPath = "\\PrintServe"
$DHCPServer = "DC-01"
$ParentContainer = "OU=Printer Groups,OU=Security Groups,DC=test,DC=local"
$DomainPrefix = "TEST\"
$SYSVOLPATH = "\\test.local\SYSVOL\test.local\Policies\"
# Define status variables
$adGroupStatus = "Unknown"
$dhcpReservationStatus = "Unknown"
$printerInstallationStatus = "Unknown"
$gpoPreferenceStatus = "Unknown"
$printerInstallOnComputersStatus = "Unknown"
#Printer Model Menu
function Show-Menu {
param (
[string]$Title = 'Select Printer Model'
)
Clear-Host
Write-Host "================ $Title ================"
# List of printer models
$printerModels = @(
"01: HP Color Laserjet Pro MFP M477",
"02: HP Color Laserjet Pro MFP M479",
"03: HP LaserJet M203",
"04: HP LaserJet M209",
"05: HP LaserJet M402"
)
$printerModels | ForEach-Object { Write-Host $_ }
}
# Function to get printer model and driver details
function Get-PrinterModelDetails {
param (
[string]$choice
)
$models = @{
'01' = @{ Model = "M477"; Driver = "HP Universal Printing PCL 5" }
'02' = @{ Model = "M479"; Driver = "HP Universal Printing PCL 5" }
'03' = @{ Model = "M203" ; Driver = "HP Universal Printing PCL 5" }
'04' = @{ Model = "M209" ; Driver = "HP Universal Printing PCL 5" }
'05' = @{ Model = "M402" ; Driver = "HP Universal Printing PCL 6" }
}
return $models[$choice]
}
# Function to install printer and report progress.
function InstallPrinter {
param (
[string]$PrinterIP,
[string]$PrintServer,
[string]$Driver,
[string]$PrinterName,
[string]$Location
)
# Step 1: Add Printer Port
Write-Progress -PercentComplete 0 -Status "Installing Printer" -Activity "Adding Printer Port"
if ($Null -eq (get-printerport -Name $PrinterIP -ComputerName $PrintServer)){
Add-PrinterPort -Name $PrinterIP -PrinterHostAddress $PrinterIP -ComputerName $PrintServer
}
# Step 2: Add Printer
Write-Progress -PercentComplete 33 -Status "Installing Printer" -Activity "Adding Printer"
if ($Null -eq (get-printer -Name $PrinterName -ComputerName $PrintServer -ErrorAction SilentlyContinue)){
Add-Printer -ComputerName $PrintServer -DriverName $Driver -Name $PrinterName -PortName $PrinterIP -Location $Location -shared -ShareName $PrinterName
}
# Step 3: Set Printer Properties
Write-Progress -PercentComplete 66 -Status "Installing Printer" -Activity "Setting Printer Properties"
Set-Printer -ComputerName $PrintServer -Name $PrinterName -Datatype "RAW" -PrintProcessor "winprint"
# End Progress
Write-Progress -PercentComplete 100 -Status "Installing Printer" -Completed -Activity "Printer Installed"
}
# Function to validate IP address
function Test-IP {
param (
[string]$ip
)
return $ip -match "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"
}
#Gather Printer IP and Site Information
write-host ""
# Prompt for printer IP and validate
do {
$PrinterIP = Read-Host "What is the printer IP?"
} while (-not (Test-IP $PrinterIP))
#Get Match DHCP Scope from Printer IP
#Run Get-DhcpServerv4Scope -ComputerName $DHCPServer | select Name | sort Name to see a list of DHCP scope names.
$PrinterSiteName = ($PrinterIP.Split(".")[0..1] -join ".") + ".0.0"
$PrinterSiteObj = Get-DhcpServerv4Scope -ComputerName $DHCPServer | Where-Object ScopeId -EQ $PrinterSiteName
$PrinterSite = $PrinterSiteObj.Name -replace " - (Domain|Public) Wireless$"
# Get printer model details
Show-Menu -Title 'Select Printer Model'
do {
$PrinterModelChoice = Read-Host "Enter a two digit number to select the printer model"
$printerDetails = Get-PrinterModelDetails -choice $PrinterModelChoice
} while (-not $printerDetails)
$Model = $printerDetails.Model
$Driver = $printerDetails.Driver
# Get printer location details
write-host ""
$PrinterLocation = Read-Host "What is the printer's room number or abbreviated location? EX: 07, 405, MC"
#Set Printer Name and Prompt to Continue
$PrinterName = $PrinterSite + "_" + $PrinterLocation + "_" + $Model
Write-Host ""
Write-Host "Printer name to create: "$PrinterName
Pause
Write-Host ""
# Attempt to get the AD Group
$ADGroup = Get-ADGroup -Filter { Name -eq $PrinterName } -ErrorAction SilentlyContinue
if ($ADGroup) {
Write-Host "Security group already created." -ForegroundColor Yellow
$adGroupStatus = "Previously Created"
} else {
try {
New-ADGroup -Name $PrinterName -Path $ParentContainer -GroupCategory Security -GroupScope Global -SamAccountName $PrinterName -DisplayName $PrinterName
Write-Host "Security Group has been created." -ForegroundColor Green
$ADGroup = Get-ADGroup -Filter { Name -eq $PrinterName } -ErrorAction SilentlyContinue
$adGroupStatus = "Success"
} catch {
Write-Host "Error creating the group: $_" -ForegroundColor Red
$adGroupStatus = "Failed"
}
}
#Search AD for Matching Computers. Prompt to Add to AD Group.
$ComputerSearch = $PrinterSite + '-' + $PrinterLocation + "*"
$Members = Get-ADComputer -Filter "Name -like '*$ComputerSearch'"
write-host "Do you want to add computers to the printer security group?"
Get-ADComputer -Filter "Name -like '*$ComputerSearch'" | Select-Object Name | Sort-Object Name | Out-Host
$Prompt = Read-Host "Type Yes to add the computers. Type No to search for your group and add members manually"
if ($Prompt -match "Y"){
foreach ($Member in $Members){Add-ADGroupMember -Identity $PrinterName -members $Member.ObjectGuid}
}
if ($Prompt -match "N"){
&rundll32.exe dsquery,OpenQueryWindow
pause
}
# Check for existing DHCP reservation
$ExistingReservation = Get-DhcpServerv4Reservation -ComputerName $DHCPServer -IPAddress $PrinterIP -ErrorAction SilentlyContinue
if ($ExistingReservation) {
Write-Host "Reservation for $PrinterIP already exists." -ForegroundColor Yellow
$dhcpReservationStatus = "Already Exists"
} else {
$Lease = Get-DhcpServerv4Lease -ComputerName $DHCPServer -IPAddress $PrinterIP
if ($Lease) {
try {
$Lease | Add-DhcpServerv4Reservation -ComputerName $DHCPServer -Name $PrinterName
Write-Host "$PrinterIP reserved." -ForegroundColor Green
$dhcpReservationStatus = "Success"
} catch {
Write-Host "Error creating the reservation: $_" -ForegroundColor Red
$dhcpReservationStatus = "Failed"
}
} else {
Write-Host "Error: No lease found for IP $PrinterIP" -ForegroundColor Red
$dhcpReservationStatus = "Lease Not Found"
}
}
#Get Printer Location
$Location = $PrinterSite + "/Room " + $PrinterLocation
# Install Printer Using Function
InstallPrinter -PrinterIP $PrinterIP -PrintServer $PrintServer -Driver $Driver -PrinterName $PrinterName -Location $Location
# Verify if the printer is installed
$installedPrinter = Get-Printer -ComputerName $PrintServer | Where-Object { $_.Name -eq $PrinterName }
if ($installedPrinter) {
Write-Host "Printer $PrinterName created on $PrintServer." -ForegroundColor Green
$printerInstallationStatus = "Success"
} else {
Write-Host "Failed to install printer $PrinterName." -ForegroundColor Red
$printerInstallationStatus = "Failed"
}
#Create Group Policy Preference
#Build GPO name and Find GPO GUID
$GPOName = "Printers - " + $PrinterSite
#Example for changing $GPOName
#if ($PrinterSite -in @("ADM", "BUS", "MNT")) {
# $GPOName = "Printers - Board"
#}
$GPOID = Get-GPO $GPOName | Select-Object ID
#Create GP Printer Preference by copying first preference in GPO and changing IP/Printer Name/Group Name.
$GPP_PRT_XMLPath = $SYSVOLPATH + "{" + $GPOID.ID + "}" + "\Machine\Preferences\Printers\Printers.xml"
[XML]$PRNT = (Get-Content -Path $GPP_PRT_XMLPath)
$CurrentDateTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$newguid = [System.Guid]::NewGuid().toString()
$NewEntry = $PRNT.Printers.PortPrinter[0].Clone()
$NewEntry.Name = $PrinterName
$NewEntry.Status = $PrinterIP.toString()
$NewEntry.Changed = "$CurrentDateTime"
$NewEntry.uid = "{" + "$newguid" + "}"
$NewEntry.disabled = '0'
$NewEntry.properties.ipAddress = $PrinterIP.toString()
$NewEntry.properties.localName = $PrinterName
$NewEntry.properties.path = $PrintServerPath + $PrinterName
$NewEntry.filters.Filtergroup.Name = $DomainPrefix + $PrinterName
$NewEntry.filters.Filtergroup.SID = $ADGroup.SID.toString()
$PRNT.DocumentElement.AppendChild($NewEntry)
$PRNT.Save($GPP_PRT_XMLPath)
if ($? -eq $true){
write-host "Printer Preference Created" -ForegroundColor Green
$gpoPreferenceStatus = "Success"
}
else {
$gpoPreferenceStatus = "Failed"
}
#Install Printer on Computers in Security Group by running Klist/Gpupdate
$Computers = Get-ADGroupMember -identity $PrinterName -Recursive | Where-Object ObjectClass -eq computer
Write-Host Running Klist/Gpupdate on printer security group
Write-Host ""
foreach ($Computer in $Computers){
$ComputerName = $Computer.Name
if ((Test-Connection -ComputerName $ComputerName -Quiet -Count 1) -eq $true){
&psexec.exe \\$ComputerName -accepteula -d -h -s -n 2 "c:\windows\System32\klist.exe" -li 0x3e7 purge | Out-Null
&psexec.exe \\$ComputerName -accepteula -d -h -s -n 2 "c:\windows\System32\gpupdate.exe" | out-null
Write-Host ""
}
}
# Print the status summary
Write-Host "Status Summary:" -ForegroundColor Cyan
Write-Host "AD Group Creation: $adGroupStatus"
Write-Host "DHCP Reservation: $dhcpReservationStatus"
Write-Host "Printer Installation: $printerInstallationStatus"
Write-Host "GPO Preference Creation: $gpoPreferenceStatus"
Write-Host ""
Write-Host "Script complete. Thanks for playing." -ForegroundColor Green
Customizing the Create Printers PowerShell Script for Your Environment
If you were to run this script right away, it likely won’t work (unless you also are using Test.local – and if you are, hello doppeldomainer. A bit of configuration is required. So, start by setting the variable values in lines 5-10.
Now that I have you invested, this script has a few assumptions:
- Printers are named like SITE_ROOM_MODEL.
- Second, you’ll be using Group Policy Preferences on the computer side to deploy an IP printer that is targeted to a security group.
This script is easy to modify – let’s get you a bit more invested and hope we don’t have any more assumptions.
Printer Models and Drivers
To configure your supported printer models, scroll down to line 19 (#Printer Model Menu) to see how the model selection menu appears. This is the menu you’ll see when running the script. These numbers correspond to actual model names and drivers that can be found below line 38 (# Function to get printer model and driver details)
IP Addresses and Site Information
To build the site information, look at lines 93-105 (#Gather Printer IP and Site Information. This section will prompt you for an IP address, look up the matching DHCP scope, and format the DHCP scope name (by removing certain words).
If your DHCP scopes are not named this way, well, mark that down as another assumption and rename your scopes. Or create add a few If statements to match IP prefixes to sites.
I like having a universal naming convention for my devices. Because a printer in a room1 and computers in a room share the same prefix, we can add computers to a targeted security group easily. This is done in lines 145 – 160 (#Search AD for Matching Computers. Prompt to Add to AD Group). For times where the name doesn’t match or additional computers need to be added, the GUI for dsquery can be called.
Creating a Group Policy Preference with PowerShell
The magic of this script is really in lines 201 – 241 (#Create Group Policy Preference). First, we find the GPO to stick the printer preference in. In my environment2, each site has a GPO for printer preferences. You might put all site settings in a single GPO per site or even put all printer preferences in one giant GPO. Change the $GPOName variable to fit your environment. You’ll see a few commented lines showing how multiple sites can share a GPO.
Open the Group Policy Management Console, edit the GPO(s) containing your printers, sort by the Order column, and copy item 1 (assuming that item 1 is a normal printer preference that creates a printer for you).
Right click and paste to duplicate your first preference. Select item 1 and disable it (stop button or right click – All tasks – Disable). Rename the preference so that it isn’t accidentally deleted later. To create a preference with PowerShell, we need a printer preference to clone from.
Printer preferences exist in an XML file within the GPO’s folder in SYSVOL. We build that path on line 214, Open the XML on line 215 and clone item 1 on line 219. From there, we enable the preference and set the correct IP/name/security group for the new preference. Finally, we save our changes in line 232.
If you deploy printers differently, this is the section to change. Open the XML file of your existing printers GPO and compare it to these values. For example, user side printer preferences have a different path (User\Preference\Printers\).
GPUpdate and Goodbye
The final part of this script, other than the summary, starts on line 243 (#Install Printer on Computers in Security Group by running Klist/Gpupdate). Infrequent readers of this blog will note that this is just a rehash of a prior post – no need to rehash the details here when this link will double my page view.
The end result should be a script that prompts you for an IP, model, and room. From there, everything but the unboxing is automated. Now go forth. Enjoy the script! And let me know if you have any issues or improvements to this process.
2023-11-15: Added a line to catch the group SID after group has been created.