Update 2011-07-06: Support for Windows 2008+ servers has been added as well as handling stand-alone vs. clustered servers.

If you haven’t seen it yet, check out my posting on my Group Policy VBScript to Replace, Add, Remove and Manage User Printer Queues.

Sometimes, managing a print server can be a real pain… Ok, who are we kidding? It’s a pain! Period. Printer failures, new installations or someone forgot to tell you they tossed that printer into the garbage compactor. These are all things that can add up to have printer queues hanging around that are just not being utilized any longer.

To help resolve this, I wrote a PowerShell script that audits the printer queues on a server (locally or remote) and will tell us whether or not a Printer Queue has been used and if so, when it was last used.

The script works by first grabbing a list all of the printer queues on a print server. It then goes through all the logs in the print server’s event log and looks specifically for ‘Print’ events. The key events we are looking for are the ones like below as they have the information we need, a queue name and a time. This is what it looks like on a Windows 2003 server:

"Document [number], [title] owned by [user] was printed on [printer] via port [port].  Size in bytes: [bytes]; pages printed: [pages]"

It can take a while to sort through all the events but as it does it tracks whether or not a queue has been used and when the last time it was used.

Once complete we get a nice little CSV file to look at. From there it should be pretty obvious which queues are in use or not.

Hold it! There is a caveat… isn’t there always? The script can only go back as far as the event log does. So if your servers are configured to discard old events or the System log has recently been cleared this script may not be as helpful. Either way, the script is below. Enjoy!

#  Script name:     PrinterQueueAudit.ps1
#  Created on:      2010-11-18
#  Author:          Gregory Strike
#  URL:             http://www.gregorystrike.com/2010/11/18/powershell-script-to-audit-print-server-queue-usage/
#
#  Purpose:         A script used to audit Printer Queues on a server (local or remote)
#                   to determine when the queues were last used or if at all.  A CSV
#                   file (PrinterQueueAudit.csv) is then generated with the results.
#
#  Requirements:    This script requires the ability to read printer queues and the 'System'
#                   event log on the Print Server.  The script is only able to go back in
#                   time as far as the Event Log.
#
#  Updates:
#                   2011-07-06
#                   Added Windows 2008+ Support
#                   Added code to handle certain stand-alone servers.

Clear

#Set these variables manually.
#MANUAL - Change this to your Print Server name.
$Computer = "YOUR-SERVER"
#MANUAL - Is $Computer a clustered name?
$Cluster = $True

#Create a custom type (Queue) to define our queues and their usage.
Add-Type @'
public class Queue{
    public System.String Name;
    public System.Boolean Used;
    public System.DateTime LastUsed;
}
'@

Write-Host ("Processing Print Server: " + $Computer)

#Detect the Windows version
$WMIVersion = Get-WMIObject -Class Win32_OperatingSystem -Property "Version" -Computer $Computer
$Version = [UInt32]$WMIVersion.Version.SubString(0,1)
If ($Version -ge 6){
    Write-Host ("OS Version: Windows 2008 or later.")
    $Windows2008 = $True
} else {
    Write-Host ("OS Version: Windows 2003 R2 or earlier.")
    $Windows2008 = $False
}

#Get all printer queues from the server.
Write-Host "Gathering Printer Queues..."
If ($Cluster) {
    $ServerQueues = Get-WMIObject -Class Win32_PerfFormattedData_Spooler_PrintQueue -Property "Name" -Computer $Computer
} else {
    $ServerQueues = Get-WMIObject -Class Win32_Printer -Property "Name" -Computer $Computer
}

#Create an array of our custom type and populate it with our the queue names.
$Queues = @()
ForEach ($ServerQueue in $ServerQueues){
    $Queue = New-Object -typeName Queue
    If ($Cluster) {
        $SplitName = ($ServerQueue.Name.Split("\"))
        $Queue.Name = $SplitName[$SplitName.Count - 1]
    } else {
        $Queue.Name = $ServerQueue.Name
    }

    $Queue.Used = $False
    $Queues = $Queues + $Queue
}

Write-Host "Gathering Event Log Data from Event Log.  This may take a while... "
#Get all Events from the  Event Log of the Computer.

If ($Windows2008) {
    $Events = (Get-WinEvent Microsoft-Windows-PrintService/Operational -Computer $Computer)
	} else {
    $Events = (Get-EventLog -LogName "System" -Computer $Computer)
}

$NumEvents = $Events.Count
Write-Host ("...There are " + $NumEvents + " records.")

#Loop through ALL events and only process "Print" events.
Write-Host("Processing Print Events...")
For ($Index = ($Events.Count - 1); $Index -ge 0; $Index--){
    $PercentComplete = (100 - [Math]::Round( ($Index / ($NumEvents - 1) * 100), 0))
    If ($PercentComplete -ne $LastPercentComplete){
        #Outputs the percentage... This looks good in the PowerShell console, not so much in ISE.
        Write-Host([System.String]::Format("`b`b`b`b`b`b`b`b`b`b`b`b`b`b{0:000}% complete.", $PercentComplete)) -NoNewLine
        $LastPercentComplete = $PercentComplete
    }

    $Log = $Events[$Index]

    #Check if the event was a print job event.
    If ($Log.Message.Contains(" was printed on ")){

        #Strip message to get just the printer name
        If ($Windows2008) {
            $Step1 = $Log.Message.SubString(0, $Log.Message.IndexOf(" through port "))
        } else {
            $Step1 = $Log.Message.SubString(0, $Log.Message.IndexOf(" via port "))
        }

        $Printer = $Step1.SubString($Step1.IndexOf(" was printed on ") + 16)        

        #Find queue name in our array and update with results.
        ForEach($Queue in $Queues){
            If ($Queue.Name -eq $Printer){
                $Queue.Used = $True

                #Only save time if it is more recent.
                If ($Windows2008) {
                    If ($Log.TimeCreated -gt $Queue.LastUsed){
                        $Queue.LastUsed = $Log.TimeCreated
                    }
                } else {
                    If ($Log.TimeGenerated -gt $Queue.LastUsed){
                        $Queue.LastUsed = $Log.TimeGenerated
                    }
                }

            }
        }
    }
}

Write-Host("")
Write-Host("Exporting results to PrinterQueueAudit.csv...")
$Queues | Export-CSV PrinterQueueAudit.csv

Gregory Strike

Husband, father, IT dude & blogger wrapped up into one good looking package.