How can you track offline printers on a printer server? Our environment has 422 network printers running from two print servers. Yesterday, I decided to sort by the Queue Status. I scrolled down to the Offline Status section and discovered 20 printers that were offline!
How can so many printers be offline without users complaining? Well, a quick glance through the list revealed several printers that are no longer here. They were replaced long ago. But what about the rest? Without more information, I have no way of telling whether a printer has been off for 1 minute or 1 month! We can fix this though – with PowerShell!
First – let’s get a list of all offline printers. Printer statuses come in a huge amount of flavors! For offline, we will only want to see printers that are “Offline” and printers that are “Error, Offline”. The “Error, Offline” status is any printer that is offline with a stuck job.
#Configuration Block #PrintServer to Connect to $PrintServer = "PrintServer.GCBE.local" #Set Location for Logging Set-Location "P:\Script Explorer\Scripts\Printers\Add Offline Comment\Joseph" #Get Current Date $Date= Get-Date #Create List of Offline Printers $OfflinePrinters = get-printer -ComputerName $PrintServer -name ADM*,BHS*,GA*,GICA*,PBX*,RAX* | Where-Object { $_.PrinterStatus -eq "Offline" -or $_.PrinterStatus -eq "Error, Offline" } | Select-Object Name,PortName,PrinterStatus | Sort-Object Name
To use the Get-Printer cmdlet, you will need the PrintManagement module. This module isn’t installed unless you are running PowerShell from a Windows 8/Server 2012 machine. You will also see that I am only interested in returning certain printer names (-name ADM*, BHS*, …). If you would like to return all printers, replace that parameter with -name * .
Now that we have our list of offline/error printers, we need to compare. This script is designed to be ran on a regular basis (as a scheduled task). Though the block below may look confusing, it will all come together in the end.
#Import Previous Offline Printers to Compare $PreviousOfflinePrinters= import-csv OfflinePrinters.CSV $OnlinePrinters = Compare-Object $PreviousOfflinePrinters.Name $OfflinePrinters.Name | where SideIndicator -EQ "<=" #Remove Offline Status for Printers Now Online ForEach-Object { set-printer -name $OnlinePrinters.InputObject -ComputerName $PrintServer -Comment "" }
This script exports a list of offline printers every time it runs. This file, OfflinePrinters.CSV, gets stored in the $PreviousOfflinePrinters variable. We then can compare snapshots of offline printers. If you run this every day, you can easily see if a printer is offline consistently. Our $OnlinePrinters variable consistent of all printers offline yesterday that are now online (where SideIndicator -EQ “<=” ). To avoid accidental deletions, we remove our offline status comment/date with the Set-Printer cmdlet.
Our final two steps are to tattoo each offline printer with a date and to export the list of offline printers for the next scheduled run.
#Add Offline Status for Printers that are Offline ForEach-Object { set-printer -name $OfflinePrinters.Name -ComputerName $PrintServer -Comment $Date } #Export List of Offline Printers $OfflinePrinters | export-csv OfflinePrinters.CSV -NoTypeInformation
By setting up this script, we can easily see how long our printers have been offline and we can automate a cleanup process. To see how to automatically delete these offline printers, jump on over to our second part.
If you have any questions at all, please let me know!
Here is the final script:
#Configuration Block #PrintServer to Connect to $PrintServer = "PrintServer.GCBE.local" #Set Location for Logging Set-Location "P:\Script Explorer\Scripts\Printers\Add Offline Comment\Joseph" #Get Current Date $Date= Get-Date #Create List of Offline Printers $OfflinePrinters = get-printer -ComputerName $PrintServer -name ADM*,BHS*,GA*,GICA*,PBX*,RAX* | Where-Object { $_.PrinterStatus -eq "Offline" -or $_.PrinterStatus -eq "Error, Offline" } | Select-Object Name,PortName,PrinterStatus,Comment | Sort-Object Name #Import Previous Offline Printers to Compare $PreviousOfflinePrinters= import-csv OfflinePrinters.CSV $OnlinePrinters = Compare-Object $PreviousOfflinePrinters.Name $OfflinePrinters.Name | where SideIndicator -EQ "<=" #Remove Offline Status for Printers Now Online if ($OnlinePrinters -ne $null){ ForEach-Object { set-printer -name $OnlinePrinters.InputObject -ComputerName $PrintServer -Comment "" }} #Add Offline Status for Printers that are Offline ForEach ($OfflinePrinter in $OfflinePrinters) { If ($OfflinePrinter.Comment -eq "" -or $OfflinePrinter.Comment -eq $null){ set-printer -name $OfflinePrinter.Name -ComputerName $PrintServer -Comment $Date} } #Export List of Offline Printers $OfflinePrinters | export-csv OfflinePrinters.CSV -NoTypeInformation
Hello, what is the meaning of this:
$OfflinePrinters = get-printer -ComputerName $PrintServer -name ADM*,BHS*,GA*,GICA*,PBX*,RAX* | Where-Object { $_.Comment -lt $Date -and $_.Comment -ne $null} | Sort-Object Name
Are the Printserver names: “ADM, BHS, GA, etc”?
I just added a line — $OfflinePrinters | export-csv C:\Temp\OfflinePrinters.CSV -NoTypeInformation
Works now. Thanks!
Thanks for the update!
Thanks for the script, but am I missing something? Where does it say to export to a CSV in your first script?
#Configuration Block
#PrintServer to Connect to
$PrintServer = “PrintServer.GCBE.local”
#Set Location for Logging
Set-Location “P:\Script Explorer\Scripts\Printers\Add Offline Comment\Joseph”
#Get Current Date
$Date= Get-Date
#Create List of Offline Printers
$OfflinePrinters = get-printer -ComputerName $PrintServer -name ADM*,BHS*,GA*,GICA*,PBX*,RAX* | Where-Object { $_.PrinterStatus -eq “Offline” -or $_.PrinterStatus -eq “Error, Offline” } | Select-Object Name,PortName,PrinterStatus | Sort-Object Name
You’re awesome! Dang it was seriously as easy as a Variable for the comment. Thank you so much Joseph!
Thanks! I appreciate that. Just a warning, there is not any logic in that append statement so you may end up with really long comments. It might be better to do something like add a special symbol at the end of each existing comment and just remove data following that symbol.
I had to add a -unique at the end of Sort-Object Name because for some reason it was giving me duplicates of everything. Spent an hour trying to figure out why. Even if you just pasted in just the #Create List of Offline Printers line, powershell would write out duplicates. Still don’t understand why. Shrug. -unique seems to have fixed it in a non-disruptive way.
Thank you for the great script. I’m currently expanding on it to check multiple print servers at the same time. I created an array of print servers and made a Foreach/Function loop to go through each server in the array. Right now I have each server checking/editing a unique CSV based on that server’s name.
$OfflinePrinters | export-csv (“OfflinePrinters” + $PrintServer + “.CSV”) -NoTypeInformation
Thanks for this script.
I have amended it slightly as shown below to get rid of the need to write to files and just use the comment field in the printer. I have also added a check and notification (using email) based on then how long the printer has been offline.
# Script to check how long a printer has been offline and send notifications based on criteria
# The offline date and time is filed into the comments field on the printer and then used i nthe calcualtion
# on subsequent runs of the script
# Define the email server to send messages to
$smtpServer = “email server name”
#PrintServer to Connect to
$PrintServer = “print server name”
#Set Location for Logging
Set-Location “c:\powershell”
#Get Current Date
$Date= Get-Date
# Converted to String to get around issues with local settings (seem to be a problem with UK dates).
$datestring=$date.ToString()
# If printer online set the comment field to blank
$OnlinePrinters = get-printer -ComputerName $PrintServer -name * | Where-Object { $_.PrinterStatus -eq “Normal” } | Select-Object Name,PortName,PrinterStatus,Comment | Sort-Object Name
ForEach ($OnlinePrinter in $OnlinePrinters) {
set-printer -name $OnlinePrinter.Name -ComputerName $PrintServer -Comment “”
}
#Create List of Offline Printers
$OfflinePrinters = get-printer -ComputerName $PrintServer -name * | Where-Object { $_.PrinterStatus -eq “Offline” -or $_.PrinterStatus -eq “Error, Offline” } | Select-Object Name,PortName,PrinterStatus,Comment | Sort-Object Name
# If not previousally offline set the date in the comment field
ForEach ($OfflinePrinter in $OfflinePrinters) {
If ($OfflinePrinter.Comment -eq “” -or $OfflinePrinter.Comment -eq $null){
set-printer -name $OfflinePrinter.Name -ComputerName $PrintServer -Comment $Datestring}
}
#Reresh List of Offline Printers with updated offline dates
$OfflinePrinters = get-printer -ComputerName $PrintServer -name * | Where-Object { $_.PrinterStatus -eq “Offline” -or $_.PrinterStatus -eq “Error, Offline” } | Select-Object Name,PortName,PrinterStatus,Comment | Sort-Object Name
# Read the new list of printers and see how long a printer has been offline and send notifications
# change in the IF statement to set time criteria for notification
ForEach ($OfflinePrinter in $OfflinePrinters) {
$offlinedate=$offlineprinter.Comment
$printername=$offlineprinter.name
$TimeSpan = new-timespan -start $offlinedate -End $Date
$hoursoffline = $Timespan.Hours
IF ($hoursoffline -gt 24){
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = “from email address”
$msg.To.Add(“to email address”)
$msg.Subject = “Offline Printer – $printername”
$msg.Body = “The Printer $printername has been offline for $hoursoffline hours. Please contact the site to find out if the printer is faulty or if it has been moved”
$smtp.Send($msg)
}
}
Thanks for optimizing this script, Bryan!
Server 2012 R2
Hi Joseph.
Thank you for posting this article, but I can’t seem to get the script to work properly.
When I run it, I get this error:
#Export List of Offline Printers
$OfflinePrinters | export-csv \\..\…\…\…\powershell\OfflinePrinters.CSV -NoTypeInformation
import-csv : Could not find file ‘\\..\…\…\…\powershell\OfflinePrinters.CSV’.
At line:15 char:27
+ $PreviousOfflinePrinters= import-csv \\..\…\…\…\powershell\ …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (:) [Import-Csv], FileNotFoundException
+ FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.ImportCsvCommand
Compare-Object : Cannot bind argument to parameter ‘ReferenceObject’ because it is null.
At line:16 char:34
+ $OnlinePrinters = Compare-Object $PreviousOfflinePrinters.Name $OfflinePrinters. …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Compare-Object], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.C
ompareObjectCommand
And even though my print server is showing one printer as offline, the csv file that is created is empty.
Any advice would be greatly appreciated.
Thanks.
Glynn.
I think that may be crappy error checking on my part. As a test, create a fake OfflinePrinters.CSV and run the script. Let me know if it works.
Hi Joseph.
If I create a fake OfflinePrinters.CSV and run the script, I get the following error:
Compare-Object : Cannot bind argument to parameter ‘ReferenceObject’ because it is null.
At \\\\\\offline_printers.ps1:19 char:34
+ $OnlinePrinters = Compare-Object $PreviousOfflinePrinters.Name $OfflinePrinters. …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Compare-Object], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.C
ompareObjectCommand
Thanks.
Glynn
So I’m loving this and I’m going to use the email variable that Bryan above introduced. I have an issue and it is such a little one. How can I append the comment box instead of overwriting what is already written. We use the comment box to put some extra information and I need to be able to just append. If I cannot do that I kind of have to re approach my method for this script which is a drag cause this is exactly what I am looking for.
Try this version. Note the changes on line 33 and line 34 ($APPENDCOMMENT)
# Script to check how long a printer has been offline and send notifications based on criteria
# The offline date and time is filed into the comments field on the printer and then used i nthe calcualtion
# on subsequent runs of the script
# Define the email server to send messages to
$smtpServer = “email server name”
#PrintServer to Connect to
$PrintServer = “print server name”
#Set Location for Logging
Set-Location “c:\powershell”
#Get Current Date
$Date = Get-Date
# Converted to String to get around issues with local settings (seem to be a problem with UK dates).
$datestring = $date.ToString()
# If printer online set the comment field to blank
$OnlinePrinters = get-printer -ComputerName $PrintServer -name * | Where-Object { $_.PrinterStatus -eq “Normal” } | Select-Object Name, PortName, PrinterStatus, Comment | Sort-Object Name
ForEach ($OnlinePrinter in $OnlinePrinters) {
set-printer -name $OnlinePrinter.Name -ComputerName $PrintServer -Comment “”
}
#Create List of Offline Printers
$OfflinePrinters = get-printer -ComputerName $PrintServer -name * | Where-Object { $_.PrinterStatus -eq “Offline” -or $_.PrinterStatus -eq “Error, Offline” } | Select-Object Name, PortName, PrinterStatus, Comment | Sort-Object Name
# If not previousally offline set the date in the comment field
ForEach ($OfflinePrinter in $OfflinePrinters) {
If ($OfflinePrinter.Comment -eq “” -or $OfflinePrinter.Comment -eq $null) {
$AppendComment = $OfflinePrinter.Comment + $datestring
set-printer -name $OfflinePrinter.Name -ComputerName $PrintServer -Comment $AppendComment
}
}
#Reresh List of Offline Printers with updated offline dates
$OfflinePrinters = get-printer -ComputerName $PrintServer -name * | Where-Object { $_.PrinterStatus -eq “Offline” -or $_.PrinterStatus -eq “Error, Offline” } | Select-Object Name, PortName, PrinterStatus, Comment | Sort-Object Name
# Read the new list of printers and see how long a printer has been offline and send notifications
# change in the IF statement to set time criteria for notification
ForEach ($OfflinePrinter in $OfflinePrinters) {
$offlinedate = $offlineprinter.Comment
$printername = $offlineprinter.name
$TimeSpan = new-timespan -start $offlinedate -End $Date
$hoursoffline = $Timespan.Hours
IF ($hoursoffline -gt 24) {
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = “from email address”
$msg.To.Add(“to email address”)
$msg.Subject = “Offline Printer – $printername”
$msg.Body = “The Printer $printername has been offline for $hoursoffline hours. Please contact the site to find out if the printer is faulty or if it has been moved”
$smtp.Send($msg)
}
}