以前にもイベントログの抽出はやっている。
PowerShell: ◆イベントログを取得(抽出)する(Get-WinEvent)
ただし、こいつは標準的なプロパティで抽出しているので、イベント固有の項目で抽出となると簡単にはいかない。
多分、下記の赤枠部分が共通的なプロパティで青枠が個別のプロパティといった感じだろう。(イベントビューワの画面)
では、この個別部分で抽出するにはどうするのか。
ヘルプを見るとFilterXPathパラメータにXPath形式で条件を記述してやるようだ。
Get-WinEvent -logname security -FilterXPath $filter |
ちなみに、Get-WinEventを引数なしで実行するとWIndows8ではなぜか以下のようなエラーの嵐に見舞われる。(2012ではOK)
シンタックスでエラーになっているわけではなく、取得したデータをうまく表示できない感じだ。
こういう非互換はちょっと困りもの。
XPath形式でのフィルターの書き方はヘルプを見るとこんな感じ。
XMLの階層を[]で区切って書くようだ。
なんかちょっと面倒くさそう。
XPathはほとんど使ったことが無いのでちょっと苦労したが、とりあえず以下のような感じでいけそうだ。
$filter = @" |
これは、イベントIDが’4624’(ログオン)で、かつログオンタイプが順に、対話、ネットワーク、リモート、キャッシュされた対話ログオン。
キャッシュされた対話ログオンというのはドメインコントローラと通信ができないときとかに使われるのかと思っていたが、結果を見ると頻繁に使われている。(というかほとんどがキャッシュログオンだ)
となっていて、イベント固有の情報は「Message」というプロパティに文字列で入っている。
そこで、こいつを文字列解析して項目を取り出そうとしたのだが、改行付の文字列なのでシングルラインモードで正規表現を使ってかなりの力技チックになってしまった。
っで、取得してから気づいたのだがメンバー一覧をよく見ると「ToXml」メソッドがちゃんと用意されている。
気を取り直して、XMLに変換してから取り出してみる。
ここら辺は、それこそPowerShell: ◆イベントログを取得(抽出)する(Get-WinEvent)でやっているので問題なし、と思ったのだが、どうにもこうにもうまくいかない。(クエリしても何も返ってこない)
以前サンプルでやったのとどこが違うのか少しずつ試していくと、ルートノードについているネームスペースが気に入らないらしい。
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'> |
このネームスペースを消すとうまく取得できた。
都度消してやろうかとも思ったが、さすがに本質的解決じゃないので色々調べたところ、どうやらネームスペース付きのXMLの場合はXPathの方でもネームスペースを指定する必要があるっぽい。
メソッドの定義を見てみるとXPathに加えて「XmlNamespaceManager」なるものを指定するオーバーロードがあるのでこれを指定すればよいのだろう。
対象のXML自体にNameTableというプロパティがあり、それをコンストラクタ引数に指定すると「XmlNamespaceManager」は作れる。
出来た「XmlNamespaceManager」にAddNamespaceメソッドで名前空間とそのプリフィックスを追加。
$secLog | %{ |
あとは、XPathのそれぞれのノードに名前空間プリフィックス(上記の場合はe:)をつけてあげる。
$filterStr = "e:Event/e:EventData/e:Data[@Name='TargetUserName']/text()" |
ちょっと面倒?
どこかでデフォルトの名前空間を指定して、プリフィックスがつかなければデフォルトの名前空間を使う、ってな方法がないのかしらん?と思うが・・・。
/Event/Systemと、/Event/EventData用にそれぞれ以下のようなXPathを用意して、
$systemFiltertmpl = "e:Event/e:System/e:{0}/text()" |
可変の要素(または属性)部分を以下のような配列で作っておき、。
$systemProps = @('Computer') |
入れ替えながら使用すれば良い。
最後に、面倒くさそうだったので後回しにしていたが、ここでクエリ(XPath)に日付の抽出条件を付ける。
イベントログのクエリに使う時間はUTC時刻を使うのだとか。
UTC時刻はConvertTimeToUtcメソッドで変換できる。
[System.TimeZoneInfo]::ConvertTimeToUtc() |
最終的にクエリに指定するには文字列変換が必要で、ToString(“u”)でUTCフォーマットになるらしいのだが、結果を見ると微妙に期待したものと違っている。
PS>[System.TimeZoneInfo]::ConvertTimeToUtc((Get-Date)).ToString("u") |
クエリーで使う形式は日付と時刻の間に”T”が入っている。
2012-09-28T15:38:44Z |
どうしてこう微妙に違うのか良く判らないが、仕方がないので直接指定で変換。
HogeHoge.ToString("yyyy-MM-ddTHH:mm:ssZ") |
以上の結果を纏めたのが以下のスクリプト。
20日前から20日間のログを対象にしている。
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 | $fromDay = -20 #前日:-1 $days = 20 #開始からの日数 $startTime = [DateTime](Get-Date).AddDays($fromDay).ToString("yyyy/MM/dd 00:00:00") $startUtcTime = [System.TimeZoneInfo]::ConvertTimeToUtc( $startTime).ToString("yyyy-MM-ddTHH:mm:ssZ") $endUtcTime = [System.TimeZoneInfo]::ConvertTimeToUtc( $startTime.AddDays($days)).ToString("yyyy-MM-ddTHH:mm:ssZ") #表示対象のプロパティ $systemProps = @('Computer') $eventDataProps = @('TargetUserName','LogonType','TargetDomainName') #表示対象のプロパティ用のXPath $systemFiltertmpl = "e:Event/e:System/e:{0}/text()" $eventDataFiltertmpl = "e:Event/e:EventData/e:Data[@Name='{0}']/text()" $filter = @" Event/System/EventID='4624' and Event/EventData[ Data[@Name='LogonType']='2' or Data[@Name='LogonType']='3' or Data[@Name='LogonType']='10' or Data[@Name='LogonType']='11' ] and Event/System/TimeCreated[@SystemTime>='$startUtcTime'] and Event/System/TimeCreated[@SystemTime<'$endUtcTime'] "@ $secLog = $null $secLog = Get-WinEvent -logname security -FilterXPath $filter $addProp = { param($name,$shortFormat) $prop = $secLogXML.selectSinglenode($filterStr ,$ns).Value if($shortFormat -and $name -eq "Computer"){$prop = ($prop -Split '\.')[0]} Add-Member -Name $name -type NoteProperty -Value $prop -Input $eventObj } $secLog | %{ $secLogXML = [xml]$_.ToXML() $ns = New-Object Xml.XmlNamespaceManager $secLogXML.NameTable $ns.AddNamespace( "e", "http://schemas.microsoft.com/win/2004/08/events/event") $eventObj = $_ $systemProps | %{ $filterStr = $systemFiltertmpl -f $_ & $addProp $_ $true } $eventDataProps | %{ $filterStr = $eventDataFiltertmpl -f $_ & $addProp $_ } return $eventObj } | ft -Property ($systemProps+$eventDataProps) -AutoSize #ft @{name="pcName";expression={($_.Computer -split '\.')[0]}}, # TargetDomainName,TargetUserName,LogonType, # TimeCreated,SubjectUserName -AutoSize |
結果
あとは、こいつを他のマシンからも取得できるようにすれば完成だが、Get-WinEventにComputerNameパラメータを指定して、適切なCredentialを指定するだけなので省略。
管理されるサーバー側でファイアウォールの例外設定が必要になるが、Powershellとは特に関係ないのでこれも省略。
0 件のコメント:
コメントを投稿