January 14, 2014

[PowerShell]不定期イベントログを捕まえる!nmcapとPowerShellで必要なイベントログのみ取得する(2/2)

前回、PowerShellでNWキャプチャを取る部分のスクリプトを作成しました

2回に分けて記載ということで、その続きになります

完成させたいスクリプト処理はこんな流れでした
(日本語イメージ)
PowerShell起動(★1)

ループ処理{
 nmcap でキャプチャ開始
  (終了時間を /StopWhen /Time オプションでN分後に設定)

 nmcap のキャプチャ終了

 過去N分の間にサーバで
 期待しているイベントID:Xが起きているかどうかチェック(★2-1, 2-2)
 →起きていた場合は、*.cap ファイルをまるっと別フォルダにコピー(★3)
 →起きていない場合は、何もしない

}( (★1)から1週間経過していると、ループを抜ける )
 
PowerShell終了

今回やりたいのは上記の赤字部分で、3つあります
  • Windowsイベントログを取得する(★2-1)
  • 必要なイベントログが発生しているかどうか判定する(★2-2)
  • ディレクトリを作成し、そこにファイルをコピーする(★3)
さて、頑張ってみていきます


*PowerShellでWindowsイベントを取得する (★2-1)

イベントビュワーで見れるWindowsのイベントログがあります

ps07

ここの情報も、PowerShellで取得できます


 1 
$MY_WAIT_TIME = 180
 2 $CKID0 = 333
 3 $CKID1 = 777
 4 
 5 $ck_date = Get-Date
 6 $ck_date = ($ck_date).AddMinutes( $MY_WAIT_TIME * -1 )
 7 
 8 $evt_err = Get-EventLog "Application" -newest 10000 | Where-Object { ( $_.EntryType -eq "Error") -and$_.TimeGenerated -ge $ck_date )}
 9 $evt_err = $evt_err | ? {( $_.InstanceID -eq $CKID0) -or$_.InstanceID -eq $CKID1 )}


ちょっと解説

これまで起きたWindowsイベント全てをチェックするのではなく
チェックしたい期間を指定することが出来ます
まずは 5〜6行目の部分 
$MY_WAIT_TIME = 180

$ck_date = Get-Date
$ck_date = ($ck_date).AddMinutes( $MY_WAIT_TIME * -1 )
上記は、180分、つまり今から3時間前の時間を取得しています
さらに 8行目

$evt_err = Get-EventLog "Application" -newest 10000 | Where-Object { ( $_.EntryType -eq "Error") -and( $_.TimeGenerated -ge $ck_date )}

この $evt_err には以下のような情報が入ります
  • Get-EventLog で、アプリケーションログを指定
  • -newest で最新のログを1万件取得
  • その中から…
  •  エラーイベント かつ
  •  3時間前よりもイベント発生時間が新しい物
こんなを取得しています

今時点での $evt_err の中身はこんな感じです
PS C:\tmp> $evt_err

   Index Time   EntryType   Source             InstanceID Message                                                         
   ----- ----      ---------   ------  ---------- -------
  185798 1 13 22:52    Error Application Error 1000  障害が発生しているアプリ...
  185513 1 13 23:09    Error EventCreate        777    さおてすと(~o~)  
おぉ、良い感じにエラーイベントのみ取得できていますね


(余談:任意のIDでイベントを起こす方法)

余談ですが、わたしはテストのために、好みのエラーイベントIDを手動で起こしています
(上記例で言うところの ID 777 の”さおてすと(~o~)”の部分ですね)

これは、eventcreate コマンドで、イベントを起こすことが出来ます
PS C:\tmp> eventcreate /T ERROR /id  777 /d "さおてすと(~o~)"

成功: 種類が 'ERROR' のイベントが、'Application' ログ内に、'EventCreate' をソー
スとして作成されました。
  • /T オプション:ERROR でエラーが起こせます
  • /id オプション:起こすイベント番号を指定します
  • /d オプション:イベントメッセージを記載します

話をイベントログ取得に戻しますね

$evt_err の中身には、先ほど 777 と 1000 の イベントIDが入っていました
今、自分がチェックしたいイベントID は "333" と "777" だと仮定します
最後の 9行目で、必要なイベントにIDに、さらに絞り込んでいます
$CKID0 = 333
$CKID1 = 777
$evt_err = $evt_err | ? {( $_.InstanceID -eq $CKID0 ) -or( $_.InstanceID -eq $CKID1 )}
Get-EventLog でイベント番号を指定するには InstanceID と比較すれば取得できます

