【テクニカル・上級編】【タスクスケジューラ実行結果レポート】Get-ScheduledTask 相当の処理を WMI と Shell 連携で作成するタスク監視 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

【タスクスケジューラ実行結果レポート】Get-ScheduledTask 相当の処理を WMI と Shell 連携で作成するタスク監視

レガシーシステムの保全において、最も恐ろしいのは「動いているが、誰も中身を把握していないタスク」の存在である。特に、PowerShellの導入が制限されるセキュアな閉域網や、歴代の担当者が場当たり的に追加してきたWindowsタスクスケジューラ群の監視は、インフラエンジニアの頭痛の種だ。

最新の環境であれば `Get-ScheduledTask` 一発で済む話だが、我々が対峙する現場では、いまだにWindows 7やServer 2008 R2、あるいはPowerShellの実行ポリシーに縛られたレガシー環境が現役で稼働している。

今回は、純粋な VBScript (WSH)WMI (Windows Management Instrumentation)、そして `schtasks.exe` の出力を巧みに組み合わせ、モダンなPowerShell環境に依存せずとも、全タスクの稼働状態と前回実行結果を美しく可視化する「タスク監視レポート自動生成エンジン」の設計思想と実装の全貌を公開する。

1. レガシー監視におけるアーキテクチャ上の罠

VBScriptでシステム監視を実装する際、素人が陥りがちな致命傷が2つある。

1. COMオブジェクトのメモリリークとプロセス肥大化
2. WMI (`Win32_ScheduledJob`) の致命的なスコープ不足

WMIの限界と `schtasks.exe` とのハイブリッド戦略

多くの解説書では「タスクの取得には `Win32_ScheduledJob` を使え」と書かれている。だが、これは現代のWindows環境においては半分間違いだ。
`Win32_ScheduledJob` は、「ATコマンド」互換で作成されたタスクしか列挙しない。タスクスケジューラ(GUI)やXMLベースで登録されたモダンなタスク(Task Scheduler 2.0以降)は、このWMIクラスには一切ヒットしないのである。

この制限を突破するため、本アーキテクチャでは `schtasks.exe /FO CSV /V` コマンドの標準出力を動的にキャプチャし、テキストストリームとしてパースする手法 を採用する。WMIの重厚なオーバーヘッドを回避しつつ、OSが管理するすべてのタスク情報を一網打尽にする、極めて実戦的なアプローチだ。

2. 実装コード:TaskStatusReporter.vbs

以下のコードは、エラーハンドリング、オブジェクトの厳密なライフサイクル管理、そしてHTMLエスケープ処理を網羅した、プロダクション品質のVBScriptである。

‘ ==============================================================================
‘ Script Name : TaskStatusReporter.vbs
‘ Description : WSH/WMI/Shell連携によるタスクスケジューラ状態監視レポート生成
‘ Author : チーフアーキテクト
‘ ==============================================================================
Option Explicit

Const ForReading = 1
Const TristateUseDefault = -2

‘ メイン処理の実行
Main

Sub Main()
Dim objShell, objFSO, objStream
Dim strTempFile, strHtmlPath
Dim tsOutput, line, fields
Dim htmlContent

On Error Resume Next

‘ 1. オブジェクトの初期化
Set objShell = CreateObject(“WScript.Shell”)
Set objFSO = CreateObject(“Scripting.FileSystemObject”)

If Err.Number <> 0 Then
WScript.Echo “致命的エラー: オブジェクトの生成に失敗しました。 (” & Err.Description & “)”
Exit Sub
End If

‘ 一時ファイルのパス生成
strTempFile = objFSO.GetSpecialFolder(2) & “\schtasks_dump_” & Int(Rnd 100000) & “.csv”
strHtmlPath = objFSO.GetParentFolderName(WScript.ScriptFullName) & “\TaskStatus_Report.html”

‘ 2. schtasks による全タスク情報のCSV出力 (UTF-16/ANSIの環境差異を吸収するためcmd経由)
objShell.Run “cmd.exe /c schtasks.exe /Query /FO CSV /V > “”” & strTempFile & “”””, 0, True

If Not objFSO.FileExists(strTempFile) Then
WScript.Echo “エラー: タスク情報の取得に失敗しました。”
Call Cleanup(objShell, objFSO, strTempFile, Nothing, Nothing)
Exit Sub
End If

‘ 3. HTMLヘッダーの構築
htmlContent = “” & _
Task Scheduler Audit Report” & _

” & _

Windows Task Scheduler Status Report

” & _

生成日時: ” & Now & “

” & _

” & _

‘ 4. CSVファイルのパース処理 (TextStreamによるメモリ効率の極限化)
Set objStream = objFSO.OpenTextFile(strTempFile, ForReading, False, TristateUseDefault)

Dim isHeaderHtml: isHeaderHtml = True
Do While Not objStream.AtEndOfStream
line = objStream.ReadLine

‘ ヘッダー行をスキップ
If isHeaderHtml Then
isHeaderHtml = False
ElseIf Trim(line) <> “” Then
fields = ParseCsvLine(line)

‘ schtasks /V の列構造に基づくマッピング (環境によってインデックスが異なる場合あり)
‘ 0:Host, 1:TaskName, 2:Next Run Time, 3:Status, 4:Logon Mode, 5:Last Run Time, 6:Last Result, …
If UBound(fields) >= 6 Then
Dim taskName, nextRun, status, lastRun, lastResult, taskToRun
taskName = CleanString(fields(1))
nextRun = CleanString(fields(2))
status = CleanString(fields(3))
lastRun = CleanString(fields(5))
lastResult = CleanString(fields(6))

