Interroger les journaux d’événements efficacement avec PowerShell Get-EventLog

Chaque administrateur système Windows est probablement familier avec le journal des événements Windows. En utilisant cette commande dans PowerShell, les administrateurs système peuvent analyser de nombreux événements en une seule fois sur plusieurs ordinateurs. Cela libère les administrateurs système de cliquer partout dans l’Observateur d’événements pour trouver le bon filtre à utiliser et déterminer où cet événement critique est précisément stocké. Cependant, Get-EventLog a ses défauts que vous verrez.

Liste des journaux d’événements avec Get-EventLog

La commande Get-EventLog est disponible sur toutes les versions modernes de Windows PowerShell. Dans son utilisation la plus simple, cette commande a besoin d’un journal d’événements à interroger, qu’elle affichera ensuite tous les événements de ce journal d’événements.

Mais que faire si vous ne connaissez pas le nom du journal d’événements en premier lieu ? Dans ce cas, nous devons déterminer tous les journaux d’événements disponibles sur notre ordinateur local. Nous faisons cela en utilisant la commande Get-EventLog -List.

Get-EventLog -List

Vous pouvez voir que j’ai quelques journaux d’événements sur mon système local maintenant, mais vous vous demandez peut-être où sont les autres ? Des dizaines d’autres journaux d’événements apparaissent sous Applications et Services dans l’Observateur d’événements. Pourquoi ne sont-ils pas ici ?

Si vous avez besoin de ces événements, malheureusement, Get-EventLog ne fonctionnera pas. Au lieu de cela, vous devrez consulter Get-WinEvent. La commande Get-EventLog pourrait être considérée comme une commande héritée à ce stade, mais c’est une commande que j’utilise encore fréquemment simplement parce qu’elle est si facile à utiliser.

Interrogation des événements avec Get-EventLog

Maintenant que nous connaissons tous les journaux d’événements disponibles, nous pouvons maintenant lire les événements dans ce journal d’événements. Peut-être que je veux voir tous les événements dans le journal d’événements de l’Application. Pour obtenir ces événements, je dois spécifier le paramètre LogName avec Get-EventLog et le cmdlet s’oblige à renvoyer tous les événements dans ce journal d’événements.

Get-EventLog -LogName Application

Par défaut, vous ne verrez que six propriétés dans la sortie :

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

En réalité, Get-EventLog en renvoie 16. La raison pour laquelle vous n’en voyez que six est due aux règles de mise en forme de PowerShell qui définissent la sortie. Voici un exemple de la sortie réelle trouvée en redirigeant Get-EventLog vers Select-Object et en sélectionnant toutes les propriétés.

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

Filtrage avec Get-EventLog

Il est probable que lors de la recherche d’événements, nous n’ayons pas besoin de tous les événements. Au lieu de cela, nous n’avons besoin que de quelques-uns. Dans ce cas, nous devons filtrer pour des événements particuliers. Get-EventLog propose plusieurs façons de le faire. Le cmdlet Get-EventLog peut filtrer en fonction de l’horodatage, du type d’entrée, de l’ID d’événement, du message, de la source et du nom d’utilisateur. Cela couvre la plupart des façons de trouver des événements.

Pour démontrer le filtrage, peut-être que je fais des requêtes d’événements de temps en temps et que je veux trouver les dix événements les plus récents. Dans ce cas, je peux utiliser le paramètre Newest et spécifier le nombre d’événements que je souhaite voir. Get-EventLog -LogName Application -Newest 10 ne renverra que les dix derniers événements.

Peut-être que je veux trouver tous les événements après un moment particulier. Pour cela, nous avons le paramètre After. Le paramètre After prend une date/heure, donc si je veux trouver uniquement les événements dans le journal d’application qui se sont produits après le 26/01/19 à 10h17, je pourrais faire cela Get-EventLog -LogName Application -After '26/01/19 10:17'. Nous pourrions également effectuer le même processus mais sélectionner les événements qui se sont produits avant une certaine date avec, vous l’avez peut-être deviné, le paramètre Before.

Le Get-EventLog a beaucoup de façons différentes de filtrer, sans inclure la date et l’heure. Nous pouvons également filtrer les événements en fonction d’autres attributs tels que l’ID d’événement (ID d’instance) et le message qui sont des attributs courants à rechercher. Peut-être que je sais que je recherche un événement avec un ID de 916; nous passerions 916 au paramètre InstanceId.

PS> Get-EventLog -LogName Application -InstanceId 916

Nous pouvons également combiner des filtres. Peut-être que je reçois beaucoup d’événements renvoyés avec un ID de 916, mais je veux ces événements avec la chaîne svchost dans le message. Dans ce cas, nous pouvons ajouter le paramètre Message à Get-EventLog et spécifier un joker comme svchost.

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

Script Bonus!

Avez-vous besoin d’un excellent exemple d’utilisation de Get-EventLog dans un script du monde réel? Si oui, vous avez de la chance! Ci-dessous se trouve un cas d’utilisation avancé de Get-EventLog que vous pouvez télécharger et utiliser dès aujourd’hui!

.SYNOPSIS
    Ce script recherche sur un ordinateur Windows toutes les entrées du journal d'événements ou du journal texte
    entre une heure de début et une heure de fin spécifiées.
