שאילתת יומני אירועים באופן יעיל עם Get-EventLog של PowerShell

כל מנהל מערכת של Windows יכול להכיר בלוג אירועים של Windows. באמצעות ה-cmdlet הזה ב-PowerShell, מנהלי מערכות יכולים לפענח הרבה אירועים בו זמנית במחשבים רבים יחד. זה משחרר את מנהלי המערכת מהצורך ללחוץ סביב ב-Event Viewer כדי לנסות למצוא את הסינון הנכון לשימוש ולקבוע באופן מדויק איפה האירוע הקריטי הזה מאוחסן. אולם, Get-EventLog יש לו חסרונות שתראו.

רשומת לוגים עם Get-EventLog

ה-cmdlet Get-EventLog זמין בכל הגרסאות המודרניות של Windows PowerShell. בשימוש הכי ישיר שלו, על ה-cmdlet הזה לקבל לוג אירועים לשאילתא שהוא יציג את כל האירועים באותו לוג אירועים.

אך מה קורה אם אתם לא יודעים את שם לוג האירועים מראש? במקרה כזה, עלינו לזהות את כל לוגי האירועים שזמינים במחשב המקומי שלנו. אנו עושים זאת על ידי השימוש בפקודה Get-EventLog -List.

Get-EventLog -List

אתם יכולים לראות שיש לי מספר לוגים אירועים במערכת המקומית שלי כעת, אך ייתכן ואתם שואלים איפה השאר? יש עשרות לוגים אחרים שמופיעים באפליקציות ובשירותים ב-Event Viewer. למה הם לא כאן?

אם אתם צריכים את האירועים ההם, למרבה הצער, Get-EventLog לא יעבוד. במקום זאת, יהיה עליכם לבדוק את Get-WinEvent. ה-cmdlet Get-EventLog יכול להיחשב כ-cmdlet מותרך בנקודה זו, אך זהו אחד שאני עדיין משתמש בו בתדירות רבה פשוט כי זה כל כך קל לשימוש.

שאילתת אירועים עם Get-EventLog

עכשיו שאנו יודעים על כל היומנים לאירועים הזמינים, נוכל כעת לקרוא את האירועים בתוך היומן הזה. אולי אני רוצה לראות את כל האירועים ביומן האירועים של היישום. כדי לקבל את האירועים אלו, עלי לציין את הפרמטר LogName עם Get-EventLog והפקודה תסכים להחזיר את כל האירועים באותו יומן אירועים.

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 מציע כמה דרכים שונות לעשות זאת. פקודת Get-EventLog יכולה לסנן לפי חותמת זמן, סוג רשומה, זיהוי אירוע, הודעה, מקור ושם משתמש. זה טוטל את הרוב של הדרכים למצוא אירועים.

להדגים סינון, אולי אני מבצע שאילתות לאירועים מדי פעם, ואני רוצה למצוא את עשרת האירועים החדשים ביותר. במקרה כזה, אני יכול להשתמש בפרמטר Newest ולציין כמה אירועים אני רוצה לראות. Get-EventLog -LogName Application -Newest 10 יחזיר רק את עשרת האירועים האחרונים.

אולי אני רוצה למצוא את כל האירועים שאחרי נקודה מסוימת בזמן. לכך, יש לנו את הפרמטר After. הפרמטר After מקבל תאריך/שעה, אז אם אני רוצה למצוא רק את האירועים בלוג היישום שקרו אחרי 26/1/19 10:17, אני יכול לעשות זאת Get-EventLog -LogName Application -After '1/26/19 10:17'. אפשר גם לבצע את אותו התהליך אך לבחור אירועים שקרו לפני תאריך מסוים עם, כפי שניחשת, הפרמטר Before.

פקודת Get-EventLog יש לה הרבה דרכים שונות לסינון לא כולל על סמך חותמת זמן. אנו יכולים גם לסנן אירועים על פי תכונות אחרות כמו זהות האירוע (מזהה ההופעה) והודעה שכמותן נפוצות כדי לחפש. אולי אני יודע שאני מחפש אירוע עם זיהוי 916; נעביר 916 לפרמטר InstanceId.

PS> Get-EventLog -LogName Application -InstanceId 916

