Windows 2003 Print Log Parsing Script [PowerShell]

Hello everyone,

Since I’m not aware of any tools that visually log print server information, I wrote a script that parses the log entries from the system event log of a Windows 2003 print server. Apparently I never got around to posting this online, but I hope that someone is able to benefit from it.

Basically, the process flow of the script looks like this:

  1. Retrieve list of all print entries from server event log
  2. Parses information from each log entry and creates an object in memory representing it
  3. Builds a hash table from all objects
  4. Iterates over the hash table and writes each print log entry to an Excel document

If you have any questions about it, please feel free to let me know.

FYI, I commented out the line near the bottom that does the Excel output, so that it could be executed on a server without Excel installed.

#################################################################################
#																				#
#	Author: Trevor Sullivan														
#																				#
#	  Date: February 10th, 2009													
#																				#
#  Purpose: This script is meant for running against a Windows 2003 event log.	
#		The verbage for the print log entries has changed for Windows Vista.	
# 																				#
#################################################################################

Function GetPrintEntriesFromTextFile($tFileName)
{
	Get-Content $tFileName
}

Function GetPrintEntriesFromLog()
{
	$PrintEntries = Get-EventLog -LogName System | Where-Object { $_.EventId -eq 10	-and $_.Source -eq "Print" -and $_.TimeGenerated.Month -eq (Get-Date).Month	-and $_.TimeGenerated.Year -eq (Get-Date).Year }
	return $PrintEntries
}

# Parses username from a Windows 2003 Print log message
Function GetUserName($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }

	$rxUserName = [regex]"owned by ([0-9a-zA-Z]{7})"
	$rxUserName = [regex]"owned by ([0-9a-zA-Z]{1,}) was"
	$rxMatches = $rxUserName.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

Function GetPrinterName($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }

	#Write-Host "Parsing printer name"
	$rxPrinterName = [regex]"printed on ([0-9a-zA-Z-_s]{5,}) via"
	$rxMatches = $rxPrinterName.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

Function GetPrintSize($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }

	#Write-Host "Getting print size"
	$rxPrintSize = [regex]"Size in bytes: ([0-9]+);"
	$rxMatches = $rxPrintSize.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

Function GetPageCount($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }

	#Write-Host "Getting page count"
	$rxPageCount = [regex]"pages printed: ([0-9]+)"
	$rxMatches = $rxPageCount.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

Function GetDocumentName($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }

	#Write-Host "Getting print size"
	$rxDocumentName = [regex]", ([a-zA-Z-_:/[#]?\=d.s()&-,]{1,}) owned by"
	$rxMatches = $rxDocumentName.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

# Retrieves user's full name from AD
Function GetUserFullName($UserId)
{
	if ($UserId -gt "")
	{
		$DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher
		$LdapFilter = "(&(objectClass=user)(samAccountName=${UserId}))"
		#Write-Host "Filter is: ${LdapFilter}"
		$DirectorySearcher.Filter = $LdapFilter
		$UserEntry = [adsi]"$($DirectorySearcher.FindOne().Path)"
		#Write-Host $UserEntry.displayName
		return $UserEntry.displayName
	}

	return
}

Function CreatePrintJob()
{
	$PrintJob = New-Object PsObject
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name PageCount -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name UserName -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name DocumentName -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Size -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Printer -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Time -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name UserFullName -Value $null
	return $PrintJob
}

