PowerShell One-Liner to Audit Print Jobs on a Windows based Print Server

You've been tasked with auditing print jobs on your company's Windows based print server to determine who is wasting so much paper, toner, and causing excessive wear and tear on printers. No budget exists for this task and you need to have something to show by the end of the day.

You would probably start off by searching the Internet, but most of the results you'll find to accomplish this task are over-complicated or simply don't work. Luckily this task can be accomplished with a PowerShell one-liner. The event log that contains the information you're looking for is "Microsoft-Windows-PrintService/Operational". It's disabled by default and needs to be enabled before it can start logging the information that you'll want to query.

printing-history3a.jpg

First, check the status of the event log to confirm that it's not already enabled. This method of enabling the event log won't work with classic event logs so if you're enabling or disabling a different event log, confirm that it's not one of those. In addition to returning results, the first command stores the results in a variable named "PrinterLog". Use that variable to enable the log and then save the changes. Then check to see if the changes did indeed take effect:

1Get-WinEvent -ListLog Microsoft-Windows-PrintService/Operational -OutVariable PrinterLog |
2Select-Object -Property LogName, IsClassicLog, IsEnabled
3
4$PrinterLog.set_IsEnabled($true)
5
6$PrinterLog.SaveChanges()
7
8Get-WinEvent -ListLog Microsoft-Windows-PrintService/Operational -OutVariable PrinterLog |
9Select-Object -Property LogName, IsClassicLog, IsEnabled

printing-history1a.jpg

The printing history can only be retrieved from the point the log was enabled and forward as shown in the previous example.

Simple query that event log for event id 307 using the Get-WinEvent cmdlet. Much of the information that you'll want is stored in the properties collection. Many of the examples I saw on the Internet parse XML for these results and there's simply no reason to add that level of complexity. My chapter (chapter 6) in the PowerShell Deep Dives book talks about querying event logs in more detail if that's something you're interested in.

The following example retrieves the information for the past day. Also keep in mind that this event log overwrites by default so the information is not kept forever.

1Get-WinEvent -FilterHashTable @{LogName="Microsoft-Windows-PrintService/Operational"; ID=307; StartTime=(Get-Date).AddDays(-1)} |
2Format-Table -Property TimeCreated,
3                        @{label='UserName';expression={$_.properties[2].value}},
4                        @{label='ComputerName';expression={$_.properties[3].value}},
5                        @{label='PrinterName';expression={$_.properties[4].value}},
6                        @{label='PrintSize';expression={$_.properties[6].value}},
7                        @{label='Pages';expression={$_.properties[7].value}}

printing-history2a.jpg

You could also output the results to a CSV if needed:

1Get-WinEvent -FilterHashTable @{LogName="Microsoft-Windows-PrintService/Operational"; ID=307; StartTime=(Get-Date -OutVariable Now).AddDays(-1)} |
2Select-Object -Property TimeCreated,
3                        @{label='UserName';expression={$_.properties[2].value}},
4                        @{label='ComputerName';expression={$_.properties[3].value}},
5                        @{label='PrinterName';expression={$_.properties[4].value}},
6                        @{label='PrintSize';expression={$_.properties[6].value}},
7                        @{label='Pages';expression={$_.properties[7].value}} |
8Export-Csv -Path "Printing Audit - $($($Now).ToString('yyyy-MM-dd')).csv" -NoTypeInformation

For simplicity, the examples shown in this blog article have been run directly on the print server itself. They could just as easily be run remotely and while Get-WinEvent does have a ComputerName parameter, running it in that manner will more than likely be blocked by the firewall on the remote server. The simplest way to run it remotely would be to wrap the commands shown in this blog article inside of Invoke-Command as long as PowerShell remoting is enabled on the remote server.

µ