Abfrage von Ereignisprotokollen effizient mit PowerShell Get-EventLog

Jeder Windows-Systemadministrator ist wahrscheinlich mit dem Windows-Ereignisprotokoll vertraut. Die Verwendung dieses Cmdlets in PowerShell ermöglicht es Systemadministratoren, viele Ereignisse auf einmal über mehrere Computer hinweg zu analysieren. Es befreit die Systemadministratoren davon, in der Ereignisanzeige herumzuklicken, um den richtigen Filter zu finden und zu bestimmen, wo genau dieses kritische Ereignis gespeichert ist. Allerdings hat Get-EventLog seine Nachteile, wie Sie sehen werden.

Auflisten von Ereignisprotokollen mit Get-EventLog

Das Cmdlet Get-EventLog ist in allen modernen Versionen von Windows PowerShell verfügbar. Bei seiner einfachsten Verwendung benötigt dieses Cmdlet ein Ereignisprotokoll, das abgefragt werden soll, und es werden dann alle Ereignisse in diesem Ereignisprotokoll angezeigt.

Aber was ist, wenn Sie den Namen des Ereignisprotokolls nicht kennen? In diesem Fall müssen wir alle verfügbaren Ereignisprotokolle auf unserem lokalen Computer herausfinden. Das tun wir, indem wir den Befehl Get-EventLog -List verwenden.

Get-EventLog -List

Sie können sehen, dass ich jetzt ein paar Ereignisprotokolle auf meinem lokalen System habe, aber Sie fragen sich vielleicht, wo die anderen sind? Es gibt dutzende andere Ereignisprotokolle, die unter „Anwendungen und Dienste-Protokolle“ in der Ereignisanzeige angezeigt werden. Warum sind sie hier nicht?

Falls Sie diese Ereignisse benötigen, funktioniert Get-EventLog leider nicht. Stattdessen müssen Sie sich Get-WinEvent ansehen. Das Cmdlet Get-EventLog könnte als veraltetes Cmdlet betrachtet werden, aber ich verwende es immer noch häufig, einfach weil es so einfach zu bedienen ist.

Abfragen von Ereignissen mit Get-EventLog

Nun, da wir alle verfügbaren Ereignisprotokolle kennen, können wir nun Ereignisse innerhalb dieses Ereignisprotokolls lesen. Vielleicht möchte ich alle Ereignisse im Anwendungsereignisprotokoll sehen. Um diese Ereignisse zu erhalten, muss ich den LogName-Parameter mit Get-EventLog angeben und das Cmdlet wird durch das Zurückgeben aller Ereignisse in diesem Ereignisprotokoll nachkommen.

Get-EventLog -LogName Application

Standardmäßig sehen Sie nur sechs Eigenschaften in der Ausgabe:

  • Index
  • Time
  • EntryType
  • Source
  • InstanceId
  • Message

Tatsächlich gibt Get-EventLog 16 von ihnen zurück. Der Grund, warum Sie nur sechs sehen, liegt an den PowerShell-Formatierungsregeln, die die Ausgabe definieren. Unten finden Sie ein Beispiel für die tatsächliche Ausgabe, die durch das Umleiten von Get-EventLog zu Select-Object und Auswahl aller Eigenschaften gefunden wurde.

Get-EventLog -LogName Application | Select-Object -Property * EventID

Filtern mit Get-EventLog

Es besteht die Wahrscheinlichkeit, dass wir beim Suchen nach Ereignissen nicht alle Ereignisse benötigen. Stattdessen benötigen wir nur wenige. In diesem Fall müssen wir nach bestimmten Ereignissen filtern. Get-EventLog bietet verschiedene Möglichkeiten, dies zu tun. Das Cmdlet kann nach Zeitstempel, Eintragstyp, Ereignis-ID, Nachricht, Quelle und Benutzername filtern. Dies deckt die meisten Möglichkeiten zur Suche nach Ereignissen ab.

