これは、PowerShell Advent Calendar 2014の12/19日の記事。
Windows Server 2008R2上のタスクスケジューラに登録しているタスクの結果をチェックして、異常があればメール送信するという依頼を受けた。
最初、タスクスケジューラのログを見れば結果が分かるだろうということで調べてみると、ログはイベントログに出力されることが分かったが、初期設定では履歴は無効になっており有効にしないとイベントログには出力されない。履歴表示を有効にするには右側パネルの「操作」欄から「すべてのタスク履歴を有効にする」をクリックして、「すべてのタスク履歴を無効にする」に変更します。
イベントログを取得するにはPowerShellを使うのがいいということで、PowerShellを勉強し始めた。
PowerShellを使おうとした時に、まず躓くエラー「スクリプトの実行がシステムで無効になっているため…」
http://technet.microsoft.com/ja-jp/scriptcenter/powershell_owner05.aspx
「確かに、これは少し厳しいと思われるかもしれません。何しろ、スクリプトを実行できないスクリプト環境なんて、あっても意味がありませんから。」ってこの記事に書いてあるけど、セキュリティー的に仕方ないのかも知れないけど厳しいよね。
でも、PSコマンドプロンプトより下記のコマンドを実行し、実行ポリシーを変更確認で[Y] はい(Y)を選択すれば、それ以降はPowerShellを存分に使える。※32bitと64bitとは別々なのでそれぞれに有効にする必要がある。
「Set-ExecutionPolicy RemoteSigned」
さて、タスクスケジューラのイベントログは「Microsoft-Windows-TaskScheduler/Operational」に登録される。Get-Eventlogコマンドでこのイベントログを取得しようとするも、存在しないとエラーになる。
調べるとGet-EventlogコマンドではなくGet-WinEventコマンドでないと取得出来ないのだった。
参照:[PowerShell]不定期イベントログを捕まえる!nmcapとPowerShellで必要なイベントログのみ取得する(2/2)
$IpAddress = "127.0.0.1"
$EventLogName = "Microsoft-Windows-TaskScheduler/Operational"
$StartDate = Get-Date
$EndDate = $StartDate - (New-TimeSpan -day 1)
$file = Split-Path $myInvocation.MyCommand.Path -Parent
$file = $file + "\tasklist.txt"
#イベントログ取得
function getEventLog ($IpAddress,$EventLogName,$StartDate,$EndDate,$file) {
#イベントログ取得
#201は操作が完了しました。
$logArray = Get-WinEvent -Computername $IpAddress -logname $EventLogName | Select-Object * |
Where-Object {$_.TimeCreated -lt $StartDate -AND
$_.TimeCreated -gt $EndDate -AND
$_.UserId.Translate([System.Type]::GetType("System.Security.Principal.NTAccount")) -match "Administrator" -AND
$_.ID -eq 201 }
#期間内のイベントログが無い場合は、ファイルにログ無しと書き込む
if ($null -eq $logArray) {
$message = "イベントログがありません。 期間:" + $startdate + " ~ " + $enddate
$message | Out-File -Filepath $file
}
else {
#ヘッダ情報をファイルに書き込む
$line = "日時,イベントID,カテゴリ,ユーザー,タスク名,実行結果"
$line | Out-File -Filepath $file
#取得したイベントログをファイルに書き込む
foreach ($row in $logArray) {
#日付の書式を整形
$workTimeCreated = [string]$row.TimeCreated.ToString("yyyy/MM/dd HH:mm:ss")
#一行としてまとめる
$line = $workTimeCreated + "," + [string]$row.ID + "," + [string]$row.TaskDisplayName + ","
$line += [string]$row.UserId.Translate([System.Type]::GetType("System.Security.Principal.NTAccount")) + ","
$line += [string]$row.Message.split("`"")[1].SubString(1) + "," #タスク名
$line += [string]$row.Message.SubString($row.Message.LastIndexOf(":")+2) #実行結果
#ファイルに書き込む
$line | Out-File -Filepath $file -Append -encoding Default
}
}
#ファイルの存在チェックをして終了
if (Test-Path $file) {
#ログを出力
Write-Host $file "を作成しました。"
}
}
#実行
getEventLog $IpAddress $EventLogName $StartDate $EndDate $file
見よう見まねで作成してみて実行すると、数行のイベントログをCSV形式出力するだけなのに結果がでてくるのに1分以上もかかるのである。
きっと別のいい方法があるはずだと家に帰宅してから調べ直すと、「schtasks.exe /query /V /FO CSV」でタスク名と実行結果などがCSV形式出力することが分かった。
Function gettasks($Path)
{
$OutPut = $Path + "\tasklist.txt"
"実行時刻,タスク名,結果" | Out-File $output -encoding default
$Tasks = schtasks.exe /query /V /FO CSV | ConvertFrom-Csv | Where { $_."ユーザーとして実行" -Like "*administrator" }
ForEach ($Task in $Tasks) {
If ($Task."実行するタスク" -match "TaskSchtCheck"){
#自分のタスクは除く
continue
}
$BeforeRunTime = $Task."前回の実行時刻"
$TaskName = $Task."タスク名".SubString(1)
$BeforeResult = $Task."前回の結果"
"$BeforeRunTime,$TaskName,$BeforeResult" | Out-File $OutPut -append -encoding default
}
}
Function Get-ScriptDirectory
{
Split-Path $script:MyInvocation.MyCommand.Path
}
#実行
gettasks(Get-ScriptDirectory)
Out-File -encoding defaultとすることで文字コードがSJISのCSVが出力される。
VBSでCSVファイルを参照して、結果が2以上ならメール送信する仕組みとした。
VBSからPowerShellのスクリプトを実行するのは下記の通りです。
Set objShell = CreateObject("Wscript.shell")
objShell.run("powershell -file GetTaskList.ps1")
【追記】タスクスケジューラからVBSを呼ぶ場合、フルパス、PowerShellの画面非表示(0)、同期処理(True)にする
objShell.run "powershell -file """ & scriptRootPath & "GetTaskList.ps1""",0,True