PowerShell Beispiele : Eventlog

Was für den UNIX-Administrator seine Syslogs sind, ist bei Windows das Eventlog. Aus Sicht eines PowerShell Entwicklers gibt es da zwei Aspekte:

  • Eventlogs lesen und auswerten
  • SelbstEvents schreiben

Diese Themen wird auf dieser Seite behandelt

Eventlogs lesen

Über das Commandlet "Get-Eventlog" kann man natürlich sehr einfach die Daten der verschiedenen Eventlogs von Windows extrahieren. Seit PowerShell 2 und Windows Vista und höher gibt es mit "Get-WinEvent" eine noch leistungsfähigere Version.

Der größte Vorteil ist dabei die Möglichkeit, alternative Credentials mitzugeben, was speziell bei der Suche auf anderen Computern hilfreich sein kann.

Get-WinEvent vs. Get-Eventlog

Wer nun genau aufgepasst hat, hat zwei Commandlets entdeckt, die eigentlich den gleichen Zweck haben:

Get-WinEvent gibt es erst seit Windows 8, während Get-Eventlog schon seit Powershell 2.0 an Bord ist. Das vom Namen vergleichbare "Get-Event" von Powershell 2.0 hat aber nichts mit Eventlogs zu tun. Get-WinEvent ist nicht nur neuer, sondern erlaubt gegenüber von Get-Eventlog auch komplexere Abfragen, z.B:

Get-WinEvent -FilterHashtable @{logname="Security";level=3;StartTime="01.07.2016";} 

Speziell bei WAN-Zugriffen kann dann der Quellserver schon Filtern und sendet nicht alle Einträge, die dann mit einem "Where" lokal gefiltert werden können.

Eventlog und Windows 2008/Vista
Das Script funktioniert leider nicht auf Windows 2008 oder Vista sondern nur auf Windows 208R2 oder Windows 7, da ein "Bug" in GET-WinEvent den Einsatz des Filters mit "FilterHashtable" nicht zulässt und ich aus Gründen der Lesbarkeit nicht den Filter als XML-Struktur verwenden wollte.
Siehe auch https://connect.microsoft.com/PowerShell/feedback/details/422072/get-winevent-on-vista-does-not-with-vista-x64-using-ctp3

Events suchen und filtern

Das komplette Lesen eines Eventlogs dauert lange und über WAN ist Bandbreite auch kostbar. Daher ist ein normales "Lesen" mit einer nachfolgenden Filterung in der folgenden Form unsinnig:

Get-EventLog -LogName security | where {$_.InstanceId -eq }4770

Hierbei würde das Commandlet alle Security Events auslesen und erst dann lokal filtern. Besser ist es hier natürlich schon bei der Anfrage den Filter mit zu geben, so dass der Server selbst schon filtern kann.

 Get-EventLog -LogName security -InstanceId 4770

Sie können die Ergebnisse vorab auch noch weiter einschränken, z.B.: über die Zeit (-Before und -After), Source und EntryType

