VBAとWMIで実現するリモートPC管理:非対話型でのプロセス起動とインストール済みアプリケーションの監査

Tech

style_prompt

隠蔽メタデータ

業務カテゴリ: VBA/WMI (Windows Management Instrumentation) 対象システム: Microsoft Excel/Access VBA 最適化手法: Late Binding, 配列処理, エラーハンドリング

難易度: 専門家向け (DCOM設定知識要求)

開示バッジ

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

VBAとWMIで実現するリモートPC管理:非対話型でのプロセス起動とインストール済みアプリケーションの監査

【背景と目的】

数多のクライアントPCの資産管理や緊急対応において、リモートデスクトップを使わずに情報を収集したり、特定のプログラムを実行したりする必要に直面します。このとき、従来のファイル共有やレジストリ操作では認証やアクセス権の問題、処理速度の低下が発生しがちです。

本ソリューションは、VBAからWMI(Windows Management Instrumentation)を介してターゲットPCに接続することで、一元的な情報取得とプロセス制御をセキュアかつ効率的に実現することを目的とします。特に、大量のPCを監査する場合に発生する「タイムアウトや認証エラー」への対策として、詳細なエラー処理を組み込みます。

【処理フロー図】

リモートPC管理に必要な接続とクエリ実行のフローです。

graph TD
    A["開始: リモートPC名と認証情報設定"] --> B{"WMI接続の試行"};
    B -- 成功 --> C["WMIサービス取得 (SWbemServices)"];
    C --> D["監査クエリ (Win32_Product) 実行"];
    D --> E["結果データを配列へ格納"];
    E --> F["リモートプロセス起動 (Win32_Process.Create)"];
    B -- 失敗 --> H["エラー処理/ログ記録"];
    F --> G["結果確認とシートへの出力"];
    G --> Z["終了"];
    H --> Z;

【実装:VBAコード】

複数のリモートPC(ターゲットリスト)に対してWMI接続を試み、インストールされているアプリケーションの一覧を取得し、さらに特定のコマンドを起動する統合プロシージャです。Late Bindingを使用するため、参照設定は不要です。

Option Explicit

' ******************************************************
' WMI管理プロシージャ
' ******************************************************
Public Sub Main_RemotePC_Management()
    ' --- 高速化のための初期設定 ---
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    Const TARGET_SHEET As String = "WMI_Results"
    Dim ws As Worksheet
    Dim vTargetPCs As Variant
    Dim i As Long

    ' ターゲットPCリストを配列から取得 (例として3台)
    ' 実際にはシート上のリストやデータベースから読み込むことを推奨
    vTargetPCs = Array("PC-A01", "PC-B02", "PC-C03") 

    ' 結果出力シートの準備
    On Error Resume Next
    Set ws = ThisWorkbook.Sheets(TARGET_SHEET)
    If Err.Number <> 0 Then
        Set ws = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
        ws.Name = TARGET_SHEET
    End If
    On Error GoTo 0

    ' ヘッダ書き込み
    ws.Cells.Clear
    ws.Range("A1:G1").Value = Array("PC名", "ステータス", "アプリ名", "バージョン", "ベンダー", "インストール日", "プロセス起動結果")
    Dim lRow As Long: lRow = 2

    ' 各PCに対して処理を実行
    For i = LBound(vTargetPCs) To UBound(vTargetPCs)
        Dim sPCName As String
        sPCName = CStr(vTargetPCs(i))

        ' 1. アプリケーションリストの取得
        Dim vAppList() As Variant
        If Get_Remote_App_List(sPCName, "root\cimv2", "", "", vAppList) Then
            ' 取得データを行方向に転置し、配列としてシートに書き込む
            If UBound(vAppList, 1) > 0 Then
                Dim arrOutput As Variant
                ReDim arrOutput(1 To UBound(vAppList, 1), 1 To 6) ' PC名, ステータス, 取得データ4列

                Dim j As Long
                For j = 1 To UBound(vAppList, 1)
                    arrOutput(j, 1) = sPCName
                    arrOutput(j, 2) = "成功 (データ取得)"
                    arrOutput(j, 3) = vAppList(j, 1)  ' Name
                    arrOutput(j, 4) = vAppList(j, 2)  ' Version
                    arrOutput(j, 5) = vAppList(j, 3)  ' Vendor
                    arrOutput(j, 6) = vAppList(j, 4)  ' InstallDate
                Next j

                ' 配列を一括出力
                ws.Cells(lRow, 1).Resize(UBound(arrOutput, 1), UBound(arrOutput, 2)).Value = arrOutput
                lRow = lRow + UBound(arrOutput, 1)
            Else
                ws.Cells(lRow, 1).Resize(1, 2).Value = Array(sPCName, "成功 (アプリなし)")
                lRow = lRow + 1
            End If
        Else
            ' 接続失敗またはエラー
            ws.Cells(lRow, 1).Resize(1, 2).Value = Array(sPCName, "接続失敗/WMIエラー")
            lRow = lRow + 1
        End If

        ' 2. リモートプロセス起動の試行
        Dim sResult As String
        sResult = Start_Remote_Process(sPCName, "calc.exe", "root\cimv2", "", "")

        ' 起動結果を最終行のG列に追記
        If lRow > 2 Then
            ws.Cells(lRow - 1, 7).Value = sResult
        Else ' 最初の行にアプリリストがない場合
             ws.Cells(lRow - 1, 7).Value = sResult
        End If

    Next i

    ' --- 後処理 ---
    ws.Columns.AutoFit
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
    MsgBox "リモートPC監査処理が完了しました。", vbInformation