更新された $evt_err はこんな感じに
PS C:\tmp> $evt_err

   Index Time   EntryType   Source             InstanceID Message                                                         
   ----- ----      ---------   ------  ---------- -------
  185513 1 13 23:09    Error EventCreate        777    さおてすと(~o~)  
ちゃんと、取りたいイベントIDを取得出来ました

(余談)
今回は、Application ログを取得しましたが、Get-EventLog ではシステムログ、セキュリティログなども利用できます
以下のような感じで、System, Security を取ることができます(これはわたしのWindows8.1端末の結果です)
PS C:\Users\Sao\Desktop> Get-EventLog -list

  Max(K) Retain OverflowAction        Entries Log
  ------ ------ --------------        ------- ---
   512   7 OverwriteOlder         760     ACEEventLog
20,480 0 OverwriteAsNeeded 32,521 Application
20,480 0 OverwriteAsNeeded 0          HardwareEvents
   512   7 OverwriteOlder        0          Internet Explorer
 1,024  0 OverwriteAsNeeded 0          iolo Applications
20,480 0 OverwriteAsNeeded 0          Key Management Service
 8,192  0 OverwriteAsNeeded 1          Media Center
   128   0 OverwriteAsNeeded 276      OAlerts
   512   7 OverwriteOlder        0           PreEmptive
                                                            Security
20,480 0 OverwriteAsNeeded 40,453 System
   512   7 OverwriteOlder         0          Windows Azure
15,360 0 OverwriteAsNeeded 3,556   Windows PowerShell

ところがこの Get-EventLog は、「WindowsXP/Server2003以前の形式」の場合に取得できる方法です

「新しいイベントログ形式」の場合は、取得の方法が少し異なります

例えば、ADFSのイベントログは「新しいイベントログ形式」であり Get-EventLog では取得できません
Get-WinEvent で取得します
$evt_err = Get-WinEvent -ProviderName "AD FS 2.0" -maxevents 10000 | Where-Object { ( $_.Level -eq 2 ) -and( $_.TimeCreated -ge $ck_date )}

$evt_err = $evt_err | ? {( $_.ID -eq $CKID0 ) -or( $_.ID -eq $CKID1 )}
Get-EventLog と Get-WinEvent ではちょっとづつ取得の方法が違っています

従来のイベントログ新しいイベントログ
Get-EventLogGet-WinEvent
タイプ指定方法"Application"-ProviderName "AD FS 2.0"
エラーの指定方法$_.EntryType -eq "Error"$_.Level -eq 2
発生時間の指定方法TimeGenerated TimeCreated
イベントIDの指定方法InstanceID ID
リモートサーバのイベントの指定方法-ComputerName SVR0-ComputerName SVR0

同じイベントログなのに、こんなにちょっとづつ取得方法が違うんですよね。。

ちなみに、リモートサーバのイベントログも取得出来ます(administratorで実行の場合)
これは Get-EventLog も Get-WinEvent も同じ -ComputerName オプションです

(参考)
TechNet Get-WinEvent


どちらのログ形式かどうかは、実はイベントビュワーのアイコンの色で判ります!

(TechNetブログより参照)
ps09

(参考)
[PowerShell] HEY YOU WHAT'S YOUR NAME?アクセスしてるのはお前さBABY!アカウント名をセキュリティログから取るぜ!
http://blogs.technet.com/b/jpilmblg/archive/2011/06/17/powershell-hey-you-what-s-your-name-baby.aspx
しかし、このブログすごいタイトルですね…
管理者は見た!〜AD と ILM 一家の秘密〜
「そんなアカウント管理で大丈夫なのか?」「大丈夫だ、問題ない」...(以下略)... 「(やっぱり) FIM にしてくれ」
ヾ(´ω`=´ω`)ノ


*指定のエラーIDがあるかどうかの判定文 (★2-2)
 プラス、ログ記載

さて、続きをプログラム的に記載するとこうなります


 1 
# 自作PowerShell用ログファイルの出力先
 2 $MY_OUT_FILE="c:\tmp\mynmcap.log"
 3 
 4 # -------------------------------------------------
 5 # 指定のエラー番号出てる?
 6 # -------------------------------------------------
 7  if$null -eq $evt_err ){
 8     Write-Output "エラーはなかった" | out-file $MY_OUT_FILE Default -append
 9  }else{
10     # ------------------------------
11     # エラーがあったよ!
12     # ------------------------------
13     Write-Output "エラーが発生しました" | out-file $MY_OUT_FILE Default -append
14     Write-Output $evt_err | out-file $MY_OUT_FILE Default -append
15 
16 }


