【ハードウェアインベントリ一括取得】WMIを用いた端末個体情報の自動集約
レガシーシステムの最前線、あるいは何千台もの端末が稼働するエンタープライズ環境において、インベントリ収集の自動化はインフラエンジニアの永続的な課題である。
PowerShell全盛の時代であっても、グループポリシーの制限、あるいはOSの世代混在(Windows 7の残党から最新のWindows 11まで)という現場の制約事項の前では、「追加ランタイム不要」「OSネイティブ稼働」「数行で完結するポータビリティ」を持つVBScript(WSH)の優位性は未だに揺るぎない。
今回は、WMI(Windows Management Instrumentation)の`Win32_BIOS`および`Win32_ComputerSystem`を叩き、端末の個体情報を極限まで効率よく収集、CSVデータベースへ非同期・追記型でストリーミング保存する実用スクリプトの全貌を解説する。
—
1. WMI接続における「本番の知見」:COMのライフサイクルとパフォーマンス
素人が書いたWMIスクリプトは、メモリリークの温床となる。VBScriptにおけるCOMオブジェクトの破棄は、単なる作法ではなく、OSのリソース枯渇を防ぐための防衛策である。
連鎖するオブジェクトの闇
`GetObject(“winmgmts:”)` を呼び出す際、モンケモニカル(暗黙的)に生成されるSWbemLocator、SWbemServices、SWbemObjectSetのライフサイクルを意識しているだろうか。
特にループ内でWMIクエリを発行する場合、明示的な変数への格納と、`Set obj = Nothing` による即時解放を行わなければ、スクリプト実行中のプロセスメモリは肥大化し続ける。
さらに、ドメイン環境や多重セグメントネットワークにおいて、WMIクエリはタイムアウトの罠を孕んでいる。これを回避するため、接続moniker(モニカ)には適切なセキュリティレベル(ネームスペース、認証レベル)を明示的に指定すべきである。
—
2. 実装コード:ハードウェアインベントリ一括集約スクリプト
以下のコードは、エラーハンドリング(`On Error Resume Next` の正しい使い方)、UTF-8/Shift-JISのファイルI/Oの選択、そしてWMIからの高速データ抽出を極めたプロダクションコードである。
‘ ==============================================================================
‘ Script Name : HardwareInventoryCollector.vbs
‘ Description : WMI via Win32_BIOS & Win32_ComputerSystem Hardware Inventory
‘ Architecture: VBScript / WSH (Windows Script Host)
‘ Author : Chief Architect (Legacy Systems Integration)
‘ ==============================================================================
Option Explicit
Const ForAppending = 8
Const CSV_FILE_PATH = “C:\Inventory\Hardware_Database.csv”
‘ メイン処理の実行
Call Main()
Sub Main()
Dim objFSO, objFile
Dim strComputer, objWMIService, colBIOS, objBIOS, colCS, objCS
Dim strSerial, strManufacturer, strModel, strUUID, strBiosVer, strHostName
Dim strCsvLine
‘ 1. ファイルシステムの準備と排他制御の考慮
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Call EnsureDirectory(objFSO, CSV_FILE_PATH)
‘ 2. CSVヘッダーの初期書き込み(ファイルが存在しない場合のみ)
If Not objFSO.FileExists(CSV_FILE_PATH) Then
Set objFile = objFSO.OpenTextFile(CSV_FILE_PATH, ForAppending, True)
objFile.WriteLine “HostName,Manufacturer,Model,SerialNumber,UUID,BIOSVersion,CollectionDate”
objFile.Close
Set objFile = Nothing
End If
‘ 3. ローカルホストの指定(リモート展開の場合はここを書き換える)
strComputer = “.”
‘ 4. WMIサービスの接続(接続プールの最適化とエラーマスク)
On Error Resume Next
Set objWMIService = GetObject(“winmgmts:{impersonationLevel=impersonate}!\\” & strComputer & “\root\cimv2”)
If Err.Number <> 0 Then
Call WriteLog(“WMI Connection Failed. Error: 0x” & Hex(Err.Number))
WScript.Quit 1
End If
‘ 5. Win32_ComputerSystem の取得 (Manufacturer, Model)
Set colCS = objWMIService.ExecQuery(“Select Manufacturer, Model, Name From Win32_ComputerSystem”)
For Each objCS in colCS
strManufacturer = Trim(objCS.Manufacturer)
strModel = Trim(objCS.Model)
strHostName = Trim(objCS.Name)
Next
‘ オブジェクトの早期解放
Set colCS = Nothing
‘ 6. Win32_BIOS の取得 (SerialNumber, Version)
Set colBIOS = objWMIService.ExecQuery(“Select SerialNumber, Version From Win32_BIOS”)
For Each objBIOS in colBIOS
strSerial = Trim(objBIOS.SerialNumber)
strBiosVer = Trim(objBIOS.Version)
Next
Set colBIOS = Nothing
‘ 7. UUIDの取得 (Win32_ComputerSystemProduct から取得するのが最も確実)
Dim colCSP, objCSP
Set colCSP = objWMIService.ExecQuery(“Select UUID From Win32_ComputerSystemProduct”)
For Each objCSP in colCSP
strUUID = Trim(objCSP.UUID)
Next
Set colCSP = Nothing
‘ WMIサービス自体の解放
Set objWMIService = Nothing
On Error GoTo 0
‘ データのサニタイズ(CSVのカンマ破壊を防ぐためダブルクォーテーションで囲む)
strCsvLine = _
“””” & strHostName & “””,” & _
“””” & strManufacturer & “””,” & _
“””” & strModel & “””,” & _
“””” & strSerial & “””,” & _
“””” & strUUID & “””,” & _
“””” & strBiosVer & “””,” & _
“””” & Now() & “”””
‘ 8. CSVデータベースへのアペンド書込
Set objFile = objFSO.OpenTextFile(CSV_FILE_PATH, ForAppending, True)
objFile.WriteLine strCsvLine
objFile.Close
Set objFile = Nothing
Set objFSO = Nothing
End Sub
‘ ——————————————————————————
‘ ユーティリティ: ディレクトリ存在チェックと自動生成
‘ ——————————————————————————
Sub EnsureDirectory(fso, filePath)
Dim parentPath
parentPath = fso.GetParentFolderName(filePath)
If Not fso.FolderExists(parentPath) Then
fso.CreateFolder(parentPath)
End If
End Sub
‘ ——————————————————————————
‘ ユーティリティ: 簡易ログ出力
‘ ——————————————————————————
Sub WriteLog(msg)
WScript.Echo “[” & Now() & “] ” & msg
End Sub
—
3. チーフアーキテクトが指摘する「現場の罠」とチューニング
上記のスクリプトをエンタープライズ環境で展開する際、以下のポイントに直面する。これらを事前に潰すことがエンジニアの腕の見せ所である。
A. シリアルナンバーの空値(OEMの怠慢)
安価なデスクトップPCや自作er向けマザーボード、あるいは一部の仮想環境において、`Win32_BIOS.SerialNumber` が `To Be Filled By O.E.M.` や `System Serial Number` といったダミー値、あるいは空文字で返ってくるケースがある。
厳密な資産管理を行う場合、`Win32_BIOS` だけでなく `Win32_SystemEnclosure` の `SerialNumber` をフォールバック(二重取得)するロジックを組むべきである。
B. CSVインジェクションとエスケープ
取得した `Model` や `Manufacturer` に予期せぬダブルクォーテーションが含まれている場合、CSVの構造が破壊される。今回のコードでは単純な前後囲みを行っているが、厳密にはフィールド内の `”` を `””` に置換するサニタイズ関数を噛ませるべきだ。
Function SanitizeCsv(val)
If IsNull(val) Then
SanitizeCsv = “”
Else
SanitizeCsv = Replace(CStr(val), “”””, “”””””)
End If
End Function
C. 実行権限とアーキテクチャの壁 (x86 / x64)
WMIクエリは、実行しているWScript/CScriptホストのビット数に依存する挙動を示すことがある。特にレジストリ直読みを行うような拡張インベントリの場合、WOW64のリダイレクションに足をすくわれる。幸い今回の `Win32_BIOS` や `Win32_ComputerSystem` はCIMリポジトリのコア層にあるためビット数の差異による影響を受けにくいが、スクリプト実行時は必ず `cscript.exe` を用いてバッチ処理(タスクスケジューラ等)からサイレント実行させるのが鉄則である。GUIベースの `wscript.exe` は、ポップアップエラーが画面に残るためサーバー・端末管理には不適格である。
—
総括
VBScriptは「古い言語」ではない。OSの深層APIとスクリプトエンジンを直結させるための、「極限まで無駄を削ぎ落とした薄いラッパー」である。
PowerShellが肥大化しがちな環境や、極限までリソースを切り詰めたマスターイメージ展開時のフックなど、VBScriptが最適解となる領域は依然として存在する。
オブジェクトの生成と破棄のライフサイクルを完全に掌握し、WMIの挙動を理解した者にとって、このコードベースは10年後もメンテナンスフリーで稼働し続ける堅牢なインフラストラクチャとなるだろう。
