В сборе данных из системного журнала удалённого хоста в домене нет ничего сложного. Этот функционал уже присутствует в системе, что называется, из коробки.
Вот только двигаемся в сторону системных логов мы далеко не из праздного любопытства. Как правило, то, что хорошо работает — особого внимания к себе и не требует. Ибо, как гласит самая главная мудрость:
Работает — не трогай!
Итак, сегодняшний пост не просто о вынужденном централизованном сборе событий выключения удалённого хоста. Этот пост больше про отчётность, которая частенько пригождается.
Мне требовалось написать скрипт, который бы проводил сбор нужных диагностических материалов с удалённого хоста и все собранные данные монтировал в html документ.
Для разработки ps1-скрипта я взял за основу 8 основных событий выключения компьютера. Велика вероятность, что какие-то события я совершенно незаслуженно обделил своим вниманием. Это достаточно легко откорректировать, но об этом чуть ниже.
Список событий, на которые я обратил внимание при написании кода:
- Событие 41 (Kernel-Power):
- Это событие указывает на то, что система была выключена или перезагружена, не завершив работу должным образом. Это может происходить из-за сбоя питания, сбоя оборудования или преднамеренной перезагрузки без должного завершения работы. Событие 41 важно для диагностики проблем, связанных с блокировкой питания или некорректной работой системы.
- Событие 6008 (EventLog):
- Это предупреждение регистрируется в случае, если система была неожиданно выключена. Оно может указывать на неожиданные выключения питания или на сбои системы. Это событие может помочь определить время и причины внезапного завершения работы.
- Событие 1074 (User32):
- Это событие фиксирует обычное завершение работы или перезагрузку системы, инициированное приложением или самим пользователем. В событии содержится информация о том, кто инициировал завершение работы, а также причина и код завершения.
- Событие 6006 (EventLog):
- Это событие указывает на то, что служба журналирования событий была нормальным образом остановлена. Обычно оно фиксируется перед завершением работы системы и может использоваться для анализа завершения работы системы.
- Событие 6005 (EventLog):
- Это событие означает, что служба журналирования событий была нормально запущена. Оно фиксируется при запуске операционной системы и является полезным индикатором для определения времени и состояния системы при загрузке.
- Событие 6001 (EventLog):
- Это событие может указывать на проблемы с журналом событий или на ошибочное состояние. Оно обычно фиксируется, когда система не может записывать события в журнал. Это событие может помочь при диагностике проблем с журналированием.
- Событие 6002 (EventLog):
- Это также событие, связанное с работой службы журналирования событий. Оно указывает на то, что служба была успешно запущена, и журнал событий снова доступен для записи новых данных.
- Событие 63 (EventLog):
- Событие 63 указывает на запись в журнале событий из-за проблем в работе системы. Это событие может быть связано с пользовательскими или системными действиями, о которых необходимо проинформировать администраторов.
Эти события я включил в массив $events. Массив можно с лёгкостью расширить или же наоборот оставить там только необходимые коды событий. Зависит от Ваших целей.
Код скрипта:
# Заголовок скрипта #
[System.Console]::Title = "Выгрузка событий выключения удалённого компьютера"
# Ввод имени хоста пользователем #
$remotehostname = Read-Host "Введите имя удалённого компьютера"
# Функция вывода сообщения об ошибке #
function ErrorMsg($msgstring)
{
Write-Host
Write-Host -ForegroundColor Red $msgstring
Start-Sleep(10)
Exit
}
# Обработка пустого значения параметра #
if ([string]::IsNullOrWhiteSpace($remotehostname))
{
ErrorMsg("Для работы скрипта нужно указать имя удалённого компьютера!")
}
$ping = Test-Connection -ComputerName $remotehostname -Count 1 -Quiet
if ($ping)
{
$events = @("41", "6008", "1074", "6006", "6005", "6001", "6002", "63")
$endDate = Get-Date
$startDate = $endDate.AddDays(-30)
$outputFile = "$PSScriptRoot\$remotehostname.html"
$results = @()
$htmltitle="События выключения для $remotehostname"
try
{
Write-Host -ForegroundColor Yellow "Начат сбор данных с $remotehostname за последние 30 дней..."
foreach ($event in $events)
{
Write-Host -ForegroundColor Yellow "Поиск события с кодом: $event"
try
{
# Получение результатов событий #
$eventResults = Get-WinEvent -ComputerName $remotehostname -FilterHashtable @{logname='System'; id=$event; StartTime=$startDate; EndTime=$endDate} -ErrorAction Stop |
Select-Object TimeCreated, Id, Message
# Проверка, получены ли результаты #
if ($eventResults)
{ $results += $eventResults }
else
{ Write-Host -ForegroundColor Yellow "Нет событий с кодом: $event в указанный период." }
}
catch
{
Write-Host -ForegroundColor Red "Ошибка при получении событий для кода: $event. $($_.Exception.Message)"
}
}
# Проверка, есть ли результаты перед сортировкой и экспортом #
if ($results.Count -gt 0)
{
# Удаление переносов строк из поля Message #
$results = $results | ForEach-Object {
$_.Message = $_.Message -replace "`r`n", ' '
$_.Message = $_.Message -replace "`n", ' '
$_.Message = $_.Message -replace "`r", ' '
$_ }
# Сортировка результатов по времени создания #
$sortedResults = $results | Sort-Object TimeCreated
# Создание HTML
$html = @"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>$htmltitle</title>
<style>
body { font-family: 'Arial', 'Helvetica', sans-serif; font-size: 15px; margin: 20px; }
h1 { color: #2c3e50; text-align: center; }
h2 { color: #2980b9; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 10px; border: 1px solid #ccc; text-align: center; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>$htmltitle</h1>
<table>
<tr>
<th>Время создания</th>
<th>Идентификатор события</th>
<th>Сообщение</th>
</tr>
"@
foreach ($result in $sortedResults) {
$html += "<tr><td>$($result.TimeCreated)</td><td>$($result.Id)</td><td>$($result.Message)</td></tr>"
}
$html += @"
</table>
</body>
</html>
"@
Set-Content -Path $outputFile -Value $html -Encoding UTF8
Write-Host -ForegroundColor Green "Результаты успешно экспортированы в $outputFile."
Start-Sleep(5)
}
else
{
Write-Host -ForegroundColor Yellow "Нет доступных результатов для экспорта."
Start-Sleep(5)
}
}
catch
{
ErrorMsg("Во время сбора данных с удалённого компьютера $remotehostname что-то пошло не так...")
}
}
else
{
ErrorMsg("Удалённый компьютер $remotehostname не доступен!")
}
Сразу после запуска скрипт интересуется именем компьютера, с которого нужно собрать диагностические данные. Если компьютер не доступен в сети будет выведена соответствующая ошибка.
После получения имени компьютера скрипт начнёт сбор данных и создание html-отчёта. Все данные будут упорядочены по возрастанию и свёрстаны в таблицу.
Сбор данных о событиях будет осуществляться только за последние 30 дней. Если нужно изменить диапазон — отредактируйте переменную $startDate:
$startDate = $endDate.AddDays(-30)
Здесь нужно заменить число 30 на необходимое количество дней, которые скрипт отнимет от текущей даты, сформируя нужный диапазон.