ここでのポイントは、$evt_err の中身があるかどうかのチェックです
if( $null -eq $evt_err ){
      エラー起きてる

どうやら $evt_err の中身が空かどうかは $null 演算子でチェックするようです
ただし、順番は ( $null -eq $evt_err ) じゃないとダメらしい…
詳しくは、以下の牟田口さんのサイトを参考にしてください

if($array -eq $null) には要注意! - PowerShell Scripting Weblog


あとは、ファイルに自分なりの独自ログを書きこむ時には

Write-Output ほげほげ | out-file ファイル名 default -append

を使うと良い感じに書いてくれます


*指定のエラーIDがあるかどうかの判定文 (★3)

よーし、これで最後です
エラーが起きた時には、ファイルを別ディレクトリに退避させます


 1 
# エラー取得用ディレクトリ名(パス)
 2 $MY_ERR_DIR = "c:\tmp"
 3 # エラー取得用ディレクトリ名(プレフィックス)
 4 $MY_ERR_DIR_PREFIX = "err_data"
 5 
 6 # エラー用のログディレクトリ名を取得する
 7 $cpdir = $MY_ERR_DIR + "\\" + $MY_ERR_DIR_PREFIX + (Get-Date).ToString( "yyyyMMdd-HHmmss" )
 8 # ディレクトリの作成
 9 New-Item $cpdir -itemType Directory
10 
11 # ログデータをコピーする
12 Get-ChildItem $MY_ERR_DIR *.cap |  % { 
13     Copy-Item $_.FullName -destination  $cpdir  -recurse 
14 }
 

イメージ的にはこんなディレクトリを作ってコピー退避します↓↓↓

ps08

ディレクトリを作るコマンドはこちら↓↓↓

New-Item $cpdir -itemType Directory
 
作ったディレクトリに、ファイルをコピーするコマンドはこちら↓↓↓
Get-ChildItem $MY_ERR_DIR *.cap |  % { 
Copy-Item $_.FullName -destination  $cpdir  -recurse 
}
この例では、拡張子 *.cap のファイルを $cpdir にコピーしています

あぁ、これでやりたいことが全部出来ましたね!!
PowerShell ありがとう!


*まとめ

今回チャレンジしたスクリプトの一覧を載せておきます
ps1ファイルのダウンロードはこちらからどうぞ!
http://jyurimaru.info/data/20140114Blog/my_cap.zip

<my_cap.ps1>
  1 # ネットワークキャプチャ簡易ツール
  2 #                                                   2014/1/13 Sao Haruka
  3 # ==============================================
  4 # ==============================================
  5 # ==============================================
  6 #
  7 #   固有項目
  8 #
  9 # ==============================================
 10 # ==============================================
 11 # 自作PS用ログファイルの出力先
 12 $MY_OUT_FILE="C:\tmp\mynmcap.log"
 13 # 動いてから何日目に止まるか、一応設定しておく。7なら7日目
 14 $MY_STOP_DAYS = 7
 15 # 取得する時間、10分間なら 10
 16 $MY_WAIT_TIME = 180
 17 # nmcap の場所
 18 $MY_NMCAP_CMD = "C:\utils\Microsoft Network Monitor 3\nmcap.exe"
 19 # nmcap の引数、100Mや500Kなど指定の単位にファイルを作成
 20 $MY_NMCAP_ARG = "/network * /capture /file C:\tmp\test.chn:500K /StopWhen /Time "
 21 # 取得したいエラーID
 22 $CKID0 = 333
 23 $CKID1 = 777
 24 # エラー取得用ディレクトリ名(パス)
 25 $MY_ERR_DIR = "c:\tmp"
 26 # エラー取得用ディレクトリ名(プレフィックス)
 27 $MY_ERR_DIR_PREFIX = "err_data"
 28 
 29 # =============================================
 30 # =============================================
 31 # =============================================
 32 # =============================================
 33 # ---------------------------------------------
 34 #
 35 # メイン処理開始
 36 #
 37 # ---------------------------------------------
 38 # 開始時間を取っておく
 39 $start_date = Get-Date
 40 Write-Output "===============================================" | out-file $MY_OUT_FILE Default -append
 41 Write-Output "  NWキャプチャ開始します " $start_date | out-file $MY_OUT_FILE Default -append
 42 Write-Output "===============================================" | out-file $MY_OUT_FILE Default -append
 43 
 44 # --------------------------
 45 # キャプチャループ処理
 46 # --------------------------
 47 do{
 48     # --------------------------
 49     # nmcap 引数の整備
 50     # --------------------------
 51     $now_date = Get-Date
 52     # 指定の分数後に終了する
 53     $stop_date = $now_date.AddMinutes( $MY_WAIT_TIME )
 54     $my_opt = $MY_NMCAP_ARG + $stop_date.ToString( "HH:mm M/d/yyyy" )
 55     
 56     # -------------------------
 57     # nmcap プロセス開始
 58     # -------------------------
 59     Write-Output "=== nmcap開始します ===" $now_date | out-file $MY_OUT_FILE Default -append
 60     Write-Output $my_opt | out-file $MY_OUT_FILE Default -append
 61     $cap_wait = Start-Process -FilePath $MY_NMCAP_CMD -ArgumentList $my_opt -PassThru
 62 
 63     Write-Output "nmcap実行中" | out-file $MY_OUT_FILE Default -append
 64     
 65     # nmcap のプロセスが終わるまで待つ 
 66     Wait-Process -Id ($cap_wait.id)
 67     
 68     # -------------------------
 69     # nmcap プロセスが終わった
 70     # -------------------------
 71     $now_date = Get-Date
 72     Write-Output "nmcap終了しました" $now_date | out-file $MY_OUT_FILE  Default -append
 73 
 74     # ------------------------------------------
 75     # 指定のイベントIDが出てるかどうかObjを取得
 76     # ------------------------------------------
 77     $ck_date = $now_date
 78     $ck_date = ($ck_date).AddMinutes( $MY_WAIT_TIME * -1 )
 79 
 80     $evt_err = Get-EventLog "Application" -newest 10000 | Where-Object { ( $_.EntryType -eq "Error") -and$_.TimeGenerated -ge $ck_date )}
 81     $evt_err = $evt_err | ? {( $_.InstanceID -eq $CKID0) -or$_.InstanceID -eq $CKID1 )}
 82     # 新しいイベント形式の場合はこれで取得する ---------------
 83     # $evt_err = Get-WinEvent -ProviderName "AD FS 2.0" -maxevents 10000 | Where-Object { ( $_.Level -eq 2) -and( $_.TimeCreated -ge $ck_date )}
 84     # $evt_err = $evt_err | ? {( $_.ID -eq $CKID0 ) -or( $_.ID -eq $CKID0 )}
 85     # --------------------------------------------------------
 86 
 87 
 88     # ------------------------------------
 89     # 指定のエラー番号出てる?
 90     # ------------------------------------
 91     if$null -eq $evt_err ){
 92         Write-Output "エラーはなかった" | out-file $MY_OUT_FILE Default -append
 93 
 94     }else{
 95         # ------------------------------
 96         # エラーがあったよ!
 97         # ------------------------------
 98         Write-Output "エラーが発生しました" | out-file $MY_OUT_FILE Default -append
 99         Write-Output $evt_err | out-file $MY_OUT_FILE Default -append
100 
101         # エラー用のログディレクトリ名を取得する
102         $cpdir = $MY_ERR_DIR + "\\" + $MY_ERR_DIR_PREFIX + (Get-Date).ToString( "yyyyMMdd-HHmmss" )
103         # ディレクトリの作成
104         New-Item $cpdir -itemType Directory
105         
106         # ログデータをコピーする
107         Get-ChildItem $MY_ERR_DIR *.cap |  % { 
108             Copy-Item $_.FullName -destination  $cpdir  -recurse 
109         }
110     }
111 
112     # 指定日にち後には、自動で終了する
113     $leave_days = ((Get-Date) - $start_date ).Days
114 
115 }while$leave_days -lt $MY_STOP_DAYS )
116 
117 $now_date = Get-Date
118 Write-Output $now_date "=== NWキャプチャ終了しました ===" | out-file $MY_OUT_FILE Default -append
119 
120 # =============================================
121 # End of FIle

今回の記事はちょっと長かったですけど、困っている人の助けになれば幸いです
ここまで読んでくださった方、ありがとうございます!
おつかれさまでした(~o~) 

haruka_sao at 23:30コメント(0)トラックバック(0)PowerShell | Tips 

トラックバックURL

コメントする

名前:
URL:
  情報を記憶: 評価:  顔   星
 
 
 
Sao's Tech Memo
mvp_logo_140

Microsoft MVP for Windows
Platform Development
[Jan,2015-Dec,2015]
Microsoft MVP for
Client Development
[Jan,2014-Dec,2014]
Microsoft MVP for Client App Dev [Jan,2010-Dec,2013]
Recent Comments
訪問者数
  • 今日:
  • 昨日:
  • 累計:

記事検索