Query Event Logs Efficiënt met PowerShell Get-EventLog

Iedere Windows-systeembeheerder is waarschijnlijk bekend met het Windows Event Log. Het gebruik van deze cmdlet in PowerShell stelt systeembeheerders in staat om in één keer veel gebeurtenissen over meerdere computers te analyseren. Het bevrijdt systeembeheerders van het klikken in de Event Viewer om precies de juiste filter te vinden en te bepalen waar precies die kritieke gebeurtenis is opgeslagen.

Lijst van gebeurtenislogboeken met Get-EventLog

De cmdlet Get-EventLog is beschikbaar op alle moderne versies van Windows PowerShell. In zijn meest eenvoudige gebruik heeft deze cmdlet een gebeurtenislogboek nodig om te bevragen, waarna het alle gebeurtenissen in dat gebeurtenislogboek weergeeft.

Maar wat als je de naam van het gebeurtenislogboek niet in de eerste plaats kent? In dat geval moeten we alle gebeurtenislogboeken achterhalen die beschikbaar zijn op onze lokale computer. We doen dat door de opdracht Get-EventLog -List te gebruiken.

Get-EventLog -List

Je ziet dat ik nu een paar gebeurtenislogboeken op mijn lokale systeem heb, maar je vraagt je misschien af waar de anderen zijn? Tientallen andere gebeurtenislogboeken verschijnen onder Toepassingen en Services-logs in de Event Viewer. Waarom staan ze hier niet?

Als je die gebeurtenissen nodig hebt, gaat Get-EventLog helaas niet werken. In plaats daarvan moet je Get-WinEvent bekijken. De cmdlet Get-EventLog kan op dit punt worden beschouwd als een legacy-cmdlet, maar ik gebruik het nog steeds vaak omdat het gewoon zo eenvoudig is om te gebruiken.

Gebeurtenissen bevragen met Get-EventLog

Nu we alle gebeurtenislogs kennen die beschikbaar zijn, kunnen we gebeurtenissen binnen dat gebeurtenislogboek lezen. Misschien wil ik alle gebeurtenissen in het toepassingsgebeurtenislogboek zien. Om die gebeurtenissen te krijgen, moet ik de parameter LogName specificeren met Get-EventLog en de cmdlet zal gehoorzamen door alle gebeurtenissen in dat gebeurtenislogboek terug te geven.

Get-EventLog -LogName Application

Standaard zie je slechts zes eigenschappen in de uitvoer:

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

In werkelijkheid geeft Get-EventLog er 16 terug. De reden dat je er slechts zes ziet, is te wijten aan de opmaakregels van PowerShell die de uitvoer definiëren. Hieronder staat een voorbeeld van de werkelijke uitvoer die wordt gevonden door Get-EventLog te pijpen naar Select-Object en alle eigenschappen te selecteren.

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

Filteren met Get-EventLog

Waarschijnlijk hebben we bij het zoeken naar gebeurtenissen niet alle gebeurtenissen nodig. In plaats daarvan hebben we er maar een paar nodig. In dat geval moeten we filteren op specifieke gebeurtenissen. Get-EventLog heeft verschillende manieren om dit te doen. De cmdlet Get-EventLog kan filteren op tijdstempel, invoertype, gebeurtenis-ID, bericht, bron en gebruikersnaam. Dit dekt de meeste manieren om gebeurtenissen te vinden.

Om filtering te demonstreren, misschien vraag ik om de zoveel tijd naar gebeurtenissen, en wil ik de tien nieuwste gebeurtenissen vinden. In dat geval kan ik de Newest-parameter gebruiken en aangeven hoeveel gebeurtenissen ik wil zien. Get-EventLog -LogName Application -Newest 10 zal alleen de laatste tien gebeurtenissen teruggeven.

Misschien wil ik alle gebeurtenissen na een bepaald tijdstip vinden. Hiervoor hebben we de After-parameter. De After-parameter neemt een datum/tijd, dus als ik alleen de gebeurtenissen binnen het toepassingslogboek wil vinden die na 1/26/19 10:17 AM zijn gebeurd, zou ik dit kunnen doen Get-EventLog -LogName Application -After '1/26/19 10:17'. We kunnen ook hetzelfde proces uitvoeren maar gebeurtenissen selecteren die voor een bepaalde datum zijn gebeurd met, je had het misschien al geraden, de Before-parameter.

De Get-EventLog-opdracht heeft veel verschillende manieren om te filteren, exclusief op basis van een tijdstempel. We kunnen ook gebeurtenissen filteren op basis van andere attributen zoals gebeurtenis-ID (instantie-ID) en bericht, die vaak voorkomende attributen zijn om op te zoeken. Misschien weet ik dat ik op zoek ben naar een gebeurtenis met een ID van 916; dan zouden we 916 doorgeven aan de InstanceId-parameter.

PS> Get-EventLog -LogName Application -InstanceId 916

We kunnen ook filters combineren. Misschien krijg ik veel gebeurtenissen terug met een ID van 916, maar ik wil die gebeurtenissen met de string svchost in het bericht. In dat geval kunnen we de Message-parameter toevoegen aan Get-EventLog en een jokerteken zoals svchost specificeren.

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

Bonus Script!

Heb je een geweldig voorbeeld nodig van het gebruik van Get-EventLog in een script voor de echte wereld? Zo ja, dan heb je geluk! Hieronder staat een geavanceerd gebruiksscenario van Get-EventLog dat je vandaag kunt downloaden en gebruiken!

<#
.SYNOPSIS
    Deze script doorzoekt een Windows-computer naar alle gebeurtenislogboek- of tekstlogboekvermeldingen
    tussen een gespecificeerde start- en eindtijd.
