【テクニカル・上級編】【インフラ管理】WMI (Windows Management Instrumentation) を経由したリモートPCのプロセス・サービス監視 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptの深淵:WMIによるリモート管理の極意 – レガシーを凌駕する自動化の真髄

諸君、私は長年、このテクノロジーの荒波を渡り歩いてきた。Excelのシートに血と汗を染み込ませ、VB6のコードに魂を刻み、そして今、VBScriptという、一見すると古びた、しかし依然として強力な武器を手に、諸君と共にインフラ管理の最前線に立っている。

WMI (Windows Management Instrumentation) は、Windows OSの管理において、まさに「王道」とも言える技術だ。しかし、その表面的な使い方に留まっているようでは、真の自動化、そしてシステム安定化の境地には到達できない。本稿では、単なるWMIクエリの羅列に終わらず、オブジェクトのライフサイクル、メモリ最適化、そしてレガシー環境との共存という、現場のエンジニアが直面するであろう「極限」の知見を、VBScriptというレンズを通して解き明かしていく。

1. WMIの核心:オブジェクトモデルとクエリ言語(WQL)の洗練

WMIは、COM (Component Object Model) を基盤としたオブジェクト指向の管理インターフェースである。これをVBScriptから扱うということは、COMオブジェクトの生成、メソッド呼び出し、プロパティ取得といった、オブジェクト指向の基本に忠実であることが求められる。

1.1. `SWbemLocator` と `SWbemServices`:接続の生命線

リモートPCへのWMIアクセスは、`SWbemLocator` オブジェクトから始まる。これは、WMIサービスへの「接続口」を確立するためのエントリポイントだ。

‘ VBScript
Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
‘ 接続文字列の構築。セキュリティのために、必要最低限の認証情報を指定する。
‘ 例: objSWbemServices = objSWbemLocator.ConnectServer(“RemotePCName”, “root\cimv2”, “Username”, “Password”)

ここで重要なのは、`ConnectServer` メソッドの引数だ。特に、リモートPC名、名前空間 (`root\cimv2` が一般的)、そして認証情報。パスワードをスクリプト内に直接記述することは、セキュリティ上のリスクを孕む。環境変数や、より安全な方法(例えば、実行時にユーザーに入力を求める、またはActive Directoryのサービスアカウントを利用するなど)を検討すべきだ。

1.2. WQL (WMI Query Language):SQLライクな世界

WMIクエリ言語 (WQL) は、SQLに似た構文でWMIリソースを検索・操作するための言語だ。しかし、その「似ている」という点に惑わされてはならない。WMIのクラス構造やプロパティは、リレーショナルデータベースとは全く異なる。

例えば、CPU使用率を取得するには、`Win32_Processor` クラスの `LoadPercentage` プロパティを使用する。

‘ VBScript
‘ WQLクエリ例:CPU使用率の取得
strWQL = “SELECT LoadPercentage FROM Win32_Processor”
Set colItems = objSWbemServices.ExecQuery(strWQL)

For Each objItem In colItems
Wscript.Echo “CPU Usage: ” & objItem.LoadPercentage & “%”
Next

極限の知見:

  • 名前空間の理解: `root\cimv2` 以外にも、`root\securitycenter2` (セキュリティ情報) など、様々な名前空間が存在する。管理したい対象に応じて、適切な名前空間を指定することが重要だ。
  • クラスとプロパティの探索: `wbemtest.exe` ツールや、PowerShellの `Get-WmiObject -List` コマンドレットなどで、利用可能なクラスとそのプロパティを事前に把握しておくことが、効率的なスクリプト作成の鍵となる。
  • パフォーマンスへの影響: WQLクエリは、特にリモートで実行する場合、ネットワーク帯域とサーバーリソースを消費する。必要最低限のプロパティのみを選択し、不要なワイルドカード (“) は避けるべきだ。

2. オブジェクトのライフサイクル管理とメモリ最適化

VBScriptは、COMオブジェクトを多用する。これらのオブジェクトは、使用後に適切に解放しないと、メモリリークやリソースの枯渇を引き起こす可能性がある。特に、長時間稼働するスクリプトや、多数のリモートPCを処理する場合には、この点は極めて重要となる。

