We rise up for the things we believe in over and over again

MS в своем стиле. Давно такой зубодробиловки не встречал. Впрочем, из стана *nix, наверняка, наоборот, могут послышаться возгласы одобрения, ибо "все есть файл".
Задача. Необходимо сделать так, чтобы по возникновению в журнале Windows события с определенным кодом администратору (или другому причастному к процессу) на почту падало письмо с содержанием этого сообщения. Заскриптовать сам процесс отправки письма - не проблема. Проблема в это письмо утрамбовать содержимое самого сообщения.
Что попытались сделать? Нам известен код события. Что ж, по возникновению этого события ищем все события с этим кодом, сортируем их в порядке убывания, отсекаем самое первое в выборке. Это и будет искомое. Дальше по скрипту вытаскиваем его содержимое, трамбуем в письмо, отсылаем депешу. Казалось бы, проблема решена.
А вот и нет. Проблемы начинаются, когда копируется сразу несколько мелких файлов. В этом случае вся наша выборка становится сбойной, и в итоге события теряются, письма приходят только на часть их. Что же делать?
Вечерний треп с  Cybeon принес весьма любопытные результаты. У каждого события в логе есть уникальный номер. Вот вытащить бы его, отдать скрипту, а скрипт уже пусть ищет именно это конкретное событие и пакует его в письмо. Идея хорошая, но как? А вот так:
blogs.technet.com/b/otto/archive/2011/08/24/tri...
Как уже выше было сказано - все есть файл. Именно файл нам на помощь и приходит.
Вкратце - создаем болванку задания, экспортируем его в XML файл и убиваем болванку. Она нам больше не нужна. А потом правим полученный XML файл и вносим туда следующее:

После чего импортируем этот файл в оснастку назначенных заданий. После таких вот премудростей наше задание будет отдавать три параметра: имя журнала, в котором возникло наше событие (eventChannel), его критичность (eventSeverity) и, та-дааааам, уникальный код этого события (eventRecordID).
Итак, сами значения мы выцарапали. Как их передать в скрипт? Через параметры командной строки:
powershell -command %PATH-TO-sсript% -eventChannel $(eventChannel) -eventRecordID $(eventRecordID) -eventSeverity $(eventSeverity)

А последний шаг - уже в том скрипте, который будет выполнять все действия по отсылке писем, первой строкой объявляем полученные параметры:
param($eventChannel,$eventRecordID,$eventSeverity)

На этом все. Но осталась важная ремарка - не дай вышние ошибиться хоть где-нибудь в регистре в именах всех этих переменных. Как оказалось - регистрозависимо тут все.

Почему передачу данных в скрипты из журналов до сих пор не сделали более менее дружелюбной к администраторам - об этом остается только гадать. Несмотря на все улучшения в Windows 2012 - пилить ее еще и пилить.

@музыка: Eric "Erock" Calderone - Killer Instinct Meets Metal

@темы: PowerShell, Scripting

Комментарии
24.10.2014 в 06:13

Тотальная неудачница и убийца жёстких дисков.
1. Забавно смотреть на иных товарищей, которые во времена Восьмёрки узнают про NT Namespace и начинают орать, что "MS наконец пошли по пути никсов", хотя этот неймспейс существует столько же, сколько сама NT.

2. Не поняла, чем WMI не угодил?
24.10.2014 в 08:39

We rise up for the things we believe in over and over again
Блин, спать охота зверски, потому не слишком въехал в смысл комментария. Какой Namespace имеется в виду, и чего ты хотела выполнить при помощи WMI?
24.10.2014 в 16:55

Тотальная неудачница и убийца жёстких дисков.
Какой Namespace имеется в виду

Иерархия объектов в ядре. Запусти WinObj ^^

чего ты хотела выполнить при помощи WMI?

EventLog читать. Ты же это пытаешься делать?
24.10.2014 в 19:17

We rise up for the things we believe in over and over again
Запусти WinObj ^^
Да имена объектов известны и так. Проблема-то в другом (см. ниже).