Function ParsePrintEntry($PrintEntry)
{
	$NewPrintJob = CreatePrintJob

	if ($PrintEntry.GetType() -eq [System.String])
	{
		$NewPrintJob.PageCount = GetPageCount $PrintEntry
		$NewPrintJob.UserName = GetUserName $PrintEntry
		$NewPrintJob.DocumentName = GetDocumentName $PrintEntry
		$NewPrintJob.Size = GetPrintSize $PrintEntry
		$NewPrintJob.Printer = GetPrinterName $PrintEntry
		$NewPrintJob.UserFullName = GetUserFullName $NewPrintJob.UserName
	}
	elseif ($PrintEntry.GetType() -eq [System.Diagnostics.EventLogEntry])
	{
		$NewPrintJob.PageCount = GetPageCount $PrintEntry.Message
		$NewPrintJob.UserName = GetUserName $PrintEntry.Message
		$NewPrintJob.DocumentName = GetDocumentName $PrintEntry.Message
		$NewPrintJob.Size = GetPrintSize $PrintEntry.Message
		$NewPrintJob.Printer = GetPrinterName $PrintEntry.Message
		$NewPrintJob.Time = $PrintEntry.TimeGenerated.ToString()
		$NewPrintJob.UserFullName = GetUserFullName $NewPrintJob.UserName
	}

	return $NewPrintJob
}

Function Main()
{
	$PrintEntries = GetPrintEntriesFromLog
	#$PrintEntries = GetPrintEntriesFromTextFile "${env:USERPROFILE}DocumentsPowershell ScriptsPrint Log ParserPrintLog.txt"
	$Global:ParsedEntries = @{}; $i = 0

	ForEach ($PrintEntry in $PrintEntries)
	{
		$ParsedEntries.Add($i, $(ParsePrintEntry $PrintEntry))
		#$ParsedEntries += (ParsePrintEntry $PrintEntry)
		$i++

		if ($i % 100 -eq 0)
		{ Write-Host "Processed $i records" }
	}

	# Export information to CSV or Excel document
	$ParsedEntries.Values | Export-Csv "c:$((Get-Date).Month).csv" -NoTypeInformation
	#WriteToExcel $ParsedEntries
}

Function WriteToExcel($tEntries)
{
	# Load Excel interop assembly
	[Void] [Reflection.Assembly]::LoadWithPartialName("Microsoft.Office.Interop.Excel")

	# Create Excel application, workbook, and worksheet objects
	$Excel = New-Object Microsoft.Office.Interop.Excel.ApplicationClass
	$Workbook = $Excel.Workbooks.Add()
	$Worksheet = $Workbook.Worksheets.Add()

	# Write Excel worksheet headers
	$Worksheet.Cells.Item(1, 1).Value2 = "Username"
	$Worksheet.Cells.Item(1, 2).Value2 = "Full Name"
	$Worksheet.Cells.Item(1, 3).Value2 = "Time"
	$Worksheet.Cells.Item(1, 4).Value2 = "Page Count"
	$Worksheet.Cells.Item(1, 5).Value2 = "Printer"
	$Worksheet.Cells.Item(1, 6).Value2 = "Size (bytes)"
	$Worksheet.Cells.Item(1, 7).Value2 = "Document Name"
	# End writing Excel worksheet headers	

	# Iterate over each print entry
	$row = 2
	ForEach ($key in $tEntries.Keys)
	{
		$Worksheet.Cells.Item($row, 1).Value2 = $tEntries[$key].UserName
		$Worksheet.Cells.Item($row, 2).Value2 = $tEntries[$key].UserFullName
		$Worksheet.Cells.Item($row, 3).Value2 = $tEntries[$key].Time
		$Worksheet.Cells.Item($row, 4).Value2 = $tEntries[$key].PageCount
		$Worksheet.Cells.Item($row, 5).Value2 = $tEntries[$key].Printer
		$Worksheet.Cells.Item($row, 6).Value2 = $tEntries[$key].Size
		$Worksheet.Cells.Item($row, 7).Value2 = $tEntries[$key].DocumentName
		$row++
	}

	# Do some formatting
	# AutoFit the columns
	[Void] $Excel.ActiveCell.CurrentRegion.Columns.AutoFit()

	# Add table styling and auto-filtering
	[Void] $Excel.ActiveCell.CurrentRegion.Select()
	$ListObject = $Excel.ActiveSheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceType]::xlSrcRange, $Excel.ActiveCell.CurrentRegion, $null ,[Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes)
	$ListObject.Name = "TableData"
	$ListObject.TableStyle = "TableStyleLight9"

	# Show the Excel window after writing data to spreadsheet
	$Excel.Visible = $true
}

Main