使用 PowerShell Get-EventLog 高效查询事件日志

每個Windows系統管理員可能都熟悉Windows事件日誌。在PowerShell中使用這個cmdlet允許系統管理員一次解析多個計算機上的大量事件。它使系統管理員不必在事件查看器中點擊來找到正確的過濾器和確定關鍵事件存儲在何處。然而,Get-EventLog確實有其缺點,您將看到。

使用Get-EventLog列出事件日誌

Get-EventLog cmdlet在所有現代版本的Windows PowerShell上都可用。在最簡單的用法中,該cmdlet需要查詢的事件日誌,然後顯示該事件日誌中的所有事件。

但是,如果您一開始不知道事件日誌的名稱該怎麼辦?在這種情況下,我們需要找出本地計算機上可用的所有事件日誌。我們可以使用命令Get-EventLog -List來完成這一點。

Get-EventLog -List

您可以看到,我在本地系統上有幾個事件日誌,但您可能想知道其他事件日誌在哪裡?在事件查看器的”應用程式和服務日誌”下顯示了數十個其他事件日誌。為什麼它們不在這裡呢?

如果您需要這些事件,不幸的是,Get-EventLog不會起作用。相反,您需要查看Get-WinEvent。目前,Get-EventLog cmdlet可以被視為遺留的cmdlet,但是我仍然經常使用它,只是因為它非常容易使用。

使用Get-EventLog進行事件查詢

現在我們已經知道所有可用的事件日誌,我們可以讀取這個事件日誌中的事件。也許我想看到應用程式事件日誌中的所有事件。要獲取這些事件,我需要在 Get-EventLog 中指定 LogName 參數,然後該命令將返回該事件日誌中的所有事件。

Get-EventLog -LogName Application

默認情況下,你只會在輸出中看到六個屬性:

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

實際上,Get-EventLog 返回了其中的 16 個屬性。你只看到六個是因為 PowerShell 的格式設定規則定義了輸出。以下是通過將 Get-EventLog 管道到 Select-Object 並選擇所有屬性找到的實際輸出的示例。

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

使用 Get-EventLog 進行過濾

當查找事件時,很可能我們不需要所有事件。相反,我們只需要一些。在這種情況下,我們需要過濾特定的事件。 Get-EventLog 有幾種不同的方法可以做到這一點。這個命令可以根據時間戳、記錄類型、事件 ID、消息、來源和用戶名進行過濾。這滿足了大多數查找事件的方式。

為了示範過濾功能,也許我會定期查詢事件並尋找最新的十個事件。在這種情況下,我可以使用 “Newest” 參數並指定我想看到的事件數量。”Get-EventLog -LogName Application -Newest 10″ 只會返回最新的十個事件。

也許我想找到特定時間點之後的所有事件。為此,我們有 “After” 參數。”After” 參數接受一個日期/時間,所以如果我只想找到在應用程式日誌中發生在 2019/1/26 10:17 AM 之後的事件,我可以這樣做 “Get-EventLog -LogName Application -After ‘1/26/19 10:17′”。我們也可以進行相同的過程,但選擇在某個日期之前發生的事件,你可能已經猜到了,使用 “Before” 參數。

“Get-EventLog” 有很多不同的過濾方式,不僅僅基於時間戳。我們還可以基於其他屬性,如事件 ID(實例 ID)和消息來過濾事件,這些通常是常見的搜索屬性。也許我知道我正在尋找 ID 為 916 的事件;我們將 916 傳遞給 “InstanceId” 參數。

PS> Get-EventLog -LogName Application -InstanceId 916

我們也可以結合過濾器。也許返回了許多 ID 為 916 的事件,但我只想要那些消息中包含 “svchost” 字串的事件。在這種情況下,我們可以在 “Get-EventLog” 中添加 “Message” 參數,並指定像 svchost 這樣的通配符。

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

額外的腳本!

您需要一个在真实世界脚本中使用Get-EventLog的很好的例子吗?如果是的话,您真幸运!下面是一个高级用例的Get-EventLog,您可以立即下载和使用!

<#
.簡介
    此腳本在指定的開始和結束時間之間搜索Windows計算機上的所有事件日誌或文本日誌項目。
.描述
    此腳本列舉在指定時間範圍內的所有事件日誌和在時間範圍內具有最後寫入時間的所有文本日誌,
    或包含在時間範圍內具有日期/時間參考的行的文件。它將此信息記錄到一組文件中,
    並將任何有趣的日誌文件複製出來進一步分析。
.示例
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME
     
    此示例查找開始時間和結束時間之間的所有事件日誌項目和任何具有擴展名的文件,
    擴展名在$FileExtension參數內,該文件在時間範圍內最後被寫入,或者包含時間範圍內的日期/時間參考的行。
.參數 開始時間戳
    希望開始搜索事件的最早日期/時間。
.參數 結束時間戳
    希望開始搜索事件的最新日期/時間。
.參數 電腦名稱
    您要搜索的遠程(或本地)計算機的名稱。
.參數 輸出文件夾路徑
    包含事件的文本文件的文件夾的路徑。