Natürlich ist die Abfrage auch per WMI möglich. Hier ein Beispiel. Hier suche ich 1221 Events der letzten 14 Tage. (Ergebnisse der Online Defragmentierung von Exchange:

$WMIDATEAGE = [System.Management.ManagementDateTimeConverter]::ToDmtfDateTime `
   ([DateTime]::UtcNow.AddDays(-14))
Get-WmiObject -computer SRV01 ´
    -query ("Select * from Win32_NTLogEvent Where Logfile='Application' and 
               Eventcode = '1221' and TimeWritten >='" + $WMIDATEAGE + "'") |
	foreach-object{
		write-host $_.message
	}

Aber man muss sich gar nicht auf die Tiefen von WMI begeben. Mit dem PowerShell Commandlets "Get-Eventlog" bzw. "Get-WinEvent" lässt sich jedes Eventlog einfach auslesen. Denken Sie aber daran, auch hier mit Filtern zu arbeiten.

Für dein Einsatz unter PRTG wollte ich einfach die Anzahl der "Error-Messages" in den letzten X Minuten haben. Hier am Beispiel eines FIM-Servers, wie er bei Office 365 zum Einsatz kommt.

Get-EventLog `
   -LogName application `
   -After (get-date).addminutes(-15) `
   -EntryType error `
   -Source "directory synchronization"

Aber schon beim Test habe ich gesehen, dass die Ergebnisse sehr schnell kommen aber dann lange "Pause" ist. Das liegt wohl daran, dass "Get-Eventlog" auch mit der Angabe eines Filters und selbst bei Angabe von "After" dennoch das komplette Eventlog absucht. Besser ist das mit WMI bzw. Get-WinEvent

$starttime = (get-date).AddMinutes(-15)
$count=(get-winevent -FilterHashTable @{LogName='Application'; Level=3; StartTime=$starttime} -ErrorAction silentlycontinue).count

Hierbei werden sehr viel schneller die Meldungen zurück gegeben und können auch noch umfangreicher gefiltert werden. Sogar Platzhalter wie "*" funktionieren. Die ErrorAction muss dazu, da Get-WinEvent ansonsten einen sichtbaren Fehler "Get-WinEvent : No events were found that match the specified selection criteria" erzeugt. Die Level haben folgende Bedeutung:

Level 2 = Error
Level 3 = Warning
Level 4= Information

Eventlogs schreiben (NET)

In PowerShell 2.0 ganz es noch kein eigenes Commandlet aber mittelt .NET-Klassen war es schon da nicht schwer einen Eventlogeintrage zu erzeugen:

# Initialize Eventlog für reporting and Debugging
$evt=new-object System.Diagnostics.EventLog("Application")
$evt.Source="MSXFAQ"
$infoevent=[System.Diagnostics.EventLogEntryType]::Information
$warnevent=[System.Diagnostics.EventLogEntryType]::Warning
$errorevent=[System.Diagnostics.EventLogEntryType]::Error

$evt.WriteEntry("SKRIPT gestartet",$infoevent,0)

Dieser Codeschnipsel legt im Application-Log eine neue Quelle "MSXFAQ" an, damit ich im letzten Schritt dann einen Eintrag anlegen kann.

Mit New-Eventlog und "Write-Eventlog", welches aber eine "Source" benötigt, die zuerst zu definieren ist, geht das "lesbarer" aber nicht wirklich kürzer. Da ein "New-Eventlog" aber administrative Rechte braucht, muss man das Skript einmalig als Admin starten oder die Zeile von Hand ausführen. Damit ich später im Skript keine Fehler bekomme, habe ich daher das New-Eventlog in eine IF-Anfrage eingebaut.

# Source bei Bedarf definieren
if (![System.Diagnostics.EventLog]::SourceExists("msxfaq")) {
    new-eventlog -logname 'Application' -Source 'msxfaq'
}

# Eventlogeintrag erzeugen
write-eventlog `
   -entrytype "Information" `
   -logname "Application" `
   -eventID 1 `
   -Source 'msxfaq' `
   -Category 0 `
   -Message "Meldung"

Neben der EventID und dem EntryType können Sie auch noch eine Category übergeben. Folgende Werte sind möglich:

Wert Bedeutung Wert Bedeutung

0

None

8

Undefiniert wird als (8) angezeigt

1

Devices

9

Undefiniert wird als (9) angezeigt

2

Disk

10

Undefiniert wird als (10) angezeigt

3

Printers

11

Undefiniert wird als (11) angezeigt

4

Services

12

Undefiniert wird als (12) angezeigt

5

Shell

13

Undefiniert wird als (13) angezeigt

6

System Event

14

Undefiniert wird als (14) angezeigt

7

Network

15

Undefiniert wird als (15) angezeigt

Wer also heute ein PowerShell-Skript schreibt und die Ausgabe von Fehlern oder Status als Eventlog vergisst, sollte sich mal fragen wie er all die schönen Skripte überwachen möchte wenn diese später automatisch laufen.

Wer statt eines Eventlogs die Meldungen an einen Syslog Server senden will, findet auf Syslog entsprechende Informationen.

Auf Events warten

Interessant ist aber auch die Funktion, einfach auf Events zu warten und die dann automatisch zu verarbeiten. Auch wenn dies mit PowerShell 2.0 noch einfacher geht (Register-WMIEvent  auf http://www.microsoft.com/technet/scriptcenter/topics/winpsh/events.mspx), ist auch PowerShell 1.0 schon sehr einfach dazu zu verwenden. Hier ein Beispiel:

$eventwatcher = new-object system.management.ManagementEventWatcher
$eventwatcher.query = "Select * From __InstanceCreationEvent Where TargetInstance ISA 'Win32_NTLogEvent'"
$event = $eventwatcher.WaitForNextEvent()
$event.targetinstance

Das Skript in einer PowerShell-Box gestartet, liefert den nächsten Event, der ein einem beliebigen Eventlog des lokalen Servers auftritt.

Mit so wenig Code kann man natürlich schöne weitere Funktionen bauen.

Weitere Links