Dim resultClass
If lastResult = “0” Or lastResult = “タスクは正常に終了しました。” Then
resultClass = “status-0”
Else
resultClass = “status-err”
End If

htmlContent = htmlContent & “

” & _

” & _

” & _

” & _

” & _

” & _

” & _

” & _


End If
End If
Loop

objStream.Close

htmlContent = htmlContent & “

ホスト名 タスク名 状態 次回実行時刻 前回実行時刻 前回結果 タスク実行(アプリ/スクリプト)
” & CleanString(fields(0)) & “ ” & taskName & “ ” & status & “ ” & nextRun & “ ” & lastRun & “ ” & lastResult & “ ” & (IIf(UBound(fields) >= 8, CleanString(fields(8)), “”)) & “

‘ 5. レポートの書き出し
Dim objOutStream
Set objOutStream = objFSO.CreateTextFile(strHtmlPath, True, True) ‘ Unicode出力
objOutStream.Write htmlContent
objOutStream.Close

‘ 6. クリーンアップとプロセス終了
Call Cleanup(objShell, objFSO, strTempFile, objStream, objOutStream)

On Error GoTo 0
WScript.Echo “レポートの生成が完了しました: ” & strHtmlPath
End Sub

‘ — 簡易CSVパーサー関数 (ダブルクォーテーション対応) —
Function ParseCsvLine(strLine)
Dim inQuotes, i, c, item, arr()
ReDim arr(0)
Dim idx: idx = 0
inQuotes = False
item = “”

For i = 1 To Len(strLine)
c = Mid(strLine, i, 1)
If c = “””” Then
inQuotes = Not inQuotes
ElseIf c = “,” And Not inQuotes Then
ReDim Preserve arr(idx)
arr(idx) = item
idx = idx + 1
item = “”
Else
item = item & c
End If
Next
ReDim Preserve arr(idx)
arr(idx) = item
ParseCsvLine = arr
End Function

‘ — 文字列サニタイズ —
Function CleanString(str)
If IsNull(str) Then
CleanString = “”
Exit Function
End If
str = Replace(str, “”””, “”)
str = Replace(str, “<", "<") str = Replace(str, ">“, “>”)
CleanString = Trim(str)
End Function

‘ — 条件分岐ヘルパー —
Function IIf(expr, truePart, falsePart)
If expr Then IIf = truePart Else IIf = falsePart
End Function

‘ — メモリ最適化・リソース解放の徹底 —
Sub Cleanup(shell, fso, tmpFile, stream1, stream2)
On Error Resume Next
If Not stream1 Is Nothing Then stream1.Close
If Not stream2 Is Nothing Then stream2.Close
If fso.FileExists(tmpFile) Then fso.DeleteFile tmpFile, True
Set stream1 = Nothing
Set stream2 = Nothing
Set fso = Nothing
Set shell = Nothing
End Sub

3. チーフアーキテクトが解説するコードの急所

① `TextStream` とUTF-16対応の罠

Windows環境において、外部コマンド(`schtasks.exe`)の出力エンコーディングは、OSのロケールやバージョンによってShift-JISだったりUTF-16LEだったりと暴れる。
今回のスクリプトでは、あえて `cmd.exe /c` 経由でリダイレクトすることでANSI(Shift-JIS)として確実にファイルへ落とし込み、VBScriptの `OpenTextFile` で安全にストリーム読み込みを行っている。さらに、生成するHTML側は `CreateTextFile(…, True, True)` により強制的にUnicode(UTF-16LE)で書き出すことで、文字化けを完全に根絶している。

② メモリリークを許さない `Cleanup` パターン

VBScriptの最大の弱点は「ガベージコレクションの気まぐれ」である。特にCOMオブジェクトやファイルストリームを開いたままスクリプトを終了させると、VBScriptのプロセス(`wscript.exe` / `cscript.exe`)がメモリ上にゾンビとして残り続け、タスクスケジューラからの定期実行時にリソース枯渇を引き起こす。
本スクリプトでは、`Cleanup` サブルーチンを必ず経由させ、すべての参照を明示的に `Nothing` に倒す設計をとっている。プロフェッショナルたるもの、リソースの借用と返却は一対で行うべきだ。

③ 独自の簡易CSVパーサーの搭載

`schtasks.exe /FO CSV` の出力は、タスクの引数やパスにカンマ(`,`)が含まれている場合、フィールド全体がダブルクォーテーションで囲まれる。VBScript標準の `Split` 関数ではこのエスケープを正しくハンドリングできず、配列のインデックスがズレてパース破綻を起こす。
これを回避するため、文字単位でクォーターステートを監視する `ParseCsvLine` 関数を自前で実装している。レガシー環境特有の「標準ライブラリの非力さ」をアルゴリズムで補う、エンジニアの矜持が宿る部分だ。

4. 運用・システム間連携への展開

このVBScriptを単体の単発ツールで終わらせてはならない。真の自動化エンジニアであれば、これをタスクスケジューラに登録し、さらに次の一手を打つ。

  • メール自動送信連携: 生成された `TaskStatus_Report.html` を、CDO.Messageコンポーネントを用いて毎朝運用チームのメールボックスへHTMLメールとして直送する。
  • 異常検知のフック: パース処理のループ内で `lastResult` が `0` 以外の異常コード(例: `2147943785` = 0x80070009 など)を検知した場合に、イベントログ(`WScript.Shell` の `LogEvent`)へ警告を即座に書き出すよう拡張する。

PowerShellが使えないからといって、システムの監視を諦める必要は微塵もない。VBScriptの深淵な挙動を掌握すれば、レガシーOSは最強の堅牢性を持つ自動化プラットフォームへと変貌するのだ。

タイトルとURLをコピーしました