2.1. `Set obj = Nothing` の重要性

VBScriptでは、オブジェクトの解放は `Set obj = Nothing` を明示的に実行することで行う。これは、COMオブジェクトが保持しているリソース(メモリ、ハンドルなど)をシステムに返却する、最も基本的な操作だ。

‘ VBScript
Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
Set objSWbemServices = objSWbemLocator.ConnectServer(“RemotePCName”, “root\cimv2”, “Username”, “Password”)

‘ … WMI処理 …

‘ オブジェクトの解放
Set objSWbemServices = Nothing
Set objSWbemLocator = Nothing

極限の知見:

  • ループ内での解放: 複数のリモートPCをループ処理する場合、各ループの終わりに、そのループで生成されたCOMオブジェクトを解放することを徹底する。

`For Each objItem In colItems` のようなコレクションも、ループ終了時に `Set objItem = Nothing` を行うことで、よりクリーンにリソースを解放できる。

  • エラーハンドリングと解放: `On Error Resume Next` を使用する際でも、エラー発生後もオブジェクトの解放処理は必ず実行されるように、`Err.Clear` と共に `Set obj = Nothing` を配置する。
  • VB.NET/C#との比較: .NET Frameworkでは、`IDisposable` インターフェースや `using` ステートメントによって、より自動的かつ安全なリソース管理が実現されている。VBScriptで同等の効果を得るためには、開発者の細心の注意が不可欠だ。

2.2. パフォーマンスチューニング:`ExecQuery` vs `ExecMethod`

WMI操作は、主に `ExecQuery` (クエリ実行) と `ExecMethod` (メソッド実行) の2つに大別される。

  • `ExecQuery`: データの取得や、特定の条件に合致するオブジェクトの列挙に用いられる。
  • `ExecMethod`: プロセスの強制終了 (`Win32_Process` クラスの `Terminate` メソッド) や、サービスの再起動 (`Win32_Service` クラスの `StartService` / `StopService` メソッド) など、アクションの実行に用いられる。

極限の知見:

  • メソッド呼び出しの効率: 特定のプロセスを強制終了する場合、`ExecQuery` でプロセスIDを特定し、その後 `ExecMethod` で `Terminate` を呼び出す、という2段階の処理になる。これが最も一般的で、かつ安全な方法だ。
  • `InvokeMethod` の利用: `ExecMethod` の代わりに `InvokeMethod` を使用することも可能だ。`InvokeMethod` は、より低レベルなCOMメソッド呼び出しに近く、引数の扱いに注意が必要だが、場合によってはパフォーマンス面で有利になることもある。ただし、VBScriptでの使用は一般的に `ExecMethod` の方が直感的だ。
  • リモート処理のオーバーヘッド: リモートでメソッドを呼び出す場合、ネットワーク遅延がパフォーマンスに大きく影響する。可能であれば、ローカルで処理を完結させる、あるいは、バッチ処理としてまとめて実行するなどの工夫が必要だ。

3. レガシー環境の保守とシステム間連携

現代のインフラ環境は、最新OSとレガシーOSが混在していることが多い。VBScriptは、そのOS非依存性の高さから、このような環境での自動化に依然として有効な手段だ。

3.1. OSバージョンの違いによるWMIクラスの差異

Windowsのバージョンアップに伴い、WMIクラスの定義や利用可能なプロパティが変更されることがある。

  • 例: `Win32_ComputerSystem` クラスの `NumberOfProcessors` プロパティは、物理CPUコア数を返す。論理プロセッサ数(ハイパースレッディング含む)を取得したい場合は、`Win32_Processor` クラスを複数回カウントする必要がある。

極限の知見:

  • バージョンチェック: スクリプトの冒頭で、OSバージョンをチェックし、バージョンごとに処理を分岐させる。

‘ VBScript
Dim objOS, strOSVersion
Set objOS = GetObject(“winmgmts:\\.\root\cimv2:Win32_OperatingSystem”)
strOSVersion = objOS.Version