Um Filterung zu demonstrieren, frage ich vielleicht regelmäßig nach Ereignissen und möchte die zehn neuesten finden. In diesem Fall kann ich den Newest-Parameter verwenden und angeben, wie viele Ereignisse ich sehen möchte. Get-EventLog -LogName Application -Newest 10 gibt nur die letzten zehn Ereignisse zurück.

Vielleicht möchte ich alle Ereignisse nach einem bestimmten Zeitpunkt finden. Dafür haben wir den After-Parameter. Der After-Parameter nimmt ein Datum/Uhrzeit an, also wenn ich nur die Ereignisse im Anwendungsprotokoll finden möchte, die nach dem 26.01.19 10:17 Uhr stattgefunden haben, könnte ich dies tun Get-EventLog -LogName Application -After '26.01.19 10:17'. Wir könnten auch denselben Prozess durchführen, aber Ereignisse auswählen, die vor einem bestimmten Datum passiert sind, mit, Sie haben es vielleicht erraten, dem Before-Parameter.

Das Get-EventLog hat viele verschiedene Möglichkeiten, zu filtern, die nicht auf einem Zeitstempel basieren. Wir können Ereignisse auch basierend auf anderen Attributen wie der Ereignis-ID (Instanz-ID) und der Meldung filtern, die oft übliche Attribute zum Suchen sind. Vielleicht weiß ich, dass ich nach einem Ereignis mit der ID 916 suche; wir übergeben 916 dem InstanceId-Parameter.

PS> Get-EventLog -LogName Application -InstanceId 916

Wir können auch Filter kombinieren. Vielleicht erhalte ich viele Ereignisse mit einer ID von 916 zurück, aber ich möchte jene Ereignisse mit dem String svchost in der Meldung. In diesem Fall können wir den Message-Parameter zu Get-EventLog hinzufügen und einen Platzhalter wie svchost angeben.

PS> Get-EventLog -LogName Application -InstanceId 916 -Message '*svchost*'

Bonus Skript!

Brauchen Sie ein großartiges Beispiel für die Verwendung von Get-EventLog in einem realen Skript? Wenn ja, haben Sie Glück! Unten finden Sie einen fortgeschrittenen Anwendungsfall von \verb|Get-EventLog|, den Sie heute herunterladen und verwenden können!

<#
.BESCHREIBUNG
    Dieses Skript sucht auf einem Windows-Computer nach allen Ereignisprotokoll- oder Textprotokolleinträgen
    zwischen einer angegebenen Start- und Endzeit.
.BESCHREIBUNG
    Dieses Skript enumeriert alle Ereignisprotokolle innerhalb eines angegebenen Zeitrahmens und alle Textprotokolle,
    die entweder innerhalb des Zeitrahmens zuletzt geschrieben wurden oder eine Zeile enthalten, die einen Datum/Uhrzeitverweis
    innerhalb des Zeitrahmens aufweist. Diese Informationen werden in eine Reihe von Dateien aufgezeichnet und 
    jede interessante Protokolldatei zur weiteren Analyse kopiert.
.BEISPIEL
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME

    Dieses Beispiel findet alle Ereignisprotokolleinträge zwischen Startzeitstempel und Endzeitstempel und alle Dateien
    mit einer Erweiterung innerhalb des $FileExtension-Parameters, die entweder innerhalb des Zeitrahmens zuletzt geschrieben wurden
    oder eine Zeile mit einem Datum/Uhrzeitverweis innerhalb des Zeitrahmens enthalten.
.PARAMETER StartTimestamp
    Das früheste Datum/Uhrzeit, ab dem Sie nach Ereignissen suchen möchten.
.PARAMETER EndTimestamp
    Das späteste Datum/Uhrzeit, ab dem Sie nach Ereignissen suchen möchten.
.PARAMETER Computername
    Der Name des Remote-(oder lokalen) Computers, auf dem Sie suchen möchten.
.PARAMETER OutputFolderPath
    Der Pfad des Ordners, der die Textdateien enthält, die die Ereignisse enthalten werden.