.參數 日誌審計文件路徑
    用於記錄匹配的日誌文件、行號和匹配類型的文本文件的路徑。
.參數 只搜索事件日誌
    如果只想在事件日誌中搜索,請使用此開關參數。
.參數 只搜索日誌文件
    如果只想在文件系統中搜索日誌,請使用此參數。
.參數 排除目錄
    如果在文件系統中搜索,請指定要跳過的任何文件夾路徑。
.參數 文件擴展名
    指定一個或多個以逗號分隔的文件擴展名集,您要在文件系統中搜索。默認為'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) {
 ## 創建本地目錄,用於存儲符合條件的所有日誌文件
 $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 {
 
    ## 只有當用戶想要查找事件日誌項目時才運行
    if (!$LogFilesOnly.IsPresent) {
 ## 查找包含至少1個事件的所有事件日誌名稱
 $Logs = (Get-WinEvent -ListLog * -ComputerName $ComputerName | where { $_.RecordCount }).LogName
 $FilterTable = @{
 'StartTime' = $StartTimestamp
 'EndTime' = $EndTimestamp
 'LogName' = $Logs
 }
 
 ## 查找在開始和結束時間之間的所有事件日誌中的所有事件
 $Events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $FilterTable -ea 'SilentlyContinue'
 Write-Verbose "Found $($Events.Count) total events"
 
 ## 將屬性轉換為更友好的格式並將每個事件附加到事件日誌文本文件中
 $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
 }
 
 ## 只有當用戶想要查找日誌文件時才運行
 if (!$EventLogsOnly.IsPresent) {
        ## 枚舉遠程計算機上的所有遠程管理共享。我這樣做是因為
        ## 驅動器可能存在但共享可能不存在,這意味著我無法訪問驅動器。
 $Shares = Get-WmiObject -ComputerName $ComputerName -Class Win32_Share | where { $_.Path -match '^\w{1}:\\$' }
 [System.Collections.ArrayList]$AccessibleShares = @()
 ## 確保我能訪問所有遠程管理共享
 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
 }
 ## 將$ExcludeDirectory參數中指定的任何目錄添加到不搜索日誌文件的列表中
 if ($ExcludeDirectory) {
 $AllFilesQueryParams.ExcludeDirectory = $ExcludeDirectory 
 }
 ## 創建用於搜索多種不同日期/時間格式的正則表達式字符串。
 ## 這在嘗試在找到的每個文本文件中搜索日期/時間字符串時使用
 ##待辦事項:添加對Jan,Feb,Mar等的匹配能力
 $DateTimeRegex = "($($StartTimestamp.Month)[\\.\-/]?$($StartTimestamp.Day)[\\.\-/]?[\\.\-/]$($StartTimestamp.Year))|($($StartTimestamp.Year)[\\.\-/]?$($StartTimestamp.Month)[\\.\-/]?[\\.\-/]?$($StartTimestamp.Day))"
 ## 枚舉與查詢參數匹配且具有內容的所有文件
 Get-ChildItem @AllFilesQueryParams | where { $_.Length -ne 0 } | foreach {
 try {
 Write-Verbose "Processing file '$($_.Name)'"
 ## 如果最後寫入時間在時間範圍內,記錄文件。這將找到可能不記錄
 ## 日期/時間時間戳但可能仍涉及用戶正在尋找的事件的日誌文件。
 if (($_.LastWriteTime -ge $StartTimestamp) -and ($_.LastWriteTime -le $EndTimestamp)) {
 Write-Verbose "Last write time within timeframe for file '$($_.Name)'"
 Add-ToLog -FilePath $_.FullName -MatchType 'LastWriteTime'
 }
 ## 如果找到的文件與我要查找的擴展名集匹配並且實際上是純文本文件。
 ## 我使用Get-Content僅檢查它是否是純文本文件,然後再解析它。
 if ($FileExtension -contains $_.Extension.Replace('.','') -and !((Get-Content $_.FullName -Encoding Byte -TotalCount 1024) -contains 0)) {
 ## 檢查文本文件的內容是否參考時間範圍中的日期
 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)'"
 ## 將所有匹配的行記錄到審計文件中。
 foreach ($Match in $LineMatches) {
 Add-ToLog -FilePath $_.FullName -LineNumber $Match.LineNumber -LineText $Match.Line -MatchType 'Contents'
 }
 ## 在輸出日誌目錄內創建與遠程計算機上的日誌文件相同的路徑,並
 ## 將具有時間範圍內事件的日誌文件複製到該路徑。
 ## 如果遇到255個字符以上的文件路徑,這將無效。
 $Trim = $_.FullName.Replace("\\$Computername\", '')
 $Destination = "$OutputFolderPath\$Trim"
 if (!(Test-Path $Destination)) {
 ##待辦事項:實施長路徑支持時,刪除錯誤操作
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

摘要

如果你需要快速查询常见的事件日志,Get-EventLog cmdlet是一个很好的命令。它易于使用,并提供了一些基本的过滤能力。然而,如果您需要进行任何深入的事件日志调查,Get-WinEvent 命令可能更适合,但它使用起来较困难,有时需要了解类似XPath的语法。

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