Splunk 6 has been out almost six months and I have not yet finished covering all the new Windows features. Let’s continue doing that by looking at print monitoring. If you have ever wanted to do charge back reporting for print jobs but lacked the data, then this is for you. The Windows Print Monitor is a new data input in the Splunk 6 Universal Forwarder (ok – it’s also available on Splunk Enterprise).
The idea of this is fairly simple. Install a Splunk 6 Universal Forwarder on your print servers, set up the data input and you will get data. There are two types of data you can get – inventory type information such as the printers, the ports they are attached to (on Windows, servers always connect to printers via ports – even on the network) and the drivers running them are the first. You can monitor this information by enabling them in the inputs.conf like this:
[WinPrintMon://printer] type=printer interval=600 baseline=1 disabled=0 [WinPrintMon://driver] type=driver interval=600 baseline=1 disabled=0 [WinPrintMon://port] type=port interval=600 baseline=1 disabled=0
You will note that there is an interval, measured in seconds. This allows you to capture changes within a certain time period. Print Servers are generally not busy servers so a low interval is generally ok. However, the process does iterate over all the printer objects in the server, so it depends on your environment. Since I generally recommend you install the Splunk Add-on for Windows on all Windows hosts, you can put this in the local inputs.conf of that add-on. You will get events like this from these inputs:
04/21/2014 13:51:59.486 operation=set type=Printer ComputerName=ops-sys-001 printer=HP LaserJet M3035 mfp PCL6 share= port=IPAddress driver=HP LaserJet M3035 mfp PCL6 comment=None location= separate_file= print_processor=hpzppwn7 data_type="RAW" parameters= status="normal" attributes=979 priority=6 default_priority=2 jobs=8 average_PagePerMinute=73
Note that the type field is Printer. The other type fields for inventory are PrintDriver and PrintPort. You can use these events to create inventory type lookups of all the information. When doing this, I would not include the dynamic fields – jobs or average_PagePerMinute – these would cause unnecessary updates to your lookup file. We’ve discussed generating lookup files in prior blog posts, so I won’t go over them here.
The (to my mind) more interesting input is the Job input. You define it like this:
[WinPrintMon://jobs] type=job interval=60 baseline=0 disabled=0
This writes out an event per print job and they look something like this:
04/21/2014 13:52:19.486 operation=add type=PrintJob printer=HP LaserJet M3035 mfp PCL6 ComputerName=ops-sys-001 machine=ops-sys-001 user=adrian document=wallstreetjournal.htm notify_name=adrian JobId=35 data_type="NT EMF 1.008" print_processor=hpzppwn7 parameters= driver_name="HP LaserJet M3035 mfp PCL6 status=printing priority=5 total_pages=9 size_bytes=397039 submitted_time=04/21/2014 13:51:40.131 page_printed=7
Producing a report by user for the number of pages printed on a per-printer basis is fairly easy:
sourcetype=WinPrintMon type=PrintJob operation=add | stats sum(page_printed) by user
However, let’s go a little bit further. Let’s say that we have generated a lookup table that has three fields, like this:
host,printer,cost ops-sys-001,HP LaserJet M3035 mfp PCL6,0.02
We now now augment our search as follows:
sourcetype=WinPrintMon type=PrintJob operation=add | lookup printerCost host,printer OUTPUT cost | eval cost=if(isnull(cost),0.01,cost) | eval job_cost=cost * page_printed | stats sum(page_printed) as pagecount, sum(job_cost) as total_cost by user
Note our use of the eval to set a default value for cost, just in case one is not specified. We are almost at an ideal search. However, this isn’t suitable as a chargeback report because we want this by department. To turn a user into a department, you can use another lookup or you can use Active Directory:
sourcetype=WinPrintMon type=PrintJob operation=add | lookup printerCost host,printer OUTPUT cost | eval cost=if(isnull(cost),0.01,cost) | eval job_cost=cost * page_printed | stats sum(page_printed) as pagecount, sum(job_cost) as total_cost by user | rex field=user "^(?[^\\]+)\\(?.*)" | ldapfilter domain=$src_nt_domain$ search="(sAMAccountName=$src_user$)" attrs="department" | eval department=if(isnull(department),"UNKNOWN",department) | stats sum(pagecount) as pagecount, sum(total_cost) as total_cost by department
The additional work separates out the username we get from the WinPrintMon into domain and user, then we use these to create an Active Directory Filter to retrieve the department. Finally, just as in the case of the page cost, we provide a default for the case when the department is not known and do the calculation. You may be wondering why we do stats twice – once at the user level and once at the department level. This is for efficiency. Do you want hundreds of thousands of ldap queries going to Active Directory, or just a couple of hundred? I suspect the latter.
You should now have the capabilities in your hand for providing chargeback reporting of print jobs to departments. With these capabilities, you can provide accounting with a report on a regular basis (as a CSV), provide individual reports for departments as needed and look at individual users.