‘ 例: Windows Server 2008 R2 以降で利用可能なクラス/プロパティがある場合
If StrComp(Left(strOSVersion, 3), “6.1”, vbTextCompare) >= 0 Then ‘ Windows 7 / Server 2008 R2 以降
‘ 新しいWMIクラス/プロパティを使用
Else
‘ 互換性のある処理を実行
End If
Set objOS = Nothing

  • COMオブジェクトの互換性: 古いOSでは、最新のCOMコンポーネントが利用できない場合がある。`CreateObject` で指定するProgIDが、ターゲットOSでサポートされているかを確認する。

3.2. VBScriptと他のスクリプト言語・ツールの連携

VBScript単体で完結しない場合、他のツールとの連携が不可欠となる。

  • PowerShell: WMI操作に関しては、PowerShellの方がより強力で簡潔な記述が可能。VBScriptからPowerShellスクリプトを呼び出す、あるいは、PowerShellで生成したデータをVBScriptで処理するといった連携が考えられる。
  • バッチファイル: `wmic` コマンドラインツールは、WMI情報を取得・操作するための強力なコマンドラインインターフェース。VBScriptから `wmic` コマンドを実行し、その結果をパースする、という手法も有効。

‘ VBScript – wmic コマンドの実行例
Dim objShell, strCommand, strOutput
Set objShell = CreateObject(“WScript.Shell”)

‘ 例: リモートPCのサービス状態を取得
strCommand = “wmic /node:RemotePCName /user:Username /password:Password service where Name=’ServiceName’ get State /value”
Set colOutputs = objShell.Exec(“%comspec% /c ” & strCommand) ‘ cmd.exe経由で実行

strOutput = colOutputs.StdOut.ReadAll
Wscript.Echo strOutput

Set objShell = Nothing

極限の知見:

  • `wmic` の出力解析: `wmic` コマンドの出力は、デフォルトでは整形されている。`/value` オプションなどを利用して、パースしやすい形式で出力させることが重要。
  • エラーコードの確認: `objShell.Exec` の戻り値や `colOutputs.ExitCode` を確認し、コマンド実行が成功したかどうかの判定を確実に行う。
  • セキュリティ: `wmic` コマンドに直接パスワードを渡すのは避けるべき。必要であれば、実行時の入力を求める、あるいは、API経由で認証情報を渡すなどの代替手段を検討する。

4. 実践的なシナリオ:プロセス監視とサービス管理

ここでは、CPU/メモリ使用率の取得、特定プロセスの強制終了、サービスの再起動といった、具体的な管理タスクをVBScriptで実現するコード例を示す。

4.1. CPU/メモリ使用率の取得

‘ VBScript
‘ WMI接続オブジェクト
Dim objSWbemLocator, objSWbemServices

‘ 接続先PC名、ユーザー名、パスワード (必要に応じて変更)
Const REMOTE_PC = “YourRemotePCName”
Const WMI_USER = “YourUsername”
Const WMI_PASS = “YourPassword”
Const WMI_NAMESPACE = “root\cimv2”

‘ —– 接続処理 —–
On Error Resume Next
Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
If Err.Number <> 0 Then
Wscript.Echo “Error creating SWbemLocator: ” & Err.Description
Wscript.Quit
End If

Set objSWbemServices = objSWbemLocator.ConnectServer(REMOTE_PC, WMI_NAMESPACE, WMI_USER, WMI_PASS)
If Err.Number <> 0 Then
Wscript.Echo “Error connecting to WMI on ” & REMOTE_PC & “: ” & Err.Description
Set objSWbemLocator = Nothing
Wscript.Quit
End If
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ —– CPU使用率の取得 —–
Dim colProcessors, objProcessor
Dim cpuUsage ‘ CPU使用率を格納する変数
cpuUsage = 0 ‘ 初期化

strWQL_CPU = “SELECT LoadPercentage FROM Win32_Processor”
Set colProcessors = objSWbemServices.ExecQuery(strWQL_CPU)

For Each objProcessor In colProcessors
cpuUsage = cpuUsage + objProcessor.LoadPercentage ‘ マルチコアの場合は合計
Next

‘ 平均CPU使用率を計算 (コア数で割る)
Dim processorCount
processorCount = colProcessors.Count
If processorCount > 0 Then
cpuUsage = cpuUsage / processorCount
Else
cpuUsage = 0 ‘ コアが見つからなかった場合
End If

