【実務・中級編】【ネットワーク診断】WMI (Win32_PingStatus) を活用した複数拠点への並列 Ping 確認とパケット損失率の自動集計 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

ping.exeを卒業せよ:WMIを掌握し、堅牢なネットワーク診断エンジンを構築する

業務自動化の現場において、多くのエンジニアが陥る「初歩的な罠」がある。それは、ネットワークの疎通確認を、外部コマンドである `ping.exe` の実行結果をテキストパース(文字列解析)することで実現しようとすることだ。

「動けばいい」という思考で書かれたそのスクリプトは、OSの言語設定が日本語から英語に変わった瞬間に崩壊し、パースエラーという名の致命的なバグを吐き出す。これはプロフェッショナルの仕事ではない。

本稿では、Windowsの核となる管理基盤 WMI (Windows Management Instrumentation) を直接操作し、`Win32_PingStatus` クラスを活用することで、言語に依存せず、構造化されたデータを高精度に取得する「真に堅牢なネットワーク診断ツール」の設計思想と実装を伝授する。

1. なぜ `ping.exe` のパースは「悪」なのか

設計者として、まずこの設計判断の根拠を明確にしておく必要がある。なぜ `WshShell.Run(“ping …”)` を使ってはならないのか。理由は3つある。

1. ロケールの脆弱性: `ping` コマンドの出力はOSの言語設定に依存する。「Reply from」を待っているコードは、日本語環境の「応答:」を見た瞬間に死ぬ。
2. パースのオーバーヘッドと不確実性: テキスト解析は常に「正規表現の罠」と隣り合わせだ。出力フォーマットの微細な変更(Windows Updateによるものすらあり得る)が、スクリプトの信頼性を根底から破壊する。
3. データの構造化欠如: `ping.exe` は「人間が読むためのテキスト」を出力する。我々が欲しいのは「数値としての応答時間」や「ステータスコード」という「機械が扱うためのデータ」だ。

これに対し、WMI はオブジェクト指向のインターフェースを提供する。取得できるのは、プロパティとして定義された「数値」や「列挙型」だ。これこそが、自動化ツールが求めるべき「構造化データ」である。

2. Win32_PingStatus のアーキテクチャ

WMIの `Win32_PingStatus` クラスは、`ExecMethod` を通じて呼び出される。これは単なるプロパティ参照ではなく、WMIに対して「Pingを実行せよ」というメソッド命令を飛ばす行為だ。

実行後に返されるオブジェクトには、以下の重要なプロパティが含まれる。

  • `StatusCode`: 成功なら 0。それ以外はエラーコード。
  • `ResponseTime`: 応答時間(ミリ秒)。
  • `Protocol`: 使用されたプロトコル。

これらを直接扱うことで、パースという不毛な工程を完全に排除できる。

3. 実装:プロダクション・グレードの診断スクリプト

以下に、複数拠点への疎通確認を行い、パケット損失率を計算してCSVへ出力する、保守性の高いコード例を示す。単なるコピペで終わらせず、コード内のコメントから「なぜこのように書くのか」という意図を読み取ってほしい。

'-------------------------------------------------------------------------------
' Network Diagnostic Tool (WMI-based)
' Author: Chief Architect
' Description: WMI Win32_PingStatus を使用した構造化データ取得と統計レポート生成
'-------------------------------------------------------------------------------

Option Explicit

' --- 定数定義 ---
Const WQL_PING_METHOD = "Win32_PingStatus"
Const REPORT_FILE = "Network_Report.csv"
Const PING_COUNT = 4 ' 各ホストに対する試行回数

' --- メインルーチン ---
Sub Main()
    Dim targets, target, reportData
    Dim wmiService, csvContent
    
    ' 診断対象リスト(実務では外部ファイルやDBから読み込む設計にすべき)
    targets = Array("8.8.8.8", "127.0.0.1", "google.com", "invalid.local.host")
    
    ' WMIサービスへの接続
    Set wmiService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
    
    ' CSVヘッダーの初期化
    csvContent = "Timestamp,Target,Status,AvgResponseTime(ms),PacketLoss(%),Result" & vbCrLf
    
    WScript.Echo "Starting Network Diagnosis..."

    For Each target In targets
        WScript.Echo "Checking: " & target
        ' 診断実行
        reportData = ExecutePingSequence(wmiService, CStr(target), PING_COUNT)
        ' レポート行の結合
        csvContent = csvContent & reportData
    Next

    ' ファイル出力(エラーハンドリングを含む)
    On Error Resume Next
    Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
    Dim ts: Set ts = fso.CreateTextFile(REPORT_FILE, True)
    If Err.Number <> 0 Then
        WScript.Echo "Error: Failed to create report file. " & Err.Description
    Else
        ts.Write csvContent
        ts.Close
        WScript.Echo "--------------------------------------------------"
        WScript.Echo "Diagnosis Complete. Report saved to: " & REPORT_FILE
    End If
    On Error GoTo 0
