Ottieni in modo efficiente i log degli eventi con PowerShell Get-EventLog

Ogni amministratore di sistema Windows è probabilmente familiare con il Registro eventi di Windows. Utilizzando questo cmdlet in PowerShell, i sysadmin possono analizzare molti eventi contemporaneamente su molti computer. Libera i sysadmin dal fare clic in giro nell’Event Viewer cercando di capire il filtro giusto da utilizzare e di determinare dove precisamente è archiviato quell’evento critico. Tuttavia, `Get-EventLog` ha i suoi svantaggi, come vedrai.

Elenca i registri eventi con Get-EventLog

Il cmdlet `Get-EventLog` è disponibile su tutte le versioni moderne di Windows PowerShell. Nel suo utilizzo più diretto, questo cmdlet richiede un registro eventi da interrogare, visualizzando poi tutti gli eventi in quel registro eventi.

Ma cosa succede se non conosci il nome del registro eventi in primo luogo? In tal caso, dobbiamo individuare tutti i registri eventi disponibili nel nostro computer locale. Lo facciamo utilizzando il comando `Get-EventLog -List`.

Get-EventLog -List

Puoi vedere che ho alcuni registri eventi sul mio sistema locale, ma potresti chiederti: dove sono gli altri? Ci sono dozzine di altri registri eventi che appaiono sotto Registri applicazioni e servizi in Event Viewer. Perché non sono qui?

Se hai bisogno di quegli eventi, sfortunatamente, `Get-EventLog` non funzionerà. Invece, dovrai dare un’occhiata a Get-WinEvent. Il cmdlet `Get-EventLog` potrebbe essere considerato un cmdlet legacy a questo punto, ma è uno che uso ancora frequentemente semplicemente perché è così facile da usare.

Interrogare gli eventi con Get-EventLog

Ora che conosciamo tutti i registri degli eventi disponibili, possiamo leggere gli eventi all’interno di quel registro degli eventi. Forse voglio vedere tutti gli eventi nel registro degli eventi dell’Applicazione. Per ottenere quegli eventi, devo specificare il parametro LogName con Get-EventLog e il cmdlet restituirà tutti gli eventi in quel registro degli eventi.

Get-EventLog -LogName Application

Per impostazione predefinita, vedrai solo sei proprietà in output:

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

In realtà, Get-EventLog ne restituisce 16. Il motivo per cui ne vedi solo sei è dovuto alle regole di formattazione di PowerShell che definiscono l’output. Di seguito è riportato un esempio dell’output effettivo trovato collegando Get-EventLog a Select-Object e selezionando tutte le proprietà.

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

Filtraggio con Get-EventLog

Probabilmente, quando cerchiamo eventi, non abbiamo bisogno di tutti gli eventi. Invece, ne abbiamo bisogno solo di alcuni. In tal caso, è necessario filtrare eventi specifici. Get-EventLog ha alcuni modi diversi per farlo. Il cmdlet Get-EventLog può filtrare in base al timestamp, al tipo di voce, all’ID dell’evento, al messaggio, alla sorgente e al nome utente. Questo si occupa della maggior parte dei modi per trovare eventi.

Per dimostrare il filtraggio, forse sto interrogando gli eventi ogni tanto e voglio trovare i dieci eventi più recenti. In tal caso, posso utilizzare il parametro Newest e specificare quanti eventi desidero visualizzare. Get-EventLog -LogName Application -Newest 10 restituirà solo gli ultimi dieci eventi.

Forse voglio trovare tutti gli eventi successivi a un determinato momento. Per questo, abbiamo il parametro After. Il parametro After richiede una data/ora, quindi se voglio trovare solo gli eventi nel log dell’applicazione che sono avvenuti dopo il 26/01/19 alle 10:17, potrei fare così Get-EventLog -LogName Application -After '26/01/19 10:17'. Possiamo anche eseguire lo stesso processo, ma selezionare eventi che sono avvenuti prima di una certa data con, avrai indovinato, il parametro Before.

Il comando Get-EventLog ha molti modi diversi per filtrare, non includendo il filtro basato su un timestamp. Possiamo filtrare gli eventi anche in base ad altri attributi come l’ID dell’evento (ID istanza) e il messaggio, che tendono ad essere attributi comuni da cercare. Forse so che sto cercando un evento con un ID di 916; passeremmo 916 al parametro InstanceId.

PS> Get-EventLog -LogName Application -InstanceId 916

Possiamo anche combinare i filtri. Forse ottengo molti eventi restituiti con un ID di 916, ma voglio quegli eventi con la stringa svchost nel messaggio. In tal caso, possiamo aggiungere il parametro Message a Get-EventLog e specificare un carattere jolly come svchost.

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

Script Bonus!

Hai bisogno di un ottimo esempio sull’utilizzo di Get-EventLog in uno script del mondo reale? In tal caso, sei fortunato! Di seguito è riportato un caso d’uso avanzato di Get-EventLog che puoi scaricare e utilizzare oggi!

<#
.SYNOPSIS
    Questo script cerca su un computer Windows tutte le voci di registro degli eventi o le voci di registro di testo
    tra un orario di inizio e un orario di fine specificati.