End Sub

' ------------------------------------------------------
' 関数1: リモートPCのアプリケーションリストを取得する
' ------------------------------------------------------
Private Function Get_Remote_App_List( _
    ByVal sPCName As String, _
    ByVal sNamespace As String, _
    ByVal sUser As String, _
    ByVal sPass As String, _
    ByRef vOutput() As Variant _
) As Boolean

    Dim objLocator As Object
    Dim objServices As Object
    Dim colItems As Object
    Dim objItem As Object
    Dim sWQL As String
    Dim lCount As Long: lCount = 0

    On Error GoTo ErrorHandler

    Set objLocator = CreateObject("WbemScripting.SWbemLocator")

    ' WMIサービスの接続 (ConnectServerで認証情報を渡す)
    If sUser <> "" Then
        Set objServices = objLocator.ConnectServer(sPCName, sNamespace, sUser, sPass)
    Else
        ' 実行ユーザーの認証情報を使用
        Set objServices = objLocator.ConnectServer(sPCName, sNamespace)
    End If

    ' WQL (WMI Query Language) を使用してインストール済み製品情報をクエリ
    sWQL = "SELECT Name, Version, Vendor, InstallDate FROM Win32_Product"
    Set colItems = objServices.ExecQuery(sWQL)

    ' 取得したデータを一旦配列に格納
    If colItems.Count > 0 Then
        ReDim vOutput(1 To colItems.Count, 1 To 4)
        For Each objItem In colItems
            lCount = lCount + 1
            vOutput(lCount, 1) = objItem.Name
            vOutput(lCount, 2) = objItem.Version
            vOutput(lCount, 3) = objItem.Vendor
            vOutput(lCount, 4) = objItem.InstallDate
        Next objItem
    Else
        ReDim vOutput(0, 0) ' データがない場合は空配列
    End If

    Get_Remote_App_List = True

CleanUp:
    Set objItem = Nothing
    Set colItems = Nothing
    Set objServices = Nothing
    Set objLocator = Nothing
    Exit Function

ErrorHandler:
    ' 接続失敗やWQLエラーが発生した場合
    Debug.Print "WMI接続エラー (" & sPCName & "): " & Err.Description
    Get_Remote_App_List = False
    Resume CleanUp
End Function

' ------------------------------------------------------
' 関数2: リモートPCでプロセスを起動する
' ------------------------------------------------------
Private Function Start_Remote_Process( _
    ByVal sPCName As String, _
    ByVal sCommand As String, _
    ByVal sNamespace As String, _
    ByVal sUser As String, _
    ByVal sPass As String _
) As String

    Dim objLocator As Object
    Dim objServices As Object
    Dim objProcessService As Object ' Win32_ProcessStartup
    Dim lngReturn As Long

    On Error GoTo ErrorHandler

    Set objLocator = CreateObject("WbemScripting.SWbemLocator")

    If sUser <> "" Then
        Set objServices = objLocator.ConnectServer(sPCName, sNamespace, sUser, sPass)
    Else
        Set objServices = objLocator.ConnectServer(sPCName, sNamespace)
    End If

    ' Win32_Processクラスを取得
    Set objProcessService = objServices.Get("Win32_Process")

    ' Createメソッドを使用してプロセス起動
    ' 戻り値(lngReturn)が 0 の場合成功
    lngReturn = objProcessService.Create(sCommand)

    If lngReturn = 0 Then
        Start_Remote_Process = "プロセス起動成功 (" & sCommand & ")"
    Else
        Start_Remote_Process = "プロセス起動失敗 (コード: " & lngReturn & ")"
    End If

