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:

CMDlet Get-WMI Get-Eventlog Get-WinEvent
Mindestversion

Seit PowerShell 2.0

Seit PowerShell 2.0

Windows 8 oder höher

Remote

Ja

Parameter - Computername

Parameter "-computer"

Filter

Flexibel oder nachverarbeiten über Pipeline

Einfache Syntax per Parameter

FilterXPath auch Remote, was schneller über WAN ist.

Performance

Schnell

Langsamer

Schnell

So erhalten Sie von allen Exchange Servern in ihrer Topologie alle entsprechenden Events. Sie können natürlich mit Get-WinEvent noch weiter filtern (z.B. Start/Endezeit) und die Ausgaben weiter verarbeiten.

Eventlog und Windows 2008/Vista
Das Script funktioniert leider nicht auf Windows 2008 oder Vista sondern nur auf Windows 2008R2 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

WMI: Events suchen und filtern

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.

Get-Eventlog: 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

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

Suchen mit WinEvent

Das Commandlet Get-WinEvent ist meiner Erfahrung nach noch etwas schneller und über die XPATH-Syntax leistungsfähiger.

#Sample Suche nach Fehlern 
Get-WinEvent -FilterHashtable @{logname="Security";level=3;StartTime="01.07.2016";Endtime="11/08/2017" } 

#Example Suche nach 9646 Events
get-winevent `
   -FilterHashTable @{LogName = "Application"; ID = 9646; Providername="MSExchangeIS"}} `
   -ComputerName $server

Natürlich kann man auch mehrere Server parallel abfragen, z.B. so:

# Einlesen der Servernamen aus einer Datei. Koennte auch eine AD-Abfrage u.a. sein
$serverlist = gc .\server.txt

# Serverliste an Invoke-Command uebergeben
$serverliste `
| %{`
   Invoke-Command `
      -ComputerName $_  `
      -Command {get-winevent -FilterHashTable @{LogName = "System"; ID = 41; Providername="Microsoft-Windows-Kernel-Power";StartTime="11/07/2017";Endtime="11/08/2017"}}  `
      -asjob
}
# Auf den Abschluss aller Jobs warten
Get-job | wait-job
# Ergebnisse einsammeln
$result=get-job | Receive-Job
# Jobs bereinigen 
get-job | remove-job
# Ergebnisse sortiert ausgeben
$result | sort TimeCreated| ft PSComputername,timecreated -autosize

PSComputerName             TimeCreated
--------------             -----------
DC2012.msxfaq.net  10/3/2016 5:55:08 PM
EX2016.msxfaq.net  10/3/2016 5:57:09 PM
SfB2015.msxfaq.net 10/3/2016 6:25:39 PM

Solche Schleifen sind auch sehr effektiv, um über mehrere Server bestimmte Events zu ermitteln, z.B. Datenbank-Failover bei Exchange oder SQL oder Skype for Business Fabric Fehler. Man bekommt dann die Events aller Server zeitlich in einer Liste.

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