Pull to refresh

Централизованный сбор и обработка журналов печати Windows

Reading time12 min
Views62K
В небольших офисах далеко не всегда используются сетевые принтеры и МФУ, поэтому получить статистику использования печатающих устройств получить достаточно сложно. Тем более, если это требуется произвести с минимальными затратами. Такие данные смогут помочь определить наиболее активно используемые устройства, оценить нагрузку на них и принять своевременные решения по закупке расходных материалов, техническом обслуживании или даже замене на более экономичные и производительные. Данную задачу возможно решить без дополнительного ПО используя встроенные средства управления журналами Windows и скриптом на Powershell.

Используя поисковые системы можно найти готовые программные продукты для аудита печати в офисе, например:
Такие системы далеко не всегда подходят, так как они требуют покупки, бесплатные версии имеют ограниченную функциональность, необходима установка как центрального ПО на сервер, так и агентов на клиентские компьютеры, некоторые программы работают только с принт-сервером и так далее. Предлагаю использовать для решения задачи встроенные средства операционной системы Windows 7/2008R2 и скрипты на Powershell.

Итак, примем за исходные данные следующую информацию:
  • В организации есть домен Active Directory
  • В организации используются компьютеры c операционной системой не ниже Windows 7, сервера – с ОС не ниже Windows Server 2008R2
  • Есть как сетевые, так и локальные принтеры и МФУ.
  • Существует необходимость централизовано обрабатывать журналы печати с принтеров и иметь статистику использования и нагрузки на печатающие устройства.

Подготовка инфраструктуры


