Posts on this page:
По мотивам темы на форуме - http://forums.microsoft.com/TechNet-RU/ShowPost.aspx?PostID=4255895&SiteID=40. PowerShell без проблем может управлять восстановлением системы - SystemRestore средствами WMI. За это отвечает классы
Примечание: SystemRestore доступно только на клиентских версиях - Windows XP/Windows Vista. В серверных редакицях Windows Server нереализовано никак.
вот так выглядит GUI окно системы восстановления в Windows Vista:
В предыдущей части мы рассмотрели возможности управления журналами событий средствами .NET Framework, а так же рассмотрели проблематику использования .NET (из моего предыдущего блога) - Странности Get-Eventlog и не совсем богатый функционал. В этом посте мы разберём данный вопрос, но с использованием WMI и все примеры будут выполняться системе под управлением Windows Vista.
В WMI за журнал событий отвечают следующие классы:
WMI, как и .NET поддерживает удалённую работу с использованием ключа -ComputerName, поэтому на этом заострять внимание не будем. За публикацию списка журналов в системе отвечает класс Win32_NTEventlogFile:
В продолжении темы работы с журналом собитый (EventLog) хочу немного рассказать об удалённой работе с EventLog и основными задачами управления. При этом хочу отметить, что у нас есть 2 различных механизма управления:
Но в зависимости от архитектуры ОС оба метода обладают различными характеристиками по времени работы, отображению событий и нагрузке системы. Забегая вперёд скажу, что использование чистого .NET в Windows Server 2003 занимает хоть и немного больше времени, но загрузка процессора ниже, чем у WMI. А вот в Windows Vista .NET работает быстрее по времени и с меньшей нагрузкой на процессор. Однако, учитывая нюансы, которые изложены здесь: Странности Get-Eventlog и тот факт, что командлет Get-Eventlog основан на .NET, то его применимость в условиях Windows Vista/2008 весьма сомнительна. Поэтому вариант с использованием .NET я буду писать применимо для Windows XP/2003.
Начнём с простого: управление журналом событий реализовано в классе System.Diagnostics.Eventlog. Для перечисления списка журналов можно воспользоваться статическим методом GetEventLogs. Как известно, в PowerShell статические методы указываются после двойного знака двоеточия - ::
[Administrator] [System.Diagnostics.EventLog]::GetEventLogs("dc1") Max(K) Retain OverflowAction Entries Name ------ ------ -------------- ------- ---- 16 384 0 OverwriteAsNeeded 289 Application 512 0 OverwriteAsNeeded 45 Directory Service 512 7 OverwriteOlder 10 DNS Server 512 0 OverwriteAsNeeded 52 File Replication Service 131 072 0 OverwriteAsNeeded 12 369 Security 16 384 0 OverwriteAsNeeded 498 System 15 360 0 OverwriteAsNeeded 625 Windows PowerShell [Administrator]
При этом обратите внимание на аргумент в скобках - в них можно указывать имена удалённых компьютеров для получения списка журналов с них. В данном случае я посмотрел список журналов на контроллере домена. Чтобы получить события из журнала нужно создать объект класса System.Diagnostics.Eventlog:
New-Object System.Diagnostics.Eventlog("Application","dc1")
В скобках первым аргументом указывается имя жрунала, а вторым аргументом - имя компьютера. Если имя не будет указано, то будет использоваться локальный компьютер:
[Administrator] $EventLog = new-Object System.Diagnostics.Eventlog("Application","dc1") [Administrator] $EventLog | gm -MemberType property TypeName: System.Diagnostics.EventLog Name MemberType Definition ---- ---------- ---------- Container Property System.ComponentModel.IContainer Container {get;} EnableRaisingEvents Property System.Boolean EnableRaisingEvents {get;set;} Entries Property System.Diagnostics.EventLogEntryCollection Entries {get;} Log Property System.String Log {get;set;} LogDisplayName Property System.String LogDisplayName {get;} MachineName Property System.String MachineName {get;set;} MaximumKilobytes Property System.Int64 MaximumKilobytes {get;set;} MinimumRetentionDays Property System.Int32 MinimumRetentionDays {get;} OverflowAction Property System.Diagnostics.OverflowAction OverflowAction {get;} Site Property System.ComponentModel.ISite Site {get;set;} Source Property System.String Source {get;set;} SynchronizingObject Property System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;} [Administrator]
Заодно мы и посмотрели свойства полученного объекта. Для получения списка логов нужно воспользоваться свойством Entries:
[Administrator] $EventLog.Entries | select -last 5 Index Time Type Source EventID Message ----- ---- ---- ------ ------- ------- 285 Nov 29 21:05 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 286 Nov 29 21:35 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 287 Nov 29 21:40 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 288 Nov 29 21:43 Info SceCli 1704 Security policy in the Group policy objects has been applied ... 289 Dec 08 20:19 Info SceCli 1704 Security policy in the Group policy objects has been applied ... [Administrator]
Кстати говоря, если посмотреть вывод команды Get-Eventlog, то он будет идентичный. Т.е. можно смело предположить, что командлет Get-Eventlog использует именно механизмы .NET. А теперь попробуем рассмотреть статические методы данного класса:
[Administrator] $EventLog | gm -MemberType methods -Static TypeName: System.Diagnostics.EventLog Name MemberType Definition ---- ---------- ---------- CreateEventSource Method static System.Void CreateEventSource(String source, String logName), static System.... Delete Method static System.Void Delete(String logName), static System.Void Delete(String logName... DeleteEventSource Method static System.Void DeleteEventSource(String source), static System.Void DeleteEvent... Equals Method static System.Boolean Equals(Object objA, Object objB) Exists Method static System.Boolean Exists(String logName), static System.Boolean Exists(String l... GetEventLogs Method static System.Diagnostics.EventLog[] GetEventLogs(), static System.Diagnostics.Even... LogNameFromSourceName Method static System.String LogNameFromSourceName(String source, String machineName) ReferenceEquals Method static System.Boolean ReferenceEquals(Object objA, Object objB) SourceExists Method static System.Boolean SourceExists(String source), static System.Boolean SourceExis... WriteEntry Method static System.Void WriteEntry(String source, String message), static System.Void Wr... WriteEvent Method static System.Void WriteEvent(String source, EventInstance instance, Params Object[... [Administrator]
Первый метод, CreateEventSource позволяет создать свой источник событий в эвентлоге. А так же свой журнал:
[Administrator] [System.Diagnostics.EventLog]::CreateEventSource("MySource", "PowerShell CustomLog", "dc1") [Administrator] [System.Diagnostics.EventLog]::GetEventLogs("dc1") Max(K) Retain OverflowAction Entries Name ------ ------ -------------- ------- ---- 16 384 0 OverwriteAsNeeded 289 Application 512 0 OverwriteAsNeeded 45 Directory Service 512 7 OverwriteOlder 10 DNS Server 512 0 OverwriteAsNeeded 52 File Replication Service 512 7 OverwriteOlder 0 PowerShell CustomLog 131 072 0 OverwriteAsNeeded 12 369 Security 16 384 0 OverwriteAsNeeded 498 System 15 360 0 OverwriteAsNeeded 625 Windows PowerShell [Administrator]
Вот так очень просто мы создали собственный источник событий (здесь не отображён) и свой собственный журнал событий! Причём, всё так же удалённо! Для удаления журнала и источника события необходимо использовать статические методы DeleteEventSource и Delete:
[System.Diagnostics.EventLog]::Delete("LogName") [System.Diagnostics.EventLog]::DeleteEventSource("SourceName")
Чтобы управлять размером журнала нужно использовать свойство MaximumKilobytes. Увеличим размер журнала PowerShell CustomLog с 512 килобайт до 2-х мегабайт:
[Administrator] $EventLog = new-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1") [Administrator] $EventLog.MaximumKilobytes = 2048 [Administrator] [System.Diagnostics.EventLog]::GetEventLogs("dc1") Max(K) Retain OverflowAction Entries Name ------ ------ -------------- ------- ---- 16 384 0 OverwriteAsNeeded 289 Application 512 0 OverwriteAsNeeded 45 Directory Service 512 7 OverwriteOlder 10 DNS Server 512 0 OverwriteAsNeeded 52 File Replication Service 2 048 7 OverwriteOlder 0 PowerShell CustomLog 131 072 0 OverwriteAsNeeded 12 369 Security 16 384 0 OverwriteAsNeeded 498 System 15 360 0 OverwriteAsNeeded 625 Windows PowerShell
Если сравнить 2 последних вывода, то заметим, что размер действительной изменился. Давайте запишем какое-нибудь событие в журнал. Для этого воспользуемся методом WriteEntry:
[Administrator] $EventLog.Source = "MySource" [Administrator] $EventLog.WriteEntry("Hello World!", "Information") [Administrator] $EventLog = new-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1") [Administrator] $EventLog.entries Index Time Type Source EventID Message ----- ---- ---- ------ ------- ------- 1 Dec 08 22:55 Info MySource 0 Hello World! [Administrator]
Сперва мы указали источник события, потом применили метод WriteEntry, где ввели текст и тип события. Последней строкой мы убедились, что событие успешно записалось! Что касается типа события, то тут самому особо придумать ничего нельзя, потому что список типов жёстко регулируется:
[Administrator] [enum]::GetNames([System.Diagnostics.EventLogEntryType]) Error Warning Information SuccessAudit FailureAudit [Administrator]
Чтобы очистить журнал от событий нужно использовать метод Clear:
$EventLog = New-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1") $EventLog.Clear()
И напоследок расскажу о действии при заполнении журнала. Для этого используется метод ModifyOverflowPolicy и метод может иметь следующие свойства:
Эти свойства, думаю, в представлении не нуждаются, поэтому покажу примеры их использования:
$EventLog = New-Object System.Diagnostics.Eventlog("PowerShell CustomLog","dc1") $EventLog.ModifyOverflowPolicy("OverwriteAsNeeded",$null) $EventLog.ModifyOverflowPolicy("DoNotOverwrite",$null) $EventLog.ModifyOverflowPolicy("OverwriteOlder",14)
К сожалению, средствами .NET невозможно архивировать журналы собитий, как это я описывал в предыдущей статье. Вот, вроде рассказал всё, что мне казалось интересным по этой теме. В следующий раз расскажу об управлении эвентлогом средствами WMI.
Снова навеяно темой на форуме TechNet-Ru. Уже не первый раз встречаю топики про архивирование журналов событий для последующего хранения в оффлайне. Безусловно не стоит пытаться скопировать .evt файл из папки Windows, поскольку файлы открыты и заблокированы (как и .pst файлы при запущенном MS Outlook). Для архивирования журнала скриптом нужно либо использовать Volume Shadow Copy либо использовать особые методы (например, вот так: http://support.microsoft.com/kb/312571). В качестве особых методов можно так же выделить использование WMI, которое позволяет решить поставленную задачу и добавит нам удалённой управляемости.
Полностью опираться на тему форума нельзя, ибо задача поставлена некорректно. Смысла в ежедневном удалении логов с машин без предварительного архивирования нету совсем (иначе вы потеряете точку отправления в решении проблемы, когда она возникнет и зачастую единственным выходом будет переинсталляция или восстановление из бакупа). Поэтому немного переформулируем задачу и напишем решение.
Задача: проводить с некоторой периодичностью архивацию журнала событий в файл. Убедиться, что бэкап сделан и после этого очистить журналы. В противном случае сделать что-нибудь другое.
За отправную точку возьмём класс Win32_NTEventlogFile. Давайте, вызовем его:
Из-за сильной занятости на работе в последнее время не получается уделять время блогу. Но сегодня выкроил немного времени и решил рассказать одну, на мой взгляд, интересную тему, которую каждый решает по-своему и не всегда очень удобным способом (о чём свидетельствует книга по PowerShell и ньюсгруппы). И на это есть весомые причины. А так же расскажу о весьма странном поведении ряда командлетов (на примере Get-Acl) при работе с файлами.
Давайте рассмотрим вопрос с начала и выйдем на нашу проблему. При поиске группы файлов PowerShell, как и многие другие командные оболочки, позволяет создавать маску поиска, как "*", "?". Безусловно с ними проблем быть не может, т.к. эти знаки в именах файлов встречаться не могут. Но если в имени файла используется символ открывающейся или закрывающейся квадратной скобки - "[" и "]", соответственно. Данные символы допустимы для именования файлов, но интерпретатор PowerShell их воспринимает как мета-символы подстановочного шаблона/маски, например:
[a-z]* - все объекты, которые начинаются с букв латинского алфавита
*[0-9] - все объекты, которые заканчиваются любой цифрой и т.д.
При этом встаёт вопрос, как сказать интерпретатору, что квадратная скобка в данном случае является литералом, а не мета-символ? Для начала я в папке создал 3 файла, 2 из которых содержат квадратные скобки:
[Folder] dir Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:47 11 text.txt -a--- 02.12.08. 0:47 296 text[0].txt -a--- 02.12.08. 0:48 769 text[text.txt [Folder]
Давайте просмотрим содержимое файла text[text.txt:
[Folder] Get-Content text[text.txt
Get-Content : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard pattern is not valid: text[
text.txt
At line:1 char:12
+ Get-Content <<<< text[text.txt
[Folder]
занятно, не правда ли? Здесь мы столкнулись с проблемой, что интерпретатор PowerShell воспринял скобку в имени файла за мета-символ. Бороться с этим можно различными методами. Как вариант - использовать параметр -LiteralPath, что будет говорить интерпретатору о буквальном чтении пути. Проверим:
[Folder] Get-Content -LiteralPath text[text.txt Quest, Quest Software, the Quest Software logo, Aelita, Benchmark Factory, Big Brother, DataFactory, DeployDirector, ERDisk, Fastlane, Final, Foglight, Funnel Web, I/Watch, Imceda, InLook, InTrust, IT Dad, JClass, JProbe, LeccoTech, LiveReorg, NBSpool, NetBase, PerformaSure, PL/Vision, Quest Central, RAPS, SharePlex, Sitraka, SmartAlarm, Speed Change Manager, Speed Coefficient, Spotlight, SQL Firewall, SQL Impact, SQL LiteSpeed, SQL Navigator, SQLab, SQLab Tuner, SQLab Xpert, SQLGuardian, SQLProtector, SQL Watch, Stat, Stat!, Toad, T.O.A.D., Tag and Follow, Vintela, Virtual DBA, and XRT are trademarks and registered trademarks of Quest Software, Inc. Other trademarks and registered trademarks used in this guide are property of their respective owners. [Folder]
Как видите, мы получили содержимое файла (там отрывок из дисклаймера Quest Software). Однако, параметром -LiteralPath обладают далеко не все командлеты. Например, Get-Acl. У него нету такого параметра, поэтому получить ACL список такого файла будет очень проблематично. Посмотрим пример:
[Folder] Get-Item -LiteralPath text[text.txt
Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 02.12.08. 0:59 769 text[text.txt
[Folder] Get-Item -LiteralPath text[text.txt | Get-Acl
Get-Acl : The specified wildcard pattern is not valid: text[text.txt
At line:1 char:45
+ Get-Item -LiteralPath text[text.txt | Get-Acl <<<<
Get-Item спокойно работает с LiteralPath, но при передаче его по конвейеру в командлет Get-Acl мы получаем ошибку. Хотя, по идее это должно было сработать. Кстати говоря, это первый обнаруженный недостаток. Вместо LiteralPath можно использовать 4 backtick (text````[text.txt), либо проще (что мне показалось более очевидным) - поместить литеральную скобку в подстановочный шаблон:
[Folder] Get-Item text````[text.txt Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:59 769 text[text.txt [Folder] Get-Item text[[]text.txt Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 02.12.08. 0:59 769 text[text.txt [Folder]
В первом примере я использовал рецепт из книги Windows PowerShell In Action (страница 310), второй я додумал сам. Однако, оба эти приёма заменяют параметр -LiteralPath, но так же не решают проблемы командлетов, как Get-Acl. Если передавать эти объекты в Gat-Acl, то всё равно мы будем получать ошибки. Как вариант решения - использование промежуточной функции-фильтра (это рецепт решения Kiron из ньюсгрупп):
filter Escape-Bracket { $_.psPath = $_.psPath -replace '\[|]','`$ $_ }
При использовании фильтра наш литеральный символ заменяем подстановочным знаком "?", пропускаем через фильтр и подаём на командлет Get-Acl:
[Folder] Get-Item text?text.txt | Escape-Bracket | Get-Acl | ft -a Directory: Microsoft.PowerShell.Core\FileSystem::D:\Users\_Shared Documents\Folder Path Owner Access ---- ----- ------ text[text.txt Thor\vPodans Everyone Allow FullControl... [Folder]
Так оно работает. В качестве первого предположения я подумал на тип объектов:
[Folder] (Get-Item 'text[[]text.txt').GetType().fullname System.IO.FileInfo [Folder] (Get-Acl text.txt).GetType().fullname System.Security.AccessControl.FileSecurity [Folder]
Что при Get-Item мы получаем тип объектов FileInfo, который не содержит ACL, поэтому данный командлет передаёт в конвейер свойство PsPath, который уже Get-Acl использует для повторного извлечения уже ACL объекта.
Однако, я обратил внимание, что при использовании UNC путей вместо локальных промежуточная функция не требуется и вполне работает подстановочная маска заключённая в квадратные скобки:
[Folder] Get-Acl 'text[[]text.txt' Get-Acl : The specified wildcard pattern is not valid: text[text.txt At line:1 char:8 + Get-Acl <<<< 'text[[]text.txt' [Folder] Get-Acl 'text[[]0[]].txt' Get-Acl : Cannot find path 'D:\Users\_Shared Documents\Folder\text[0].txt' because it does not exist. At line:1 char:8 + Get-Acl <<<< 'text[[]0[]].txt' [Folder] Get-Acl '\\Thor\_Shared Documents\Folder\text[[]text.txt' | ft -a Directory: Microsoft.PowerShell.Core\FileSystem::\\Thor\_Shared Documents\Folder Path Owner Access ---- ----- ------ text[text.txt Thor\vPodans Everyone Allow FullControl... [Folder] Get-Acl '\\Thor\_Shared Documents\Folder\text[[]0[]].txt' | ft -a Directory: Microsoft.PowerShell.Core\FileSystem::\\Thor\_Shared Documents\Folder Path Owner Access ---- ----- ------ text[0].txt Thor\vPodans Everyone Allow FullControl... [Folder]
В первых двух командах я поместил квадратную скобку в подстановочный шаблон (заключил во внешние квадратные скобки) для локальных путей, а во втором - сделал то же самое, но для UNC пути. При этом, типы объектов не изменились:
[Folder] (Get-Item '\\thor\_Shared Documents\Folder\text[[]text.txt').gettype().fullname System.IO.FileInfo [Folder] (Get-Acl '\\thor\_Shared Documents\Folder\text[[]text.txt').gettype().fullname System.Security.AccessControl.FileSecurity [Folder]
Следовательно, моё предположение было неверным. Вполне возможно, что это баг командлетов, поэтому я на всякий случай запостил проблему на Connect'е:
https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=386138&SiteID=99
Желающие могут перейти по ссылке и подтвердить данную ошибку. Всем спасибо за участие :)