Wscript.Echo “— CPU Usage —”
Wscript.Echo “Remote PC: ” & REMOTE_PC
Wscript.Echo “CPU Usage: ” & Round(cpuUsage, 2) & “%”

‘ —– メモリ使用率の取得 —–
Dim colMemory, objMemory
Dim totalMemory, freeMemory, usedMemory
Dim memoryUsagePercent ‘ メモリ使用率

totalMemory = 0
freeMemory = 0

strWQL_Memory = “SELECT TotalVisibleMemorySize, FreePhysicalMemory FROM Win32_OperatingSystem”
Set colMemory = objSWbemServices.ExecQuery(strWQL_Memory)

For Each objMemory In colMemory
totalMemory = objMemory.TotalVisibleMemorySize / 1024 ‘ MB単位に変換
freeMemory = objMemory.FreePhysicalMemory / 1024 ‘ MB単位に変換
Next

usedMemory = totalMemory – freeMemory
If totalMemory > 0 Then
memoryUsagePercent = (usedMemory / totalMemory) 100
Else
memoryUsagePercent = 0 ‘ TotalMemoryが0の場合は計算不能
End If

Wscript.Echo vbCrLf & “— Memory Usage —”
Wscript.Echo “Total Physical Memory: ” & Round(totalMemory, 2) & ” MB”
Wscript.Echo “Free Physical Memory: ” & Round(freeMemory, 2) & ” MB”
Wscript.Echo “Used Physical Memory: ” & Round(usedMemory, 2) & ” MB”
Wscript.Echo “Memory Usage: ” & Round(memoryUsagePercent, 2) & “%”

‘ —– オブジェクトの解放 —–
Set colProcessors = Nothing
Set colMemory = Nothing
Set objSWbemServices = Nothing
Set objSWbemLocator = Nothing

Wscript.Echo vbCrLf & “Script finished.”

4.2. 特定プロセスの強制終了

‘ VBScript
‘ WMI接続オブジェクト
Dim objSWbemLocator, objSWbemServices

‘ 接続先PC名、ユーザー名、パスワード (必要に応じて変更)
Const REMOTE_PC = “YourRemotePCName”
Const WMI_USER = “YourUsername”
Const WMI_PASS = “YourPassword”
Const WMI_NAMESPACE = “root\cimv2”

‘ 終了させるプロセス名 (例: notepad.exe)
Const PROCESS_NAME = “notepad.exe”

‘ —– 接続処理 —–
On Error Resume Next
Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
If Err.Number <> 0 Then
Wscript.Echo “Error creating SWbemLocator: ” & Err.Description
Wscript.Quit
End If

Set objSWbemServices = objSWbemLocator.ConnectServer(REMOTE_PC, WMI_NAMESPACE, WMI_USER, WMI_PASS)
If Err.Number <> 0 Then
Wscript.Echo “Error connecting to WMI on ” & REMOTE_PC & “: ” & Err.Description
Set objSWbemLocator = Nothing
Wscript.Quit
End If
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ —– プロセスの検索と終了 —–
Dim colProcess, objProcess
Dim processFound ‘ プロセスが見つかったかどうかのフラグ
processFound = False

‘ プロセス名でフィルタリング
strWQL_Process = “SELECT FROM Win32_Process WHERE Name = ‘” & PROCESS_NAME & “‘”
Set colProcess = objSWbemServices.ExecQuery(strWQL_Process)

If colProcess.Count > 0 Then
For Each objProcess In colProcess
Wscript.Echo “Found process: ” & objProcess.Name & ” (PID: ” & objProcess.ProcessId & “)”
Wscript.Echo “Terminating process…”