אנו יכולים לשלב סינונים גם. אולי אני מקבל הרבה אירועים עם זיהוי 916, אבל אני רוצה את האירועים ההם עם המחרוזת svchost בהודעה. במקרה כזה, אנו יכולים להוסיף את הפרמטר Message ל־Get-EventLog ולציין תו כללי כמו svchost.

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

תסריט בונוס!

מחפש דוגמה נהדרת לשימוש ב-Get-EventLog בתסריט ממשי? אז אתה במזל! למטה יש דוגמה מתקדמת של Get-EventLog שאתה יכול להוריד ולהשתמש בה היום!

<#
.SYNOPSIS
    סקריפט זה מחפש במחשב של Windows את כל רשומות יומן האירועים או רשומות הטקסט בין זמן ההתחלה והסיום המצויין.
.DESCRIPTION
    סקריפט זה רואה כל יומן אירועים בתחום הזמן המצויין וכל יומני טקסט שיש להם זמן כתיבה אחרון בתחום זמן זה או מכילים שורה שיש בה התייחסות לתאריך/שעה בתחום הזמן. הסקריפט רושם את המידע הזה לסט של קבצים ומעתיק קובץ יומן מעניין לצורך ניתוח נוסף.
.EXAMPLE
    PS> .\Get-EventsFromTimeframe.ps1 -StartTimestamp '01-29-2014 13:25:00' -EndTimeStamp '01-29-2014 13:28:00' -ComputerName COMPUTERNAME
 
    בדוגמה זו, מתבצע חיפוש לכל רשומות יומן אירועים בין StartTimestamp ו-EndTimestamp וכל קובץ עם סיומת בתוך הפרמטר $FileExtension שהוא או שהוא נכתב לאחרונה בתחום הזמן או מכיל שורה עם התייחסות לתאריך/שעה בתחום הזמן.
.PARAMETER StartTimestamp
 התאריך/שעה הקטנה ביותר שבו תרצה לחפש אחרי אירועים.
.PARAMETER EndTimestamp
 התאריך/שעה הגדולה ביותר שבו תרצה לחפש אחרי אירועים.
.PARAMETER Computername
 שם המחשב המרוחק (או המקומי) שבו תרצה לחפש.
.PARAMETER OutputFolderPath
 הנתיב לתיקייה שתכיל את קבצי הטקסט שיכילו את האירועים.
.PARAMETER LogAuditFilPath
 הנתיב לקובץ הטקסט שיתעד את קובץ היומן, מספר השורה וסוג ההתאמה ליומנים שהתאימו.
.PARAMETER EventLogsOnly
 השתמש בפרמטר המתג אם תרצה לחפש רק ביומני אירועים.
.PARAMETER LogFilesOnly
 השתמש בפרמטר זה אם תרצה לחפש רק יומנים במערכת הקבצים.
.PARAMETER ExcludeDirectory
 אם אתה מחפש במערכת הקבצים, ציין את נתיב התיקייה שבה אתה רוצה לדלג על החיפוש.
.PARAMETER 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 
 }
 ## צור את מחרוזת הרגולרית המטורפת שאני משתמשת בה לחיפוש אחר כמה פורמטים שונים של תאריך/שעה.
 ## זה נעשה בניסיון לחפש מחרוזות תאריך/שעה בכל קובץ טקסט שנמצא
 ##TODO: הוסף אפשרות להתאים ל-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)) {
 ##TODO: הסר את פעולת הטיפול בשגיאות כאשר נתמכת תמיכה בנתיבים ארוכים
 mkdir $Destination -ErrorAction SilentlyContinue | Out-Null
 }
 Copy-Item -Path $_.FullName -Destination $Destination -ErrorAction SilentlyContinue -Recurse
 }
 }
 } catch {
 Write-Warning $_.Exception.Message 
 }
 }
 }
}

עמידום

הפקודה Get-EventLog היא פקודה מצוינת לשימוש כאשר תמצא את עצמך זקוק לשאילתת אחד מלוגי הארועים הנפוצים במהירות. זה קל לשימוש ומספק אפשרות לסינון בסיסית. עם זאת, אם יש לך צורך לבצע חקירת לוג ארוע מעמיקה, ייתכן שהפקודה Get-WinEvent תעבוד יותר טוב, אך זה קצת יותר מורכב ולפעמים דורשת הבנה של תחביר כמו XPath.

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