CleanUp:
    Set objProcessService = Nothing
    Set objServices = Nothing
    Set objLocator = Nothing
    Exit Function

ErrorHandler:
    Start_Remote_Process = "プロセス起動エラー (接続/権限)"
    Debug.Print "Process Error (" & sPCName & "): " & Err.Description
    Resume CleanUp
End Function

【技術解説】

WMIオブジェクトモデルの構造

WMIは、Windowsの管理情報にアクセスするためのインターフェースであり、VBAからはCOMオブジェクトとして利用します。

  1. SWbemLocator (WbemScripting.SWbemLocator): WMIサービスへの接続を提供するルートオブジェクトです。ConnectServer メソッドを使用することで、リモートマシン名、WMIネームスペース(通常はroot\cimv2)、認証情報(ユーザー名とパスワード)を指定して接続できます。これにより、実行元の権限に依存せず、必要な認証情報を用いて接続することが可能になります。

  2. SWbemServices: 特定のネームスペースに接続された後のサービスオブジェクトで、WQL(WMI Query Language)を実行するための ExecQuery メソッドや、特定の管理クラスを取得するための Get メソッドを提供します。

  3. ExecQueryWin32_Product: ExecQuery はSQLライクなWQLを実行し、結果を SWbemObjectSetcolItems)として返します。Win32_Product クラスは、システムにインストールされているアプリケーション情報を保持しますが、このクラスへのクエリ実行はシステムリソースを大量に消費する(再設定処理が実行されるため)という点に留意が必要です。

  4. Win32_Process.Create メソッド: リモートPCでプロセスを起動する際に使用します。これは、WMIクラスのインスタンスメソッドではなく、静的メソッドとして提供されています。戻り値が 0 であれば成功、それ以外はWMIのエラーコードを示します。

高速化のポイント

VBAで大量のデータ処理を行う場合、WMIから取得した結果(colItems)を逐次シートに出力せず、一時的にVBAの多次元配列 vOutput に格納します。処理完了後、ws.Cells(...).Value = arrOutput の形式で一度にシートに書き出すことで、Excelの再描画やセル操作のオーバーヘッドを劇的に削減しています。また、冒頭で ScreenUpdating = False および Calculation = xlCalculationManual を設定し、処理中の描画と自動再計算を停止しています。

【注意点と運用】

1. DCOMとファイアウォールの設定(最大の落とし穴)

WMIによるリモート接続は、DCOM(Distributed Component Object Model)プロトコルを使用します。ターゲットPC側で以下の設定が適切に行われていないと、認証情報が正しくても接続が拒否されます。

  • ファイアウォール: リモート管理(WMI)の着信ルールが有効になっている必要があります。通常、ポート135(RPC Endpoint Mapper)と、それ以降の動的ポート範囲が許可されている必要があります。

  • DCOMセキュリティ: リモートPCのDCOM構成(dcomcnfg)において、リモート起動およびアクセス権限が、VBAを実行するユーザーまたはコード内で指定したユーザーに許可されている必要があります。

2. Win32_Product クラスの使用制限

Win32_Product クラスをクエリすると、システムがインストーラーの整合性チェックを実行するため、大規模環境や頻繁な実行には不向きです。大量監査を行う場合は、代わりにレジストリを直接操作(HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall)するか、Win32_InstalledSoftware(非標準ネームスペースの場合がある)の利用を検討してください。

3. 認証情報の取り扱い

本コードではユーザー名とパスワードを変数として渡していますが、これらをVBAコード内に平文で記述することはセキュリティ上極めて危険です。実運用では、認証情報をセキュアな外部ファイルやデータベース(暗号化必須)から読み込むか、実行ユーザーの権限でカバーできる範囲に限定してください。

【まとめ】

VBAとWMIを組み合わせたリモートPC管理の運用のコツは以下の3点です。

  1. Late Bindingの徹底: CreateObject を使用することで、参照設定の依存性を排除し、異なる環境間での互換性を保ちます。

  2. DCOMとネットワーク設定の事前確認: WMI操作の成否は、VBAコードよりもリモートPC側のファイアウォールとDCOMセキュリティ設定に大きく依存します。実行前にターゲット環境の設定を監査ツールなどで確認してください。

  3. 配列処理による速度維持: WMI接続のオーバーヘッドを最小化するため、取得したデータは配列に格納し、Excel操作(シートへの書き出し)は最小限に抑えることで、多数のPCへの並列処理時間を短縮します。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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