‘ プロセスを終了させる (Terminateメソッド)
‘ InvokeMethodは、メソッド名、引数配列を渡す
‘ Terminateメソッドは引数なし
objProcess.Terminate() ‘ または objSWbemServices.ExecMethod(“Win32_Process.Handle='” & objProcess.Handle & “‘”, “Terminate”)

Wscript.Echo “Process terminated.”
processFound = True
Next
Else
Wscript.Echo “Process ‘” & PROCESS_NAME & “‘ not found on ” & REMOTE_PC & “.”
End If

‘ —– オブジェクトの解放 —–
Set colProcess = Nothing
Set objSWbemServices = Nothing
Set objSWbemLocator = Nothing

Wscript.Echo vbCrLf & “Script finished.”

4.3. サービスの再起動

‘ VBScript
‘ WMI接続オブジェクト
Dim objSWbemLocator, objSWbemServices

‘ 接続先PC名、ユーザー名、パスワード (必要に応じて変更)
Const REMOTE_PC = “YourRemotePCName”
Const WMI_USER = “YourUsername”
Const WMI_PASS = “YourPassword”
Const WMI_NAMESPACE = “root\cimv2”

‘ 再起動するサービス名 (例: wuauserv – Windows Update Service)
Const SERVICE_NAME = “wuauserv”

‘ —– 接続処理 —–
On Error Resume Next
Set objSWbemLocator = CreateObject(“WbemScripting.SWbemLocator”)
If Err.Number <> 0 Then
Wscript.Echo “Error creating SWbemLocator: ” & Err.Description
Wscript.Quit
End If

Set objSWbemServices = objSWbemLocator.ConnectServer(REMOTE_PC, WMI_NAMESPACE, WMI_USER, WMI_PASS)
If Err.Number <> 0 Then
Wscript.Echo “Error connecting to WMI on ” & REMOTE_PC & “: ” & Err.Description
Set objSWbemLocator = Nothing
Wscript.Quit
End If
On Error GoTo 0 ‘ エラーハンドリングを元に戻す

‘ —– サービス状態の確認と再起動 —–
Dim objService
Dim servicePath

‘ サービス名からWMIオブジェクトを取得
‘ Handleプロパティではなく、__PATHプロパティを使用する
servicePath = “‘” & WMI_NAMESPACE & “:Win32_Service.Name='” & SERVICE_NAME & “‘”
Set objService = objSWbemServices.Get(servicePath)

If objService Is Nothing Then
Wscript.Echo “Service ‘” & SERVICE_NAME & “‘ not found on ” & REMOTE_PC & “.”
Else
Wscript.Echo “Service: ” & objService.Name & ” (State: ” & objService.State & “)”

‘ サービスが実行中であれば停止
If objService.State = “Running” Then
Wscript.Echo “Stopping service…”
‘ StopServiceメソッドを実行
objService.StopService()
Wscript.Echo “Service stopped.”
End If

‘ サービスを開始
Wscript.Echo “Starting service…”
‘ StartServiceメソッドを実行
objService.StartService()
Wscript.Echo “Service started.”

‘ 状態を再確認 (オプション)
Set objService = objSWbemServices.Get(servicePath) ‘ オブジェクトを再取得して状態を更新
Wscript.Echo “Current service state: ” & objService.State
End If

‘ —– オブジェクトの解放 —–
Set objService = Nothing
Set objSWbemServices = Nothing
Set objSWbemLocator = Nothing

Wscript.Echo vbCrLf & “Script finished.”

5. まとめ:VBScriptの未来と、現場のエンジニアへのメッセージ

VBScriptは、そのシンプルさとOSへの統合性から、今なお多くの現場で活用されている。今回解説したWMIによるリモート管理は、その代表的な例だ。しかし、この技術を真に使いこなすためには、単にコードを動かすだけでなく、オブジェクトのライフサイクル、メモリ管理、そしてOSのバージョン間の差異といった、より深いレベルでの理解が不可欠となる。

レガシーシステムを保守し、進化させるためには、過去の技術を深く理解し、その上で最新の技術と組み合わせる「ハイブリッドな視点」が求められる。VBScriptも、PowerShellやPythonといった新しい言語と共存し、それぞれの得意分野を活かすことで、その価値をさらに高めることができるだろう。

諸君、このVBScriptという古くて新しい武器を手に、日々のインフラ管理業務を、より効率的かつ堅牢なものへと変革していくことを期待する。技術の深淵を覗き、真髄を追求する者だけが、真の自動化を成し遂げることができるのだから。

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