Druckeraccounting

Selbstverständlich gibt es auch viele kommerzielle Produkte, die allerdings meist eins gemeinsam haben:
  • sehr teuer
  • viele Features von denen nur ein Bruchteil genutzt/ gebraucht wird
Um dies aus dem Weg zu gehen und trotzdem ein voll funktionsfähiges Accounting für Printserver, die unter Windows 2008 laufen zu haben, ist das Folgende entstanden.

Das Funktionsprinzip ist:
Beim Eintreten eines Events (das kann das Schreiben eines Logfileeintrags sein, oder auch ein geplanter Task) wird ein Powershell- Script aufgerufen. Dieses Script analysiert das Logfile eines (Remote) Printservers und schreibt die gefundenen Werte in eine (Remote) SQL- Datenbank. Als Merker wertet das Script den Zeitstempel des letzten analysierten Eintrags aus und schreibt diesen ebenfalls in die Datenbank um beim nächsten Lauf erst dort wieder anzufangen.
Die erfassten Werte sind:
  • Zeitstempel des Ausdrucks
  • Benutzername
  • Dokumentname
  • Druckername
  • Anzahl der Seiten
  • Größe in Bytes
  Die Variablen in den Zeilen 3-7 sind entsprechend der Umgebung anzupassen.

ACHTUNG: Das Script funktioniert ausschliesslich für Windows 2008 Server. Im Windows 2008 R2 Server wurde einiges an der Protokollierung geändert. Eine passende Version für R2- Server ist hier.

Powershellscript

# printserver accounting to sql database
 
$printserver = 'print.server.rs.de'
$dbserver = 'db.server.rs.de'
$dbuser = 'prnacc'
$dbpass = 'dodalgeheim'
$dbname = 'prnacc'
 
# load assemblies
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null;
 
# connect to SQL Server
$server = New-Object -typeName Microsoft.SqlServer.Management.Smo.Server -argumentList "$dbserver"
 
# login using SQL authentication, which means we supply the username
# and password
$server.ConnectionContext.LoginSecure=$false;
$server.ConnectionContext.set_Login($dbuser)
$securePassword = ConvertTo-SecureString $dbpass -AsPlainText -Force
$server.ConnectionContext.set_SecurePassword($securePassword)
 
# clear the screen
cls
 
$conn = New-Object System.Data.SqlClient.SqlConnection("Server=$dbserver; Database=$dbname; Integrated Security=SSPI")
$conn.Open()
 
$cmd = $conn.CreateCommand()
$cmd.CommandText = "SELECT wert FROM [dbo].[configs] WHERE schluessel = 'lastinsertrow'"
 
$Reader = $cmd.ExecuteReader()
while ($Reader.Read()) {
  $lastinsertrow = $Reader.GetValue(0)
}
$Reader.Close()
 
write-host "Starting with record: $lastinsertrow" -back red
 
$entries = Get-WmiObject -ComputerName $printserver -Query "SELECT RecordNumber, TimeGenerated, InsertionStrings FROM Win32_NTLogEvent WHERE 
 
SourceName = 'Microsoft-Windows-PrintSpooler' AND EventCode=10 AND TimeGenerated > '$lastinsertrow'" | Select-Object RecordNumber, TimeGenerated, InsertionStrings 
 
foreach($entry in $entries){
  # recordnumber
  if ( $entry.TimeGenerated -gt $lastinsertrow ) {
    $lastinsertrow = $entry.TimeGenerated
    write-host $lastinsertrow
  }
  # record
  # [int]$entry.RecordNumber
  # time
  $datarr=$entry.TimeGenerated.split(".")
  # user
  #$entry.InsertionStrings[2] 
  # docname
  #$entry.InsertionStrings[1]
  # drucker
  #$entry.InsertionStrings[3]
  # pages
  #$entry.InsertionStrings[6]
  # bytes
  #$entry.InsertionStrings[5]
  write-host "working on $datarr..."
  $cmd = $conn.CreateCommand()
  $cmd.CommandText = "INSERT INTO [dbo].[jobs] (datetime, username, docname, printer, pages, size) VALUES ('" + $datarr[0] + "','" + $entry.InsertionStrings[2] + "','" + $entry.InsertionStrings[1] + "','" + $entry.InsertionStrings[3] + "'," + $entry.InsertionStrings[6] + "," + $entry.InsertionStrings[5] + ")"
  $cmd.ExecuteNonQuery() | out-null
 
}
 
$cmd = $conn.CreateCommand()
$cmd.CommandText = "UPDATE [dbo].[configs] SET wert = '" + $lastinsertrow + "' WHERE schluessel = 'lastinsertrow'"
$cmd.ExecuteNonQuery() | out-null
 
$conn.close()
 
Write-Host "Latest-Record: $lastinsertrow" -back red

Datenbankschemas

Und hier noch die Datenbankschemas der beiden benötigten Tabellen. Der Datenbankname selbst ist hier prnacc und muss wenn er geändert wird auch noch im Powershell-Script angepasst werden.

SQL Schema für Tabelle configs

USE [prnacc]
GO
 
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[configs](
  [id] [int] IDENTITY(1,1) NOT NULL,
  [schluessel] [nvarchar](50) NOT NULL,
  [wert] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_configs] PRIMARY KEY CLUSTERED 
(
  [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
 
GO

SQL Schema für Tabelle jobs

USE [prnacc]
GO
 
SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[jobs](
  [datetime] [nvarchar](50) NOT NULL,
  [username] [nvarchar](50) NOT NULL,
  [docname] [nvarchar](max) NOT NULL,
  [printer] [nvarchar](50) NOT NULL,
  [pages] [int] NOT NULL,
  [size] [bigint] NOT NULL
) ON [PRIMARY]
 
GO

Aufruf

Das Script selbst kann, wie bereits gesagt, über einen geplanten Task aufgerufen werden.
Hierzu ist als Kommandozeile Folgendes zu verwenden:  
powershell.exe -Noninteractive -Command "&{PFAD_ZUM_SCRIPT}"