.DESCRIPTION
    Ce script énumère tous les journaux d'événements dans une plage horaire spécifiée et tous les journaux texte
    qui ont une heure d'écriture dans la plage horaire ou contiennent une ligne avec une date/heure
    de référence dans la plage horaire. Il enregistre ces informations dans un ensemble de fichiers et copie
    tout fichier journal intéressant pour une analyse ultérieure.
.EXAMPLE
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME
 
    Cet exemple recherche toutes les entrées du journal d'événements entre StartTimestamp et EndTimestamp et tout fichier
    avec une extension à l'intérieur du paramètre $FileExtension qui a été soit écrit dans la plage horaire
    ou contient une ligne avec une référence date/heure dans la plage horaire.
.PARAMETER StartTimestamp
 La date/heure la plus tôt à partir de laquelle vous souhaitez commencer la recherche d'événements.
.PARAMETER EndTimestamp
 La date/heure la plus récente à partir de laquelle vous souhaitez commencer la recherche d'événements.
.PARAMETER Computername
 Le nom de l'ordinateur distant (ou local) que vous souhaitez rechercher.
.PARAMETER OutputFolderPath
 Le chemin du dossier qui contiendra les fichiers texte contenant les événements.
.PARAMETER LogAuditFilPath
 Le chemin du fichier texte qui documentera le fichier journal, le numéro de ligne et le type de correspondance
 pour les journaux qui ont été trouvés.
.PARAMETER EventLogsOnly
 Utilisez ce commutateur si vous souhaitez uniquement rechercher dans les journaux d'événements.
.PARAMETER LogFilesOnly
 Utilisez ce paramètre si vous souhaitez uniquement rechercher des journaux sur le système de fichiers.
.PARAMETER ExcludeDirectory
 Si vous recherchez sur le système de fichiers, spécifiez les dossiers que vous souhaitez ignorer.
.PARAMETER FileExtension
 Spécifiez un ou plusieurs ensembles d'extensions de fichiers séparés par des virgules que vous souhaitez rechercher sur le système de fichiers.
 Cela défaut aux extensions '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) {
 ## Créer le répertoire local où stocker les fichiers journaux correspondant aux critères
 $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 {
 
    ## Exécuter uniquement si l'utilisateur souhaite trouver des entrées de journal d'événements
    if (!$LogFilesOnly.IsPresent) {
 ## Trouver tous les noms de journaux d'événements qui contiennent au moins 1 événement
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## Trouver tous les événements dans tous les journaux d'événements qui se situent entre les horodatages de début et de fin
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## Convertir les propriétés en quelque chose de plus convivial et ajouter chaque événement au fichier texte du journal d'événements
 $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
 }
 
 ## Exécuter uniquement si l'utilisateur souhaite trouver des fichiers journaux
 if (!$EventLogsOnly.IsPresent) {
        ## Énumérer toutes les partages d'administration à distance sur l'ordinateur distant. Je fais cela au lieu d'énumérer tous les disques physiques car
        ## le disque peut être là et le partage peut ne pas l'être, ce qui signifie que je ne peux pas accéder au disque de toute façon.
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## Assurer que je peux accéder à tous les partages d'administration à distance
 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
 }
 ## Ajouter tous les répertoires spécifiés dans $ExcludeDirectory pour ne pas rechercher de fichiers journaux
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## Créer la chaîne regex folle que j'utilise pour rechercher plusieurs formats de date/heure différents.
 ## Ceci est utilisé dans une tentative de recherche de chaînes date/heure dans chaque fichier texte trouvé
 ##TODO: Ajouter la possibilité de correspondre à Jan, Feb, Mar, etc.
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## Énumérer tous les fichiers correspondant aux paramètres de requête qui ont du contenu
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## Enregistrer le fichier si l'heure de la dernière écriture est dans la plage horaire. Cela trouve des fichiers journaux qui peuvent ne pas enregistrer un
 ## horodatage date/heure mais peuvent toujours être impliqués dans l'événement que l'utilisateur essaie de trouver.
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## Si le fichier trouvé correspond à l'ensemble d'extensions que j'essaie de trouver et qu'il s'agit effectivement d'un fichier texte brut.
 ## J'utilise Get-Content pour vérifier simplement que c'est du texte brut avant de le parcourir.
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## Vérifier le contenu du fichier texte pour des références à des dates dans la plage horaire
 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)'"
 ## Enregistrer toutes les lignes correspondantes dans le fichier d'audit.
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## Créer le même chemin vers le fichier journal sur l'ordinateur distant à l'intérieur du répertoire de sortie du journal et
 ## copier le fichier journal avec un événement dans la plage horaire vers ce chemin.
 ## Cela ne fonctionnera pas si un chemin de fichier supérieur à 255 caractères est rencontré.
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##TODO: Supprimer l'action d'erreur lorsque la prise en charge des chemins longs est implémentée
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

Résumé

Le cmdlet Get-EventLog est une excellente commande à utiliser si vous vous trouvez jamais dans le besoin d’interroger rapidement l’un des journaux d’événements courants. Il est facile à utiliser et fournit une certaine capacité de filtrage de base. Cependant, si vous avez besoin de faire des recherches approfondies dans les journaux d’événements, la commande Get-WinEvent fonctionnera probablement mieux, mais elle est un peu plus difficile à utiliser et nécessite parfois de connaître la syntaxe comme XPath.

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