.PARAMETER LogAuditFilPath
    Der Pfad zur Textdatei, die das Protokoll, die Zeilennummer und den Übereinstimmungstyp dokumentiert,
    zu den Protokollen, die übereinstimmen.
.PARAMETER EventLogsOnly
    Verwenden Sie diesen Schalterparameter, wenn Sie nur in den Ereignisprotokollen suchen möchten.
.PARAMETER LogFilesOnly
    Verwenden Sie diesen Parameter, wenn Sie nur nach Protokollen im Dateisystem suchen möchten.
.PARAMETER ExcludeDirectory
    Wenn Sie im Dateisystem suchen, geben Sie die Ordnerpfade an, die Sie überspringen möchten.
.PARAMETER FileExtension
    Geben Sie eine oder mehrere kommagetrennte Dateierweiterungssets an, nach denen Sie im Dateisystem suchen möchten.
    Dies ist standardmäßig auf Erweiterungen 'log,txt,wer' eingestellt.
#>
[CmdletBinding(DefaultParameterSetName = 'Neither')]
param (
    [Parameter(Mandatory)]
    [datetime]$StartTimestamp,
    [Parameter(Mandatory)]
    [datetime]$EndTimestamp,
    [Parameter(ValueFromPipeline,
        ValueFromPipelineByPropertyName)]
    [string]$ComputerName = 'localhost',
    [Parameter()]
 [string]$OutputFolderPath = ".\$Computername",
 [Parameter(ParameterSetName = 'LogFiles')]
 [string]$LogAuditFilePath = "$OutputFolderPath\LogActivity.csv",
 [Parameter(ParameterSetName = 'EventLogs')]
 [switch]$EventLogsOnly,
    [Parameter(ParameterSetName = 'LogFiles')]
    [switch]$LogFilesOnly,
    [Parameter(ParameterSetName = 'LogFiles')]
 [string[]]$ExcludeDirectory,
 [Parameter(ParameterSetName = 'LogFiles')]
 [string[]]$FileExtension = @('log', 'txt', 'wer')
)
 
begin {
 if (!$EventLogsOnly.IsPresent) {
 ## Erstellen Sie das lokale Verzeichnis, in dem alle übereinstimmenden Protokolldateien gespeichert werden sollen
 $LogsFolderPath = "$OutputFolderPath\logs"
 if (!(Test-Path $LogsFolderPath)) {
 mkdir $LogsFolderPath | Out-Null
 }
 }
 
 function Add-ToLog($FilePath,$LineText,$LineNumber,$MatchType) {
 $Audit = @{
 'FilePath' = $FilePath;
 'LineText' = $LineText
 'LineNumber' = $LineNumber
 'MatchType' = $MatchType
 }
 [pscustomobject]$Audit | Export-Csv -Path $LogAuditFilePath -Append -NoTypeInformation
 }
}
 