End Sub

'-------------------------------------------------------------------------------
' Function: ExecutePingSequence
' Description: 指定された回数分Pingを実行し、統計情報を構造化文字列として返す
'-------------------------------------------------------------------------------
Function ExecutePingSequence(wmiService, strTarget, intCount)
    Dim i, successCount, totalResponseTime, statusCode
    Dim pingResult, rowStr, statusText
    
    successCount = 0
    totalResponseTime = 0
    
    ' WMIのWin32_PingStatusオブジェクトを取得
    ' ※ExecMethodを使用するため、クラスのインスタンスに対して呼び出す
    Dim pingClass : Set pingClass = wmiService.GetSingleton(WQL_PING_METHOD)

    For i = 1 To intCount
        ' WMIメソッドの実行。引数にはターゲットIP/ホスト名を渡す
        ' 戻り値は、実行結果を含むオブジェクト
        Set pingResult = pingClass.ExecMethod("Ping", Array(Array("Address", strTarget)))
        
        statusCode = pingResult.StatusCode
        
        If statusCode = 0 Then
            successCount = successCount + 1
            totalResponseTime = totalResponseTime + pingResult.ResponseTime
            statusText = "OK"
        Else
            statusText = "FAILED"
        End If
    Next

    ' 統計計算
    Dim avgTime, lossRate
    If successCount > 0 Then
        avgTime = totalResponseTime / successCount
    Else
        avgTime = 0
    End If
    
    lossRate = ((intCount - successCount) / intCount) * 100

    ' CSV行の生成 (Timestamp, Target, Status, AvgTime, LossRate, Result)
    rowStr = Now & "," & _
             strTarget & "," & _
             statusText & "," & _
             FormatNumber(avgTime, 2) & "," & _
             FormatNumber(lossRate, 0) & "%," & _
             IIf(lossRate = 0, "Healthy", "Degraded") & vbCrLf
             
    ExecutePingSequence = rowStr
End Function

' --- 簡易的な三項演算子代用関数 ---
Function IIf(expr, truePart, falsePart)
    If expr Then IIf = truePart Else IIf = falsePart
End Function

' 実行
Main

4. アーキテクトの視点:実務運用における注意点

このコードをプロダクション環境に投入する際、以下の3点に留意せよ。

① オブジェクトのライフサイクル管理

VBScriptはガベージコレクションが自動で行われるが、WMIオブジェクトのようなCOMオブジェクトは、ループ内で大量に生成・破棄を繰り返すと、メモリリークやハンドル不足を招く恐れがある。本コードでは `pingClass` をループの外で一度だけ取得し、`ExecMethod` で再利用する設計をとっている。これが「パフォーマンスの重み」を知る者の書き方だ。

② 並列処理の限界と設計

VBScript(WSH)はシングルスレッドである。そのため、上記のコードはターゲットを一つずつ順次処理する。もし数千台の拠点を確認する必要があるなら、VBScript単体で頑張ってはならない。

  • 設計案A: このスクリプトを「1実行あたり1ターゲット」として設計し、タスクスケジューラや外部のジョブ管理ツールから並列に叩く。
  • 設計案B: 本格的な並列処理が必要なら、PowerShellの `ForEach-Object -Parallel`(PowerShell 7以降)や、C#によるマルチスレッド実装へ移行する。

③ エラーハンドリングの「質」

`On Error Resume Next` を乱用するのは素人のすることだ。本コードでは、ファイル出力という「外部リソースへの依存」が発生する箇所にのみ限定して適用している。ロジックの内部では、`StatusCode` をチェックすることで、エラーを「例外」としてではなく「データ」として処理している。これが堅牢な設計の境界線だ。

結論

WMIを活用したネットワーク診断は、単なる「ツールの作成」ではない。それは、OSの深層にある構造化データにアクセスし、環境の変化に左右されない「不変のロジック」を構築する行為である。

`ping.exe` のテキストを追いかける日々は、今日で終わりにせよ。構造化されたデータこそが、自動化の未来を支える。

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