Журнал событий Windows (Event Log) — это важный инструмент, который позволяет администратору отслеживать ошибки, предупреждения и другие информационные сообщения, которые регистрируются операционной системой, ее компонентами и различными программами. Для просмотра журнала событий Windows можно использовать графическую MMC оснастку Event Viewer (
eventvwr.msc
). В некоторых случаях для поиска информации в журналах событий и их анализа гораздо удобнее использовать PowerShell. В этой статье мы покажем, как получать информацию из журналов событий Windows с помощью командлета Get-WinEvent.
Содержание:
- Получение логов Windows с помощью Get-WinEvent
- Get-WinEvent: быстрый поиск в событиях Event Viewer с помощью FilterHashtable
- Расширенный фильтры событий Get-WinEvent с помощью FilterXml
- Получить логи Event Viewer с удаленных компьютеров
На данный момент в Windows доступны два командлета для доступа к событиям в Event Log: Get-EventLog и Get-WinEvent. В подавляющем большинстве случаев рекомендуем использовать именно Get-WinEvent, т.к. он более производителен, особенно в сценариях обработки большого количества событий с удаленных компьютеров. Командлет Get-EventLog является устаревшим и использовался для получения логов в более ранних версиях Windows. Кроме того, Get-EventLog не поддерживается в современных версиях PowerShell Core 7.x.
Получение логов Windows с помощью Get-WinEvent
Для использования команды Get-WinEvent нужно запустить PowerShell с правами администратора (при запуске Get-WinEvent от имени пользователя вы не сможете получить доступ к некоторым логам, например, к Security).
Для получения списка событий из определенного журнала, нужно указать его имя. В данном примере мы выведем последние 20 событий из журнала System:
Get-WinEvent -LogName Application -MaxEvents 20
Чаще всего вам нужно будет получать информацию из журналов System, Application, Security или Setup. Но вы можете указать и другие журналы. Полный список журналов событий в Windows можно получить с помощью команды:
Get-WinEvent -ListLog *
Например, чтобы вывести события RDP подключений к компьютеру, нужно указать лог Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational:
Get-WinEvent -LogName Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational
Или получить логи SSH подключений к Windows из журнала OpenSSH/Operational:
Get-WinEvent -LogName OpenSSH/Operational
Можно выбрать события сразу из нескольких журналов. Например, чтобы получить информацию о ошибках и предупреждениях из журналов System и Application за последние 24 часа (сутки), можно использовать такой код:
$StartDate = (Get-Date) - (New-TimeSpan -Day 1)
Get-WinEvent Application,System | Where-Object {($_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning") -and ($_.TimeCreated -ge $StartDate )}
Чтобы вывести только определенные поля событий, можно использовать Select-Object или Format-Table:
Get-WinEvent -LogName System | Format-Table Machinename, TimeCreated, Id, UserID
Можно выполнить дополнительные преобразования с полученными данными. Например, в этом примере мы сразу преобразуем имя пользователя в SID:
Get-WinEvent -filterhash @{Logname = 'system'} |
Select-Object @{Name="Computername";Expression = {$_.machinename}},@{Name="UserName";Expression = {$_.UserId.translate([System.Security.Principal.NTAccount]).value}}, TimeCreated
Get-WinEvent: быстрый поиск в событиях Event Viewer с помощью FilterHashtable
Рассмотренный выше способ выбора определенных событий из журналов Event Viewer с помощью Select-Object прост для понимая, но выполняется крайне медленно. Это особенно заметно при выборке большого количества событий. В большинстве случаев для выборки событий нужно использовать фильтрацию на стороне службы Event Viewer с помощью параметра FilterHashtable.
Попробуем сформировать список ошибок и предупреждений за 30 дней с помощью Where-Object и FilterHashtable. Сравнима скорость выполнения этих двух команд PowerShell с помощью Measure-Command:
$StartDate = (Get-Date).AddDays(-30)
Проверим скорость выполнения команды с Where-Object:
(Measure-Command {Get-WinEvent Application,System | Where-Object {($_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning") -and ($_.TimeCreated -ge $StartDate )}}).TotalMilliseconds
Аналогичная команда с FilterHashtable:
(Measure-Command {Get-WinEvent -FilterHashtable @{LogName = 'System','Application'; Level =2,3; StartTime=$StartDate }})..TotalMilliseconds
В данном примере видно, что команда выборки событий через FilterHashtable выполняется в 30 раз быстрее, чем если бы обычный Where-Object (
2.5
сек vs
76
секунд).
Если вам нужно найти события по EventID, используйте следующую команду с FilterHashtable:
Get-WinEvent -FilterHashtable @{logname='System';id=1074}|ft TimeCreated,Id,Message
В параметре FilterHashtable можно использовать фильтры по следующим атрибутам событий:
- LogName
- ProviderName
- Path
- Keywords (для поиска успешных событий нужно использовать значение 9007199254740992 или для неуспешных попыток 4503599627370496)
- ID
- Level (1=FATAL, 2=ERROR, 3=Warning, 4=Information, 5=DEBUG, 6=TRACE, 0=Info)
- StartTime
- EndTime
- UserID (SID пользователя)
- Data
Пример поиска события за определенный промежуток времени:
Get-WinEvent -FilterHashTable @{LogName='System'; StartTime=(get-date).AddDays(-7); EndTime=(get-date).AddHours(-1); ID=1234}
Если нужно найти определенный текст в описании события, можно использовать такую команду:
Get-WinEvent -FilterHashtable @{logname='System'}|Where {$_.Message -like "*USB*"}
Расширенный фильтры событий Get-WinEvent с помощью FilterXml
Фильтры Get-WinEvent с параметром FilterHashtable являются несколько ограниченными. Если вам нужно использовать для выборки событий сложные запросы с множеством условий, нужно использовать параметр FilterXml, который позволяет сформировать запрос на выбор событий в Event Viewer с помощью XML запроса. Как и FilterHashtable, фильтры FilterXml выполняется на стороне сервера, поэтому результат вы получите довольно быстро.
Например, аналогичный запрос для получения последних ошибок из журнала System за последние 30 дней может выглядеть так:
$xmlQuery = @'
<QueryList>
<Query Id="0" Path="System">
<Select Path="System">*[System[(Level=2 or Level=3) and TimeCreated[timediff(@SystemTime) <= 2592000000]]]</Select>
</Query>
</QueryList>
'@
Get-WinEvent -FilterXML $xmlQuery
Для построения кода сложных XML запросов можно использовать графическую консоль Event Viewer:
- Запустите
eventvwr.msc
; - Найдите журнал для которого вы хотите создать и выберите Filter Current Log;
- Выберите необходимые параметры запроса в форме. В этом примере я хочу найти события с определенными EventID за последние 7 дней от определенного пользователя;
- Чтобы получить код XML запроса для параметра FilterXML, перейдите на вкладку XML и скопируйте полученный код (CTRL+A, CTRL+C);
- Если нужно, вы можете вручную отредактировать данный запрос.
Для экспорта списка событий в CSV файл нужно использовать командлет Export-CSV:
$Events= Get-WinEvent -FilterXML $xmlQuery
$events| Export-CSV "C:\ps\FilterSYSEvents.csv" -NoTypeInformation -Encoding UTF8
Получить логи Event Viewer с удаленных компьютеров
Для получения события с удаленного компьютер достаточно указать его имя в параметре -ComputerName:
$computer='msk-dc01'
Get-WinEvent -ComputerName $computer -FilterHashtable @{LogName="System"; StartTime=(get-date).AddHours(-24)} | select Message,Id,TimeCreated
Можно опросить сразу несколько серверов/компьютеров и поискать на них определенные события. Список серверов можно получить из текстового файла:
$servers = Get-Content -Path C:\ps\servers.txt
Или из Active Directory:
$servers = (Get-ADComputer -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"').Name
foreach ($server in $servers) {
Get-WinEvent -ComputerName $server -MaxEvents 5 -FilterHashtable @{
LogName = 'System'; ID= 1234
} | Select-Object -Property ID, MachineName
}
Здесь есть другой пример для поиска событий блокировки учетной записи пользователя на всех контроллерах домена:
$Username = 'a.ivanov'
Get-ADDomainController -fi * | select -exp hostname | % {
$GweParams = @{
‘Computername’ = $_
‘LogName’ = ‘Security’
‘FilterXPath’ = "*[System[EventID=4740] and EventData[Data[@Name='TargetUserName']='$Username']]"
}
$Events = Get-WinEvent @GweParams
$Events | foreach {$_.Computer + " " +$_.Properties[1].value + ' ' + $_.TimeCreated}
}
Проникновение во внутреннюю сеть организации часто начинается с заражения рабочей станции сотрудника, например, с применением фишинговой рассылки. Анализ логов является одним из основных этапов проведения расследований инцидентов кибербезопасности.
В ОС существуют стандартные средства просмотра логов — Event Viewer, однако, при большом объёме информации требуется более масштабируемый и автоматизированный подход. Удобным решением является использование языка Powershell, что позволяет ощутимо упростить и увеличить скорость проведения анализа.
Основные команды Powershell для работы с логами: Get-EventLog (для работы с классическими журналами — Application, System, Security) и Get-WinEvent (для работы с любыми журналами). Список доступных журналов можно получить командой Get-WinEvent -ListLog. Вывести 10 последних записей журнала System позволяет команда Get-WinEvent –LogName ‘System’ –MaxEvents 10. Для более удобной фильтрации можно использовать хэш-таблицы, например, команда Get-WinEvent –FilterHashTable @{LogName=’Security’;ID=4740} выводит информацию о событиях журнала System с кодом 4740, генерация которых связана с блокировкой пользователя.
Для решения задачи получения удаленных входов на АРМ по RDP за последнюю неделю можно написать скрипт, сохранив его для удобства в отдельный файл.
$events = Get-WinEvent –FilterHashTable @{LogName=’Security’;StartTime=((get-date).Adddays(-7));ID=4624}
$result = $events | Where {$_.Properties[8].Values – eq 10} | foreach {
$event = $_
»$($event.TimeGenerated)$($event. Properties [5]) $($event. Properties [11])$($event. Properties [18])»
}
$result | out-file result.txt
В результате в файл выводится время события, имя пользователя, название рабочей станции и ip-адрес источника подключения.
Аналогичным образом можно выявлять в логах следы вредоносной активности, среди которых можно выделить:
- Перебор пользователей и групп (События 4798 и 4799)
- Создание локальной учётной записи и изменения в локальных группах (События 4720, 4722-4276, 4738, 4740, 4767, 4780, 4781,4794, 5376, 5377)
- Попытки входа с локальной учётной записью (События 4624)
- Подключение устройств Plug’n’Play (События 6416)
При необходимости сбора данных с нескольких АРМ целесообразно собирать логи удалённо. Используя опцию -ComputerName, можно получить логи с удалённого АРМ. Пример команд для сбора логов с нескольких компьютеров:
$comp_list = »comp1», »comp2», »comp3»
ForEach ($comp in $comp_list) {$comp; Get-EventLog –LogName Security –ComputerName $comp –After 01/10/21 | Where-Object {$_.EventID –eq »’6416»}}
В результате выполнения скрипт выведет события подключения устройств Plug’n’Play после 01/10/21 с АРМ, заданных в $comp_list.
В случае сложной инфраструктуры и большого числа рабочих мест организации необходимо централизованно собирать и хранить логи на сервере. Задача может быть реализована путём распространения на все компьютеры организации скрипта powershell, загружающего логи на сервер, с помощью MS SCCM, либо службы Active Directory.
Search code, repositories, users, issues, pull requests…
Provide feedback
Saved searches
Use saved searches to filter your results more quickly
Sign up
Пора поговорить про удобную работу с логами, тем более что в Windows есть масса неочевидных инструментов для этого. Например, Log Parser, который порой просто незаменим.
В статье не будет про серьезные вещи вроде Splunk и ELK (Elasticsearch + Logstash + Kibana). Сфокусируемся на простом и бесплатном.
Журналы и командная строка
До появления PowerShell можно было использовать такие утилиты cmd как find и findstr. Они вполне подходят для простой автоматизации. Например, когда мне понадобилось отлавливать ошибки в обмене 1С 7.7 я использовал в скриптах обмена простую команду:
findstr "Fail" *.log >> fail.txt
Она позволяла получить в файле fail.txt все ошибки обмена. Но если было нужно что-то большее, вроде получения информации о предшествующей ошибке, то приходилось создавать монструозные скрипты с циклами for или использовать сторонние утилиты. По счастью, с появлением PowerShell эти проблемы ушли в прошлое.
Основным инструментом для работы с текстовыми журналами является командлет Get-Content, предназначенный для отображения содержимого текстового файла. Например, для вывода журнала сервиса WSUS в консоль можно использовать команду:
Get-Content -Path 'C:\Program Files\Update Services\LogFiles\SoftwareDistribution.log' | Out-Host -Paging
Для вывода последних строк журнала существует параметр Tail, который в паре с параметром Wait позволит смотреть за журналом в режиме онлайн. Посмотрим, как идет обновление системы командой:
>Get-Content -Path "C:\Windows\WindowsUpdate.log" -Tail 5 -Wait
Смотрим за ходом обновления Windows.
Если же нам нужно отловить в журналах определенные события, то поможет командлет Select-String, который позволяет отобразить только строки, подходящие под маску поиска. Посмотрим на последние блокировки Windows Firewall:
Select-String -Path "C:\Windows\System32\LogFiles\Firewall\pfirewall.log" -Pattern 'Drop' | Select-Object -Last 20 | Format-Table Line
Смотрим, кто пытается пролезть на наш дедик.
При необходимости посмотреть в журнале строки перед и после нужной, можно использовать параметр Context. Например, для вывода трех строк после и трех строк перед ошибкой можно использовать команду:
Select-String 'C:\Windows\Cluster\Reports\Cluster.log' -Pattern ' err ' ‑Context 3
Оба полезных командлета можно объединить. Например, для вывода строк с 45 по 75 из netlogon.log поможет команда:
Get-Content 'C:\Windows\debug\netlogon.log' | Select-Object -First 30 -Skip 45
Журналы системы ведутся в формате .evtx, и для работы с ними существуют отдельные командлеты. Для работы с классическими журналами («Приложение», «Система», и т.д.) используется Get-Eventlog. Этот командлет удобен, но не позволяет работать с остальными журналами приложений и служб. Для работы с любыми журналами, включая классические, существует более универсальный вариант ― Get-WinEvent. Остановимся на нем подробнее.
Для получения списка доступных системных журналов можно выполнить следующую команду:
Get-WinEvent -ListLog *
Вывод доступных журналов и информации о них.
Для просмотра какого-то конкретного журнала нужно лишь добавить его имя. Для примера получим последние 20 записей из журнала System командой:
Get-WinEvent -LogName 'System' -MaxEvents 20
Последние записи в журнале System.
Для получения определенных событий удобнее всего использовать хэш-таблицы. Подробнее о работе с хэш-таблицами в PowerShell можно прочитать в материале Technet about_Hash_Tables.
Для примера получим все события из журнала System с кодом события 1 и 6013.
Get-WinEvent -FilterHashTable @{LogName='System';ID='1','6013'}
В случае если надо получить события определенного типа ― предупреждения или ошибки, ― нужно использовать фильтр по важности (Level). Возможны следующие значения:
- 0 ― всегда записывать;
- 1 ― критический;
- 2 ― ошибка;
- 3 ― предупреждение;
- 4 ― информация;
- 5 ― подробный (Verbose).
Собрать хэш-таблицу с несколькими значениями важности одной командой так просто не получится. Если мы хотим получить ошибки и предупреждения из системного журнала, можно воспользоваться дополнительной фильтрацией при помощи Where-Object:
Get-WinEvent -FilterHashtable @{LogName='system'} | Where-Object -FilterScript {($_.Level -eq 2) -or ($_.Level -eq 3)}
Ошибки и предупреждения журнала System.
Аналогичным образом можно собирать таблицу, фильтруя непосредственно по тексту события и по времени.
Подробнее почитать про работу обоих командлетов для работы с системными журналами можно в документации PowerShell:
- Get-EventLog.
- Get-WinEvent.
PowerShell ― механизм удобный и гибкий, но требует знания синтаксиса и для сложных условий и обработки большого количества файлов потребует написания полноценных скриптов. Но есть вариант обойтись всего-лишь SQL-запросами при помощи замечательного Log Parser.
Работаем с журналами посредством запросов SQL
Утилита Log Parser появилась на свет в начале «нулевых» и с тех пор успела обзавестись официальной графической оболочкой. Тем не менее актуальности своей она не потеряла и до сих пор остается для меня одним из самых любимых инструментов для анализа логов. Загрузить утилиту можно в Центре Загрузок Microsoft, графический интерфейс к ней ― в галерее Technet. О графическом интерфейсе чуть позже, начнем с самой утилиты.
О возможностях Log Parser уже рассказывалось в материале «LogParser — привычный взгляд на непривычные вещи», поэтому я начну с конкретных примеров.
Для начала разберемся с текстовыми файлами ― например, получим список подключений по RDP, заблокированных нашим фаерволом. Для получения такой информации вполне подойдет следующий SQL-запрос:
SELECT
extract_token(text, 0, ' ') as date,
extract_token(text, 1, ' ') as time,
extract_token(text, 2, ' ') as action,
extract_token(text, 4, ' ') as src-ip,
extract_token(text, 7, ' ') as port
FROM 'C:\Windows\System32\LogFiles\Firewall\pfirewall.log'
WHERE action='DROP' AND port='3389'
ORDER BY date,time DESC
Посмотрим на результат:
Смотрим журнал Windows Firewall.
Разумеется, с полученной таблицей можно делать все что угодно ― сортировать, группировать. Насколько хватит фантазии и знания SQL.
Log Parser также прекрасно работает с множеством других источников. Например, посмотрим откуда пользователи подключались к нашему серверу по RDP.
Работать будем с журналом TerminalServices-LocalSessionManager\Operational.
Не со всеми журналами Log Parser работает просто так ― к некоторым он не может получить доступ. В нашем случае просто скопируем журнал из %SystemRoot%\System32\Winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager%4Operational.evtx в %temp%\test.evtx.
Данные будем получать таким запросом:
SELECT
timegenerated as Date,
extract_token(strings, 0, '|') as user,
extract_token(strings, 2, '|') as sourceip
FROM '%temp%\test.evtx'
WHERE EventID = 21
ORDER BY Date DESC
Смотрим, кто и когда подключался к нашему серверу терминалов.
Особенно удобно использовать Log Parser для работы с большим количеством файлов журналов ― например, в IIS или Exchange. Благодаря возможностям SQL можно получать самую разную аналитическую информацию, вплоть до статистики версий IOS и Android, которые подключаются к вашему серверу.
В качестве примера посмотрим статистику количества писем по дням таким запросом:
SELECT
TO_LOCALTIME(TO_TIMESTAMP(EXTRACT_PREFIX(TO_STRING([#Fields: date-time]),0,'T'), 'yyyy-MM-dd')) AS Date,
COUNT(*) AS [Daily Email Traffic]
FROM 'C:\Program Files\Microsoft\Exchange Server\V15\TransportRoles\Logs\MessageTracking\*.LOG'
WHERE (event-id='RECEIVE') GROUP BY Date ORDER BY Date ASC
Если в системе установлены Office Web Components, загрузить которые можно в Центре загрузки Microsoft, то на выходе можно получить красивую диаграмму.
Выполняем запрос и открываем получившуюся картинку…
Любуемся результатом.
Следует отметить, что после установки Log Parser в системе регистрируется COM-компонент MSUtil.LogQuery. Он позволяет делать запросы к движку утилиты не только через вызов LogParser.exe, но и при помощи любого другого привычного языка. В качестве примера приведу простой скрипт PowerShell, который выведет 20 наиболее объемных файлов на диске С.
$LogQuery = New-Object -ComObject "MSUtil.LogQuery"
$InputFormat = New-Object -ComObject "MSUtil.LogQuery.FileSystemInputFormat"
$InputFormat.Recurse = -1
$OutputFormat = New-Object -ComObject "MSUtil.LogQuery.CSVOutputFormat"
$SQLQuery = "SELECT Top 20 Path, Size INTO '%temp%\output.csv' FROM 'C:\*.*' ORDER BY Size DESC"
$LogQuery.ExecuteBatch($SQLQuery, $InputFormat, $OutputFormat)
$CSV = Import-Csv $env:TEMP'\output.csv'
$CSV | fl
Remove-Item $env:TEMP'\output.csv'
$LogQuery=$null
$InputFormat=$null
$OutputFormat=$null
Ознакомиться с документацией о работе компонента можно в материале Log Parser COM API Overview на портале SystemManager.ru.
Благодаря этой возможности для облегчения работы существует несколько утилит, представляющих из себя графическую оболочку для Log Parser. Платные рассматривать не буду, а вот бесплатную Log Parser Studio покажу.
Интерфейс Log Parser Studio.
Основной особенностью здесь является библиотека, которая позволяет держать все запросы в одном месте, без россыпи по папкам. Также сходу представлено множество готовых примеров, которые помогут разобраться с запросами.
Вторая особенность ― возможность экспорта запроса в скрипт PowerShell.
В качестве примера посмотрим, как будет работать выборка ящиков, отправляющих больше всего писем:
Выборка наиболее активных ящиков.
При этом можно выбрать куда больше типов журналов. Например, в «чистом» Log Parser существуют ограничения по типам входных данных, и отдельного типа для Exchange нет ― нужно самостоятельно вводить описания полей и пропуск заголовков. В Log Parser Studio нужные форматы уже готовы к использованию.
Помимо Log Parser, с логами можно работать и при помощи возможностей MS Excel, которые упоминались в материале «Excel вместо PowerShell». Но максимального удобства можно достичь, подготавливая первичный материал при помощи Log Parser с последующей обработкой его через Power Query в Excel.
Приходилось ли вам использовать какие-либо инструменты для перелопачивания логов? Поделитесь в комментариях.
Today, I’m going to show you how you can use Windows PowerShell to quickly and easily find the Windows event log entries that you need to see right now. By the end of this article, you’ll understand how to write event log queries that pull selected events from local and/or remote Windows computers.
Contents
- Understanding Get-EventLog
- Where’s the Event ID?
- More event log search tips
- Remote event logs
- The Get-WinEvent cmdlet
Recall that we use good ol’ Event Viewer (eventvwr.msc) to read event messages in a graphical environment.
Our old friend the Windows Event Viewer
Ever since Windows Vista, we’ve had far more than the handful of “classic” Windows event logs. Let’s learn how to wrangle all those thousands of messages programmatically by using Windows PowerShell.
Understanding Get-EventLog
The Get-EventLog cmdlet has two parameter sets, as you can see in the partial help output below:
PS C:\> Get-Help Get-EventLog Synopsis Gets the events in an event log, or a list of the event logs, on the local or remote computers. Syntax Get-EventLog [-LogName] <String> [[-InstanceId] <Int64[]>] [-After <DateTime>] [-AsBaseObject ] [-Before <DateTime>] [-ComputerName <String[]>] [-EntryType <String[]>] [-Index <Int32[]>] [-Message <String>] [-Newest <Int32>] [-Source <String[]>] [-UserName <String[]>] [<CommonParameters>] Get-EventLog [-AsString ] [-ComputerName <String[]>] [-List ] [<CommonParameters>]
Basically, we use the first parameter set to interrogate event log contents. We invoke the second parameter set to view “meta” information about our event logs, like so:
PS C:\> Get-EventLog -List | Sort-Object -Property Entries -Descending Max(K) Retain OverflowAction Entries Log ------ ------ -------------- ------- --- 20,480 0 OverwriteAsNeeded 2,928 Security 20,480 0 OverwriteAsNeeded 1,449 Application 20,480 0 OverwriteAsNeeded 1,421 System 15,360 0 OverwriteAsNeeded 126 Windows PowerShell 512 0 OverwriteAsNeeded 4 ThinPrint Diagnostics 128 0 OverwriteAsNeeded 0 OAlerts 20,480 0 OverwriteAsNeeded 0 HardwareEvents 20,480 0 OverwriteAsNeeded 0 Key Management Service 512 7 OverwriteOlder 0 Internet Explorer
Now let’s use the first (most common) parameter set to see the latest three entries from my Windows 8.1 computer’s System log:
Get-EventLog -LogName System | Select-Object -First 3 Index Time EntryType Source InstanceID Message ----- ---- --------- ------ ---------- ------- 1421 Apr 15 20:05 Information Service Control M... 1073748864 The sta... 1420 Apr 15 19:05 Information Microsoft-Windows... 17 Install... 1419 Apr 15 19:05 Information Microsoft-Windows... 17 Install...
By the way, we can do the same thing from within the Get-EventLog cmdlet:
Get-EventLog –LogName System –Newest 3
It pays to read the documentation!
In any event, I find that the event log data is so dense that the default Format-Table output truncates the most important stuff! Let’s view the most recent System log entry in list view instead:
PS C:\> Get-EventLog -LogName System -Index 1421 | Format-List Index : 1421 EntryType : Information InstanceId : 1073748864 Message : The start type of the Background Intelligent Transfer Service service was changed from auto start to demand start. Category : (0) CategoryNumber : 0 ReplacementStrings : {Background Intelligent Transfer Service, auto start, demand start, BITS} Source : Service Control Manager TimeGenerated : 4/15/2015 8:05:11 PM TimeWritten : 4/15/2015 8:05:11 PM UserName : NT AUTHORITY\SYSTEM
Much better, but still verbose. We’ll whittle away at this problem as we proceed through the rest of this tutorial.
Where’s the Event ID?
In my experience as a Windows systems administrator, I use the Event ID as the most useful “handle” for investigating event log entries. Sadly, the PowerShell team chose not to include EventID as a default property. We can fix that, though.
Let’s view the full property list for that newest System log entry we used earlier:
PS C:\> Get-EventLog -LogName System -Index 1421 | Get-Member -MemberType Properties TypeName: System.Diagnostics.EventLogEntry#System/Service ControlManager/1073748864 Name MemberType Definition ---- ---------- ---------- Category Property string Category {get;} CategoryNumber Property int16 CategoryNumber {get;} Container Property System.ComponentModel.IContainer Container... Data Property byte[] Data {get;} EntryType Property System.Diagnostics.EventLogEntryType Entry... Index Property int Index {get;} InstanceId Property long InstanceId {get;} MachineName Property string MachineName {get;} Message Property string Message {get;} ReplacementStrings Property string[] ReplacementStrings {get;} Site Property System.ComponentModel.ISite Site {get;set;} Source Property string Source {get;} TimeGenerated Property datetime TimeGenerated {get;} TimeWritten Property datetime TimeWritten {get;} UserName Property string UserName {get;} EventID ScriptProperty System.Object EventID {get=$this.get_Event...
A-ha! As it happens, EventID isn’t even a native .NET property. Instead, it’s a ScriptProperty, which means that this synthetic member relies on a direct call to the .NET Framework. Let’s look deeper at this situation:
PS C:\> Get-EventLog -LogName System -Index 1421 | Get-Member -Name EventID | Format-List TypeName : System.Diagnostics.EventLogEntry#System/Service Control Manager/1073748864 Name : EventID MemberType : ScriptProperty Definition : System.Object EventID {get=$this.get_EventID() -band 0xFFFF;}
Alrighty then—the ScriptProperty pulls the EventID directly from the .NET Framework. I wonder why the PowerShell team exposed EventID this way. Inquiring minds want to know!
In any event, we can recast our Get-EventLog statement to show us the EventID column with no muss, fuss, or greasy aftertaste:
PS C:\> Get-EventLog -LogName System -Newest 3 | Sort-Object -Property TimeGenerated -Descending | Select-Object -Property TimeGenerated, Source, EventID, Message | Format-Table -AutoSize TimeGenerated Source EventID Message ------------- ------ ------- ------- 4/15/2015 8:05:11 PM Service Control Manager 7040 The start... 4/15/2015 7:05:45 PM Microsoft-Windows-WindowsUpdateClient 17 Installat... 4/15/2015 7:05:40 PM Microsoft-Windows-WindowsUpdateClient 17 Installat...
Of course, if we know the Event ID we’re looking for, we can include a Where-Object expression:
PS C:\> Get-EventLog -LogName System | Where-Object { $_.EventID -eq "17" } | Sort-Object -Property TimeGenerated -Descending Index Time EntryType Source InstanceID Message ----- ---- --------- ------ ---------- ------- 1424 Apr 16 00:21 Information Microsoft-Windows... 17 Install... 1420 Apr 15 19:05 Information Microsoft-Windows... 17 Install... 1417 Apr 15 19:05 Information Microsoft-Windows... 17 Install... 1418 Apr 15 19:05 Information Microsoft-Windows... 17 Install...
More event log search tips
If you’re a busy systems administrator like I am, your event log scans occur either when you know there’s a problem or you at least suspect there’s one. To that point, we can pass the –EntryType parameter to filter our results based on, well, the entry type:
Get-EventLog –LogName System –EntryType Error
You should know that Get-EventLog can fetch log entries not only from the “classic” logs but also from the plethora of Application and Services logs. Let’s run a search of the Windows PowerShell log to find all entries that include the string “stopped”:
Get-EventLog –LogName "Windows PowerShell" –Message "*stopped*"
Don’t forget about PowerShell tab completion. In the previous example, you can press TAB after typing a few characters of “Windows PowerShell” to let PowerShell complete the log name for you.
We can leverage Group-Object to perform aggregations. For instance, let’s group events from the System log by EventID count:
Get-EventLog –LogName System | Group-Object –Property EventID | Sort-Object –Property Count -Descending
Time is of the essence, as it were. We can use the –Before and –After parameters of Get-EventLog to do time/date-based queries. The following query picks up all System log errors in the past seven days:
Get-EventLog –LogName System –EntryType Error –After (Get-Date).AddDays(-7)
We can do a date range as well. Let’s run the previous query, this time focusing it on a particular date:
Get-EventLog –LogName System –After 04/14/2015 –Before 04/15/2015
Note that PowerShell uses the American date format of Month/Day/Year instead of the European Day/Month/Year.
Thus far in this tutorial, we’ve searched only one log at a time. What if we need to aggregate, say, error messages across the entire Windows event log platform?
Because Get-EventLog hits only one log at a time, we’ll need to tap into Windows Management Instrumentation (WMI) to get the job done (hat tip: PowerShell.com). Actually, we’ll use the newer Get-CimInstance instead of the shopworn Get-WmiObject for this task.
The following one-liner fetches the first 100 error events from the System and Application log files of the local host:
PS C:\> Get-CimInstance -ClassName Win32_NTLogEvent -Filter 'Type="Error" AND (LogFile="System" OR LogFile="Application")' | Select -First 100 -Property TimeGenerated, Logfile, EventCode, Message TimeGenerated Logfile EventCode Message ------------- ------- --------- ------- 4/7/2015 1:46:41 PM Application 2006 There was an err... 3/19/2015 1:05:5... Application 24 Event provider P... 3/19/2015 1:05:5... Application 24 Event provider ... 4/7/2015 1:47:04 PM System 10010 The server {4545... 4/7/2015 2:03:39 AM System 10010 The server {BF6C... 4/7/2015 2:03:12 AM System 10010 The server {1B1F...
Remote event logs
Everything we’ve learned thus far concerning Get-EventLog applies 100 percent to Windows PowerShell remoting. We’ll use Invoke-Command, which employs “true” Windows Remote Management (WinRM)-based remoting and parallel execution, to query the Security log of multiple computers:
PS C:\> Invoke-Command -ComputerName dc1,vpnclient1,lync1 -ScriptBlock { Get-EventLog -LogName Security -Newest 1 | Select-Object -Property PSComputerName, TimeGenerated, EventID } TimeGenerated : 4/16/2015 8:58:30 AM EventID : 4624 PSComputerName : lync1 RunspaceId : 6896e2be-aa1e-4af9-911d-a959e88121b6 TimeGenerated : 4/16/2015 8:58:30 AM EventID : 4624 PSComputerName : vpnclient1 RunspaceId : 5b44b5cf-4e84-4ea9-8902-083618f09af7 TimeGenerated : 4/16/2015 8:58:30 AM EventID : 4624 PSComputerName : dc1 RunspaceId : 5714e2f6-297b-4442-b76b-9d1ed3cd042a
One (of many) nice things about Invoke-Command over, say, the –ComputerName parameter of Get-Wmiobject is that Invoke-Command includes the PSComputer object in the output. I think you’ll agree that it’s nice to see which remote host each event log entry comes from!
The Get-WinEvent cmdlet
Many Windows administrators are completely unaware that we have Get-WinEvent in addition to Get-EventLog. What are the differences? Two come to my mind:
- Get-WinEvent gives you much wider and deeper reach into the event logs. It can access log providers directly as well as tap into Windows event tracing logs. That said, it’s easier to delve into the content of classic event log entries with Get-EventLog.
- For remoting, Get-WinEvent uses the built-in Windows event log remoting technology instead of PowerShell remoting. Thus, you’ll find that remote log queries run faster with Get-WinEvent than with Get-EventLog.
To see what I’m talking about in terms of Get-WinEvent’s reach, run each of the following commands and note the output differences:
Get-EventLog -List Get-WinEvent –ListLog *
The “word on the street” from Windows PowerShell MVPs I’ve spoken with is that Microsoft will eventually deprecate Get-EventLog in favor of Get-WinEvent. Evidence for this position is that, for instance, some of the newer logs such as Desired State Configuration (DSC) are of the “new school” variety and are accessible only with Get-WinEvent.
Let’s close with a couple of quick Get-WinEvent examples. First, we’ll gather the most recent 10 events from the Application log:
Get-WinEvent –LogName Application –MaxEvents 10
Thanks to Richard Siddaway’s fine example, we’ll now pull the most recent 500 error messages from the Application log:
Get-WinEvent -Logname Application -MaxEvents 500 |Where-Object { $_.LevelDisplayName -eq "Error" } | Format-Table –Property Id, LevelDisplayName, Message -AutoSize
I hope you found this article helpful. Thanks for reading, and happy PowerShelling!