process {
 
    ## Ausführen, nur wenn der Benutzer Ereignisprotokolleinträge finden möchte
    if (!$LogFilesOnly.IsPresent) {
 ## Finde alle Ereignisprotokollnamen, die mindestens 1 Ereignis enthalten
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## Finde alle Ereignisse in allen Ereignisprotokollen, die zwischen den Start- und Endzeitstempeln liegen
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## Konvertiere die Eigenschaften in etwas Benutzerfreundlicheres und füge jedes Ereignis in die Ereignisprotokolltextdatei ein
 $LogProps = @{ }
 [System.Collections.ArrayList]$MyEvents = @()
 foreach ($Event in $Events) {
 $LogProps.Time = $Event.TimeCreated
 $LogProps.Source = $Event.ProviderName
 $LogProps.EventId = $Event.Id
 if ($Event.Message) {
 $LogProps.Message = $Event.Message.Replace("<code>n", '|').Replace("</code>r", '|')
 }
 $LogProps.EventLog = $Event.LogName
 $MyEvents.Add([pscustomobject]$LogProps) | Out-Null
 }
 $MyEvents | sort Time | Export-Csv -Path "$OutputFolderPath\eventlogs.txt" -Append -NoTypeInformation
 }
 
 ## Ausführen, nur wenn der Benutzer Protokolldateien finden möchte
 if (!$EventLogsOnly.IsPresent) {
        ## Enumeriere alle Remote-Adminfreigaben auf dem Remote-Computer. Ich mache das anstelle des Enumerierens aller physischen Laufwerke, weil
        ## das Laufwerk möglicherweise vorhanden ist und die Freigabe nicht, was bedeutet, dass ich ohnehin nicht auf das Laufwerk zugreifen kann.
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## Stellen Sie sicher, dass ich auf alle Remote-Adminfreigaben zugreifen kann
 foreach ($Share in $Shares) {
 $Share = "\\$ComputerName\$($Share.Name)"
 if (!(Test-Path $Share)) {
 Write-Warning "Unable to access the '$Share' share on '$Computername'"
 } else {
 $AccessibleShares.Add($Share) | Out-Null 
 }
 }
 
 $AllFilesQueryParams = @{
 Path = $AccessibleShares
 Recurse = $true
 Force = $true
 ErrorAction = 'SilentlyContinue'
 File = $true
 }
 ## Fügen Sie alle im $ExcludeDirectory-Parameter angegebenen Verzeichnisse hinzu, um keine Protokolldateien zu suchen.
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## Erstellen Sie den komplexen Regex-String, den ich verwende, um nach verschiedenen Datums-/Zeitformaten zu suchen.
 ## Dies wird verwendet, um in jedem gefundenen Textdokument nach Datums-/Zeitzeichenfolgen zu suchen
 ##TODO: Fügen Sie die Möglichkeit hinzu, nach Jan, Feb, Mär usw. zu suchen
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## Enumeriere alle Dateien, die den Abfrageparametern entsprechen und Inhalte haben
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## Protokollieren Sie die Datei, wenn die letzte Schreibzeit innerhalb des Zeitrahmens liegt. Dies findet Protokolldateien, die möglicherweise keine aufzeichnen
 ## Datums-/Uhrzeitstempel, aber trotzdem an dem Ereignis beteiligt sein können, das der Benutzer zu finden versucht.
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## Wenn die gefundene Datei dem Satz von Erweiterungen entspricht, die ich zu finden versuche, und es sich tatsächlich um eine einfache Textdatei handelt.
 ## Ich verwende Get-Content, um nur sicherzustellen, dass es sich um reinen Text handelt, bevor ich es durchsuche.
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## Überprüfen Sie den Inhalt der Textdatei auf Verweise auf Daten im Zeitrahmen
 Write-Verbose "Checking log file '$($_.Name)' for date/time match in contents"
 $LineMatches = Select-String -Path $_.FullName -Pattern $DateTimeRegex
 if ($LineMatches) {
 Write-Verbose "Date/time match found in file '$($_.FullName)'"
 ## Protokollieren Sie alle übereinstimmenden Zeilen in der Prüfdatei.
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## Erstellen Sie denselben Pfad zur Protokolldatei auf dem Remote-Computer innerhalb des Ausgabeprotokollverzeichnisses und
 ## kopieren Sie die Protokolldatei mit einem Ereignis im Zeitrahmen zu diesem Pfad.
 ## Dies funktioniert nicht, wenn ein Dateipfad über 255 Zeichen erreicht wird.
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##TODO: Entfernen Sie die Fehleraktion, wenn die Unterstützung für lange Pfade implementiert ist
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

Zusammenfassung

Der Get-EventLog-Befehl ist ein großartiger Befehl, wenn Sie sich jemals in der Situation befinden, schnell eine der gängigen Ereignisprotokolle abfragen zu müssen. Er ist einfach zu verwenden und bietet einige grundlegende Filtermöglichkeiten. Wenn Sie jedoch eine gründliche Untersuchung der Ereignisprotokolle durchführen müssen, wird wahrscheinlich der Befehl Get-WinEvent besser funktionieren, aber er ist etwas schwieriger zu verwenden und erfordert manchmal das Kennen von Syntax wie XPath.

Source:
https://adamtheautomator.com/get-eventlog/