.BESCHRIJVING
    Deze script doorloopt alle gebeurtenislogboeken binnen een gespecificeerd tijdsbestek en alle tekstlogboeken
    die een laatste schrijftijd hebben binnen het tijdsbestek of een regel bevatten die een datum/tijd
    referentie binnen het tijdsbestek heeft. Het registreert deze informatie naar een set bestanden en kopieert
    eventuele interessante logbestanden voor verder analyse.
.VOORBEELD
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME
 
    Dit voorbeeld vindt alle gebeurtenislogboekvermeldingen tussen StartTimestamp en EndTimestamp en elk bestand
    met een extensie binnen de $FileExtension-param dat ofwel het laatst is geschreven binnen het tijdsbestek
    of een regel bevat met een datum/tijdreferentie binnen het tijdsbestek.
.PARAMETER StartTimestamp
 De vroegste datum/tijd waarop u gebeurtenissen wilt beginnen te zoeken.
.PARAMETER EndTimestamp
 De laatste datum/tijd waarop u gebeurtenissen wilt beginnen te zoeken.
.PARAMETER Computername
 De naam van de externe (of lokale) computer waarop u wilt zoeken.
.PARAMETER OutputFolderPath
 Het pad van de map die de tekstbestanden zal bevatten met de gebeurtenissen.
.PARAMETER LogAuditFilPath
 Het pad naar het tekstbestand dat het logboekbestand, regelnummer en overeenkomsttype documenteert
 naar de logboeken die zijn gematcht.
.PARAMETER EventLogsOnly
 Gebruik deze schakelparameter als u alleen in de gebeurtenislogboeken wilt zoeken.
.PARAMETER LogFilesOnly
 Gebruik deze parameter als u alleen naar logbestanden op het bestandssysteem wilt zoeken.
.PARAMETER ExcludeDirectory
 Als u op het bestandssysteem zoekt, geef dan een lijst op van mappen die u wilt overslaan.
.PARAMETER FileExtension
 Specificeer een of meer door komma's gescheiden reeks bestandsextensies waarop u wilt zoeken op het bestandssysteem.
 Dit standaard naar 'log,txt,wer'-extensies.
#>
[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) {
 ## Maak de lokale map aan waarin alle overeenkomende logbestanden worden opgeslagen
 $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 {
 
    ## Uitvoeren alleen als de gebruiker gebeurtenislogboekvermeldingen wil vinden
    if (!$LogFilesOnly.IsPresent) {
 ## Vind alle gebeurtenislogboeknamen die minstens 1 gebeurtenis bevatten
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## Vind alle gebeurtenissen in alle gebeurtenislogboeken die zich tussen de start- en eindtijdstempels bevinden
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## Converteer de eigenschappen naar iets vriendelijker en voeg elke gebeurtenis toe aan het gebeurtenislogboektekstbestand
 $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
 }
 
 ## Uitvoeren alleen als de gebruiker logbestanden wil vinden
 if (!$EventLogsOnly.IsPresent) {
        ## Doorloop alle externe beheerdershares op de externe computer. Ik doe dit in plaats van alle fysieke stations op te sommen omdat
        ## het station misschien aanwezig is en de share misschien niet, wat betekent dat ik sowieso niet bij het station kan.
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## Zorg ervoor dat ik toegang heb tot alle externe beheerdershares
 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
 }
 ## Voeg eventuele mappen die zijn gespecificeerd in de $ExcludeDirectory-param toe om geen logbestanden in te zoeken
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## Maak de gekke regex-string die ik gebruik om te zoeken naar een aantal verschillende datum/tijd-indelingen.
 ## Dit wordt gebruikt in een poging om datum/tijdreeksen in elk gevonden tekstbestand te zoeken
 ##TODO: Voeg de mogelijkheid toe om te matchen op Jan,Feb,Mar,enz
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## Doorloop alle bestanden die voldoen aan de queryparameters en inhoud hebben
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## Registreer het bestand als de laatste schrijftijd binnen het tijdsbestek valt. Dit vindt logbestanden die mogelijk geen 
 ## datum/tijd-tijdstempel registreren maar mogelijk toch betrokken zijn bij welke gebeurtenis de gebruiker probeert te vinden.
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## Als het gevonden bestand overeenkomt met de reeks extensies die ik probeer te vinden en het is eigenlijk een plat tekstbestand.
 ## Ik gebruik Get-Content om te controleren of het echt een plat tekstbestand is voordat ik het doorloop.
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## Controleer de inhoud van het tekstbestand op verwijzingen naar datums in het tijdsbestek
 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)'"
 ## Registreer alle overeenkomende regels in het auditbestand.
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## Maak hetzelfde pad naar het logboekbestand op de externe computer binnen de uitvoerlogmap en
 ## kopieer het logboekbestand met een gebeurtenis binnen het tijdsbestek naar dat pad.
 ## Dit werkt niet als een bestandspad van meer dan 255 tekens wordt tegengekomen.
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##TODO: Verwijder de foutactie wanneer ondersteuning voor lange paden is geïmplementeerd
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

Samenvatting

De Get-EventLog cmdlet is een geweldig commando om te gebruiken als je jezelf ooit snel wilt kunnen richten op een van de veelvoorkomende gebeurtenislogs. Het is eenvoudig te gebruiken en biedt enige basisfiltermogelijkheden. Als je echter diepgaand onderzoek naar gebeurtenislogs moet doen, zal het Get-WinEvent-commando waarschijnlijk beter werken, maar het is wat moeilijker te gebruiken en vereist soms kennis van syntax zoals XPath.

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