Первым делом необходимо подготовить инфраструктуру для централизованного сбора журналов событий с клиентских компьютеров.
Для работы подписки на события Windows на компьютере-источнике необходимы следующие настройки:
  1. Наличие пользователя в группе Читатели журнала событий (Event Log Readers), от имени которого будет читаться журнал
  2. Доступ по удаленному управлению (Windows Remote Management с сервера-коллектора
  3. Настроенное разрешение на пересылку событий на сервер-коллектор логов.
  4. Включенный журнал событий печати (выключен по умолчанию)

Создаем в оснастке Пользователи и Компьютеры нового пользователя, в качестве полного имени и логина указываем EventCollectorUser. Назначаем сложный пароль и ставим галочки «Запретить смену пароля пользователем» и «Срок действия пароля неограничен».

Далее создаем на контроллер домена новую групповую политику и называем ее, например, GPO-EventCollector.
В политике задаем следующие параметры:
  1. В разделе «Конфигурация компьютера — Настройка — Параметры панели управления — Службы» создаем запись службы «Автозагрузка: Автоматически (отложенный запуск)», «Имя службы — Служба удаленного управления Windows (MS-Management) (WinRM)», «Действие службы: Запуск службы»
  2. В разделе «Конфигурация компьютера — Политики — Административные шаблоны — Компоненты Windows — Удаленное управление Windows — Служба удаленного управления Windows» задать параметр «Разрешить автоматическую настройку прослушивателей: Включить» и разделе параметра «Фильтр IPv4″ поставить значение «*».
  3. В разделе «Конфигурация компьютера – Политики – Конфигурация Windows – Параметры безопасности – Брандмауэр Windows в режиме повышенной безопасности – Брандмауэр Windows в режиме повышенной безопасности – Правила для входящих подключений» создаем новое правило. Выбираем пункт «Предопределенные правила» и в списке выбираем «Удаленное управление Windows (HTTP — входящий трафик)»
  4. В разделе «Конфигурация компьютера — Политики — Административные шаблоны — Компоненты Windows — Пересылка событий» задать параметр «Настроить конечный диспетчер подписки» и в разделе параметра «SubscriptionManagers» вписать полный FQDN путь до сервера–коллектора.
  5. В разделе «Конфигурация компьютера – Политики – Конфигурация Windows – Параметры безопасности – Группы с ограниченным доступом» добавляем новую группу «Читатели журнала событий». В члены группы добавляем созданного нами пользователя EventCollectorUser.


После создания групповой политики необходимо перезагрузить целевые компьютеры или выполнить на них команду gpupdate /force.

Включить журнал печати на целевом компьютере можно через оснастку MMC «Просмотр событий» по пути «Журналы приложений и служб – Microsoft – Windows – PrintService – Работает» (кликнуть правой кнопкой мыши на журнале и выбрать «включить»). Этот вариант подходит, если компьютеров не так много.
В случае, если требуется включить журнал на большой группе ПК, то можно воспользоваться следующим способом:
  1. Подготовить текстовый файл со списком имен компьютеров. Например, d:\temp\computers.txt
  2. Запустить следующую команду от имени пользователя с правами администратора домена:
    For /F %i in (d:\temp\computers.txt) do wevtutil sl Microsoft-Windows-PrintService/Operational /e:true /r:%i


Update: Также, как верно заметил NeSvist, можно установить ключ реестра:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Channels\Microsoft-Windows-PrintService/Operational — Enabled равным 1.
Данный параметр можно раздать в групповой политике, которую мы создали выше.
Для справки: О работе с реестром в GPO

Создание подписки на события


После подготовки инфраструктуры можно переходить непосредственно к настройке подписки на события на сервере-коллекторе.
  1. Идем в оснастку «Просмотр событий – Подписки».
  2. Выбираем справа в меню «Создать подписку…»
  3. Вводим имя подписки, например, «Test Subscription». Добавляем понятное описание.
  4. Далее выбираем журнал, в который будут попадать принимаемые события. Рекомендую оставить все по умолчанию – «Перенаправленные события». Тут стоит заметить, что найти способ помещать события в журнал, созданный вручную, мне не удалось. Так что вариант по-умолчанию остается почти единственным.
  5. Далее щелкаем на кнопке «Выбрать компьютеры…». В открывшемся окне кнопкой «Добавить» добавляем необходимые компьютеры-источники. Кнопкой «Проверить» можно проверить доступность целевых машин по протоколу удаленного управления, результат покажут в информационном окне.
  6. Выбираем события, которые будем собирать с источников. Нас интересует уровень событий «Сведения» из журнала «Microsoft-Windows-PrintService/Работает» с кодом событий 307. Все остальные настройки оставляем по умолчанию.
  7. Щелкаем по кнопке «Дополнительно», в открывшемся окне выбираем пункт «определенный пользователь» и указываем созданного нами пользователя и его пароль. Настройки оптимизации доставки событий оставляем в положение «Обычная».
  8. Щелкаем «ОК» и подписка создана. При появлении событий в логе компьютеров-источников, они в течение 15 минут будут загружены на сервер-коллектор в журнал «Перенаправленные события».

Скрипт обработки собранных событий


Собранные события можно выгрузить в формате CSV средствами оснастки «Просмотр событий», но полученные данные будут не очень информативны и не позволят на их основе получить интересную статистику, которую можно показать руководству. Поэтому я предлагаю вариант обработки полученных событий средствами Powershell для удобства их дальнейшего использования.
Поиском на просторах Интернета был найден скрипт анализа журналов печати для Windows Server 2003. (http://trevorsullivan.net/2009/11/06/windows-2003-print-log-parsing-script-powershell/) Так как есть определенные различия в командлетах Powershell новых версий, и способ получения событий отличается от предложенного в статье, то скрипт был мной переработан.
Общую структуру скрипта я менять не стал, он разбит по функциям, которые легко модифицировать (в случае локализации, например). Логика работы скрипта следующая:
  1. Получаем список событий из журнала
  2. Парсим свойство Message каждого события и записываем поля в объект PrintJob
  3. Сохраняем полученный список объектов в формате CSV


Получаем список событий из журнала «Перенаправленные события» по коду 307 (на случай, если в указанный журнал пересылаются еще какие-либо события).
Function GetPrintEntriesFromLog()
{
	$PrintEntries = get-winevent -FilterHashTable @{LogName='ForwardedEvents'; ID=307;}
	return $PrintEntries
}

Создаем новый объект PrintJob. Добавляем в него требуемые поля.
Скрытый текст
Function CreatePrintJob()
{
	$PrintJob = New-Object PsObject
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name PageCount -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name UserName -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name DocumentName -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Size -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Printer -Value $null
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Time -Value $null
	return $PrintJob
}

Получаем из свойства Message события поля Имя пользователя, Имя принтера, Количество напечатанных страниц в документе, Имя документа. Вытаскиваем это все из строки регулярными выражениями. При англоязычном журнале меняем строки в регулярных выражениях на другие, соответственно.
Скрытый текст
#Парсим параметры объекта
Function ParsePrintEntry($PrintEntry)
{
	$NewPrintJob = CreatePrintJob

	$NewPrintJob.PageCount = GetPageCount $PrintEntry.Message
	$NewPrintJob.UserName = GetUserName $PrintEntry.Message
	$NewPrintJob.DocumentName = GetDocumentName $PrintEntry.Message
	$NewPrintJob.Size = GetPrintSize $PrintEntry.Message
	$NewPrintJob.Printer = GetPrinterName $PrintEntry.Message
	$NewPrintJob.Time = $PrintEntry.TimeCreated.ToString()

	return $NewPrintJob
}

#Получаем имя пользователя
Function GetUserName($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }
	$rxUserName = [regex]"которым владеет ([0-9a-zA-Z.]{1,})"
	$rxMatches = $rxUserName.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

#Получаем имя принтера
Function GetPrinterName($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }
	$rxPrinterName = [regex]"распечатан на (.{1,}) через"
	$rxMatches = $rxPrinterName.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

#Получаем размер распечатки в байтах
Function GetPrintSize($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }
	$rxPrintSize = [regex]"Размер в байтах: ([0-9]+)."
	$rxMatches = $rxPrintSize.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

#Получаем количество страниц (самый важный параметр)
Function GetPageCount($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }
	$rxPageCount = [regex]"Страниц напечатано: ([0-9]+)"
	$rxMatches = $rxPageCount.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}

#Получаем имя документа
Function GetDocumentName($PrintEntry)
{
	If ($PrintEntry -eq "" -or $PrintEntry -eq $null) { return $null }
	$rxDocumentName = [regex]", (.{1,}) которым владеет"
	$rxMatches = $rxDocumentName.Match($PrintEntry)
	return $rxMatches.Groups[1].Value
}



Основная функция. Получаем список событий и в цикле превращаем его в объект PrintJob. Потом делаем экспорт полученного списка объектов в указанный файл. Ставим кодировку UTF8 для корректного отображения кириллицы и разделитель «;» для читаемого открытия файла в Excel. Каждые 100 событий пишем лог, это удобно при отладке.
Function Main()
{
	$PrintEntries = GetPrintEntriesFromLog
	$Global:ParsedEntries = @{}; $i = 0

	ForEach ($PrintEntry in $PrintEntries)
	{	
        $ParsedEntries.Add($i, $(ParsePrintEntry $PrintEntry))
		$i++
		if ($i % 100 -eq 0)
		{ Write-Host "Processed $i records" }
	}
    $ParsedEntries.Values | Export-Csv "D:\PrintReports\PrintUsageReport.csv" -NoTypeInformation -Encoding UTF8 -Delimiter ';'
}

#Запускаем главную функцию.
Main 

При подготовке регулярных выражений для скрипта использовался сайт Regex101.com. Сайт понятный, с краткой документацией, подсветкой результата поиска в исходной строке, расшифровкой смысла регулярного выражения на лету. Очень информативно и удобно, рекомендуется к употреблению.
Полученный скрипт можно применять по требованию, запуская в командной оболочке PowerShell, а можно назначить в качестве задания в Планировщике заданий.
Для этого необходимо:
  1. Открыть Планировщик заданий (Пуск – Администрирование – Планировщик заданий)
  2. Выбрать пункт «Создать простую задачу…»
  3. Ввести имя задачи, описание, выбрать интервал выполнения
  4. Выбрать действие «запустить программу». В качестве программы выбрать «C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe», а в качестве аргумента – путь к скрипту.
  5. Открыть свойства задачи и выставить переключатель «Выполнять вне зависимости от регистрации пользователя», выбрать пользователя, от имени которого должна выполняться задача и ввести пароль от него.

Таким образом, в указанном при создании скрипта каталоге будет с заданным интервалом обновляться файл со списков напечатанных документов.

Решение проблем


  1. Если при запуске скрипта возникает ошибка вида:
    Невозможно загрузить файл D:\PrintReports\PrintInfo.ps1, так как выполнение сценариев отключено в этой системе. Для получения дополнительных
    сведений см. about_Execution_Policies по адресу go.microsoft.com/fwlink/?LinkID=135170.
    + CategoryInfo: Ошибка безопасности: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId: UnauthorizedAccess
    это означает, что политика безопасности для исполняемых скриптов Powershell не позволяет запустить программу. Для запуска созданного скрипта необходимо понизить уровень безопасности до «Remote Signed», то есть будет разрешено выполнение любых сценариев, созданных локально, а сценарии, созданные на удаленных системах, выполняются только в том случае, если подписаны доверенным издателем. Для этого в консоли Powershell, запущенной от имени администратора, необходимо выполнить команду Set-ExecutionPolicy RemoteSigned.
  2. Для тестирования пересылки и сбора событий можно использовать инструмент командной строки eventcreate.exe. Эта утилита позволяет создавать события вручную в определенном журнале. К примеру, можно создать событие с ID 100 в логе Приложение при помощи следующей команды: eventcreate /t error /id 100 /l application /d «Custom event». Если все настроено правильно и есть сбор событий из указанного журнала, то событие окажется на сервере-коллекторе в течение минуты.
    Если событие не попало на сервер-коллектор, необходимо проверить следующее:
    • Если параметры задавались групповой политикой, то проверить, что политика применяется. При необходимости, можно принудительно применить групповую политику командой gpupdate /force
    • Статус службы Windows Remote Management (WinRM) (Служба удаленного управления Windows). Служба должна быть запущена и настроена на автоматический запуск. При необходимости на клиентском компьютере можно сконфигурировать данную службу командой winrm quickconfig. Эта команда конфигурирует службу WinRM, создает прослушиватель winrm и создает правило исключения брандмауэра.
      Для проверки, что сервер-коллектор может соединиться с компьютером-источником с помощью WinRM можно воспользоваться следующей командой: winrm id -remote:<Имя целевого компьютера> -u:<Имя пользователя> -p:<Пароль>. Указываются учетная запись и пароль пользователя, имеющего возможность подключения по winrm.
    • Пользователь EventCollectorUser входит в состав группы Читатели журнала событий. Только члены этой группы могут читать события на конкретном компьютере.


Что можно улучшить


Предложенный скрипт обработки не является универсальным решением и может быть улучшен в различных направлениях, например,
  • Выборка событий по времени (за день, неделю, месяц) и запись их в соответствующие отдельные листы электронной таблицы или выходные CSV-файлы. Можно предусмотреть задание диапазона дат при помощи входных параметров скрипта.
  • Добавление графического представления данных с использованием сводных таблиц по пользователям и печатающим устройствам и графиков. Такой отчет можно сразу класть на стол руководителю.
  • Настроить архивирование и ротацию журнала «Перенаправленные события», добавить функцию анализа соответствующих архивных файлов. Это будет разумно при работе с достаточно большим числом компьютеров (более 50).

Итак, в этой статье я постарался показать принцип централизованного сбора событий с журналов Windows и их дальнейшей обработки средствами Powershell. Данный способ подходит для решения задачи в небольших организациях. Разумеется, специализированное программное обеспечение справляется с поставленными целями более эффективно, но оно дорого, более громоздко, его установка приносит IT-специалисту меньше новых знаний и навыков, понимания работы обслуживаемого им программного обеспечения.

При подготовке данной статьи были использованы следующие материалы и источники:
  1. mcp.su/windows-server-2008/event-collector-in-windows-server-2008
  2. windowsnotes.ru/powershell-2/nastrojka-udalennogo-vzaimodejstviya-v-powershell-chast-1
  3. social.technet.microsoft.com/Forums/en-US/8e7399f6-ffdc-48d6-927b-f0beebd4c7f0/enabling-print-history-through-group-policy?forum=winserverprint
  4. mywinsysadm.wordpress.com/2012/07/16/powershell-audit-printer-event-logs
  5. www.winblog.ru/admin/1147767392-29031101.html
  6. windowsitpro.com/security/q-what-are-some-simple-tips-testing-and-troubleshooting-windows-event-forwarding-and-collec


Update: Как заметили NeSvist и selenite, привязка к конкретной локализации и разбор ее регулярными выражениями — не самое эффективное решение. В связи с этим, привожу вариант обработки собранных событий с использованием их представления в XML. Решение получилось более аккуратным и понятным.

Заголовок спойлера
# Получаем события из лога      
$Events = Get-Winevent -FilterHashTable @{LogName='ForwardedEvents'; ID=307;}      

# Массив заданий печати
$Jobs = @()
ForEach ($Event in $Events) {            
    # Конвертируем событие XML            
    $eventXML = [xml]$Event.ToXml()
    # Создаем новый объект задания печати и заполняем поля из XML-представления события            
    $PrintJob = New-Object PsObject
    Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name PageCount -Value $eventXML.Event.UserData.DocumentPrinted.Param8
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name UserName -Value $eventXML.Event.UserData.DocumentPrinted.Param3
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name DocumentName -Value $eventXML.Event.UserData.DocumentPrinted.Param2
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Size -Value $eventXML.Event.UserData.DocumentPrinted.Param7
	Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Printer -Value $eventXML.Event.UserData.DocumentPrinted.Param5
    # Приводим дату из формата SystemTime к обычному представлению.
    $date = Get-Date $eventXML.Event.System.TimeCreated.SystemTime
    Add-Member -Force -InputObject $PrintJob -MemberType NoteProperty -Name Time -Value $date
    # Добавляем задание печати к массиву
    $Jobs += $PrintJob
}            

# Выводим список полученных заданий печати в CSV          
$Jobs | Export-Csv D:\PrintReports\events.csv -NoTypeInformation -Encoding UTF8 -Delimiter ';'  

Tags:
Hubs:
+8
Comments18

Articles

Change theme settings