EventLog читать. Ты же это пытаешься делать?
Читать EventLog можно по-всякому, результат будет один и тот же - набор записей согласно выборке. Вот с выборкой и была проблема.
Опишу ситуацию более подробно. Сеттинг - есть папка, куда копируются/удаляются файлы. Периодически возникают ситуации, когда какая-нибудь "умная леди" сотрет нужный файл, а потом выставляет претензии, мол, файл сам удалился.
Задача - в момент копирования в папку файла или удаления из папки файла нужно получать уведомления об этом событии. Само событие при помощи штатных методов виндов можно записать в эвентлог. Дальше цифры с потолка, ибо точно уже не помню коды: на копирование файла формируется событие с кодом 1, на удаление - с кодом 2.
Что хотим сделать? Как только появляется событие с кодом 1, привязываем к этому событию powershell-скрипт, который делает следующее - выбирает из всего эвентлога события с кодом 1, сортирует их в порядке убывания, берет самое верхнее. По задумке, это и будет нужное сообщение. Вытаскиваем его, парсим, отправляем письмо. Проблема в задержках при формировании выборки. И начинаются эти проблемы, если в журнале возникло сразу несколько сообщений с кодом 1, касающиеся разных файлов. Куча таких событий будет просто пропущена.
Как это можно обойти. У каждого события в эвент-логе есть не только код события, но и код записи, по которому в любой момент времени можно выбрать эту и только эту запись. И вот тут главная задача - каким образом этот код записи передать в скрипт? Код события-то был проще, он известен заранее. А код записи будет каждый раз меняться :) И об этом-то и речь в посте.
Вот и получается, что проблема не в том, чем эвент-лог читать, хоть повершеллом, хоть VBS и WMI запросами, хоть вообще через Wevutil :)
25.10.2014 в 14:44

Тотальная неудачница и убийца жёстких дисков.
Читать Event Log раз в десять секунд? Запоминать последнее нужное событие и присылать в письме всё новые события, появившиеся после? В самом событии имя файла указывается?
25.10.2014 в 14:53

We rise up for the things we believe in over and over again
Такой вариант предлагался. Был отвергнут по причине требования реал-тайма. Пришел файл - хочу уведомление. Маразм пользователей иногда просто зашкаливает, тебе ли не знать )
Впрочем, не будь этого маразма, не было бы интересной задачи )
25.10.2014 в 16:03

Тотальная неудачница и убийца жёстких дисков.
Десятисекундная задержка делает отличия от рилтайма ускользающе малыми.
25.10.2014 в 16:03

Тотальная неудачница и убийца жёстких дисков.
По крайней мере я бы решала задачу именно так.
25.10.2014 в 17:08

We rise up for the things we believe in over and over again
делает отличия от рилтайма ускользающе малыми
it depends... :)
25.10.2014 в 18:47

Тотальная неудачница и убийца жёстких дисков.
От чего?
25.10.2014 в 23:18

We rise up for the things we believe in over and over again
Прежде всего - от хотелок юзверей.
А во-вторых - мы это попытались сделать, так, на всякий случай. Если происходит одновременная выкладка кучки очень маленьких файлов, когда события о них генерятся в пределах одной секунды, такой подход приносит хреновые результаты. Powershell вообще не быстрая штука. Свою лепту в лаг вносит еще и таск шедулер. В итоге получается, что даже с разовым опросом в 10 секунд есть вероятность (пусть и низкая), что какое-то событие будет утеряно в выборке. Получается, что система будет работать, грубо говоря, через раз. А в таком случае систему работающей назвать просто сложно.
Потому приходится искать железобетонное решение. Да, оно несколько сложнее в настройке, зато оно дает гарантию, что ни одно событие в логе не будет упущено.
Если честно, я сам не смогу тебе сказать, почему требуется именно такая надежность и скорость этих грешных уведомлений, мы это решение не для своей конторы искали. А коллега, который и присматривает за той конторой, сам не шибко много может рассказать об этом всем. Так что приходится довольствоваться малым - есть задача, ее нужно решить. Что ж, решили. Попутно оба обогатив свой багаж знаний в области настройки планировщика и powershell'a. А это тоже неплохо, согласись же :)
26.10.2014 в 00:20

Тотальная неудачница и убийца жёстких дисков.
Скажу на языке VBsсript. Берёшь в одну руку Do Loop, а в другую - WScript.Sleep, и - опа! - шедулер уходит заниматься более важными вещами. Но ты не останавливаешься на этом и берёшь в одну руку Do While Event.Date = LastSentDate, а в другую - LastSentDate = FirstEvent.Date, и - опа! - система начинает работать стабильно.
26.10.2014 в 00:21

Тотальная неудачница и убийца жёстких дисков.
Do ... Loop Until Event.Date = LastSentDate, конечно же.
26.10.2014 в 00:23

Тотальная неудачница и убийца жёстких дисков.
Если что, фишка в том, что скрипт крутится в цикле постоянно и запускается только один раз, а за одну итерацию обрабатывается не одно событие, а все, какие набежали с последнего раза.
26.10.2014 в 13:04

We rise up for the things we believe in over and over again
можно попробовать и так решить. Посмотрим, что в итоге приживется. Пока все же остановились на связке PS+EventLog.
26.10.2014 в 19:09

Тотальная неудачница и убийца жёстких дисков.
Мне такое решение кажется очевиднее и проще ^^