.DESCRIPTION
    Questo script elenca tutti i log degli eventi entro un determinato intervallo di tempo e tutti i log di testo
    che hanno un'ultima data di scrittura nell'intervallo di tempo o contengono una riga che ha un riferimento data/ora
    nell'intervallo di tempo. Registra queste informazioni in un set di file e copia
    qualsiasi file di log interessante per ulteriori analisi.
.EXAMPLE
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME
 
    Questo esempio trova tutte le voci di registro degli eventi tra StartTimestamp e EndTimestamp e qualsiasi file
    con un'estensione all'interno del parametro $FileExtension che è stato scritto per l'ultima volta nell'intervallo di tempo
    o contiene una riga con un riferimento data/ora nell'intervallo di tempo.
.PARAMETER StartTimestamp
    La data/ora più precoce da cui iniziare la ricerca degli eventi.
.PARAMETER EndTimestamp
    La data/ora più recente da cui iniziare la ricerca degli eventi.
.PARAMETER Computername
    Il nome del computer remoto (o locale) su cui si desidera effettuare la ricerca.
.PARAMETER OutputFolderPath
    Il percorso della cartella che conterrà i file di testo che conterranno gli eventi.
.PARAMETER LogAuditFilPath
    Il percorso del file di testo che documenterà il file di log, il numero di riga e il tipo di corrispondenza
    per i log che sono stati trovati.
.PARAMETER EventLogsOnly
    Utilizzare questo parametro switch se si desidera cercare solo nei log degli eventi.
.PARAMETER LogFilesOnly
    Utilizzare questo parametro se si desidera cercare solo i log nel file system.
.PARAMETER ExcludeDirectory
    Se si sta cercando nel file system, specificare eventuali percorsi di cartelle che si desidera ignorare.
.PARAMETER FileExtension
    Specificare uno o più insiemi di estensioni di file separate da virgola che si desidera cercare nel file system.
    Questo valore predefinito per le estensioni 'log,txt,wer'.
#>
[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) {
 ## Crea la directory locale in cui archiviare i file di log che corrispondono ai criteri
 $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 {
 
    ## Esegui solo se l'utente desidera trovare voci di registro degli eventi
    if (!$LogFilesOnly.IsPresent) {
 ## Trova tutti i nomi dei log degli eventi che contengono almeno 1 evento
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## Trova tutti gli eventi in tutti i log degli eventi che sono compresi tra gli orari di inizio e fine
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## Converti le proprietà in qualcosa di più leggibile ed unisci ogni evento nel file di testo del log degli eventi
 $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
 }
 
 ## Esegui solo se l'utente desidera trovare file di log
 if (!$EventLogsOnly.IsPresent) {
        ## Enumera tutte le condivisioni amministrative remote sul computer remoto. Faccio questo anziché enumerare tutte le unità fisiche perché
        ## l'unità potrebbe essere presente ma la condivisione potrebbe non esserci, il che significa che non posso accedere all'unità comunque.
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## Assicurati di poter accedere a tutte le condivisioni amministrative remote
 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
 }
 ## Aggiungi eventuali directory specificate nel parametro $ExcludeDirectory per non cercare file di log in
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## Crea la stringa di regex folle che uso per cercare diversi formati di data/ora.
 ## Questo viene utilizzato nel tentativo di cercare stringhe di data/ora in ogni file di testo trovato
 ##TODO: Aggiungere la capacità di corrispondere a Jan,Feb,Mar,etc
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## Enumera tutti i file corrispondenti ai parametri di query che hanno contenuto
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## Registra il file se l'ultima data di scrittura è nell'intervallo di tempo. Questo trova file di log che potrebbero non registrare un 
 ## timestamp data/ora ma potrebbero comunque essere coinvolti nell'evento che l'utente sta cercando.
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## Se il file trovato corrisponde all'insieme di estensioni che sto cercando ed è effettivamente un file di testo semplice.
 ## Utilizzo Get-Content per verificare solo che sia un testo semplice prima di analizzarlo.
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## Controlla il contenuto del file di testo per riferimenti a date nell'intervallo di tempo
 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)'"
 ## Registra tutte le righe corrispondenti nel file di audit.
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## Crea lo stesso percorso del file di log sul computer remoto all'interno della directory di log di output e 
 ## copia il file di log con un evento nell'intervallo di tempo in quel percorso.
 ## Questo non funzionerà se viene raggiunto un percorso di file superiore a 255 caratteri.
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##TODO: Rimuovere l'azione dell'errore quando il supporto per i percorsi lunghi viene implementato
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

Sommario

Il cmdlet Get-EventLog è un ottimo comando da utilizzare se ti trovi mai nella necessità di interrogare rapidamente uno dei registri eventi comuni. È facile da usare e fornisce alcune capacità di filtraggio di base. Tuttavia, se hai bisogno di fare qualche investigazione approfondita sul registro eventi, probabilmente il comando Get-WinEvent funzionerà meglio, ma è un po’ più difficile da usare e a volte richiede la conoscenza di sintassi come XPath.

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