【実務・中級編】【上級プロフェッショナル向け】FSOとWMI(Win32_Volume)連携によるBitLocker暗号化ドライブの状態判定と自動処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

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

「動けばいい」は素人の発想だ:WMIとFSOを同期させ、BitLocker保護下でのみ機密操作を完遂する堅牢な設計論

業務自動化の現場において、多くのエンジニアが陥る致命的な罠がある。それは、「ファイル操作(FSO)という『結果』にのみ焦点を当て、その実行環境の『状態(コンテキスト)』を無視すること」だ。

機密情報を扱うエンタープライズ・スクリプトにおいて、単に `FileSystemObject` でファイルをコピーするコードを書くのは、鍵のかかっていない部屋に金庫を運び込むようなものだ。もし、そのドライブがBitLockerで暗号化されていなければ、物理的な紛失や盗難の際にデータは丸裸になる。

本稿では、VBScriptの限界を突破し、WMI(Windows Management Instrumentation)を用いてシステムのセキュリティ状態を判定した後、安全が確認された場合のみFSOを実行する、プロフェッショナル仕様のアーキテクチャを伝授する。

1. なぜ FSO 単体では不十分なのか?

`FileSystemObject` (FSO) は、ファイルやフォルダの操作において極めて強力かつ直感的だ。しかし、FSOはあくまで「ファイルシステムという抽象化された層」の上で動く「作業員」に過ぎない。

FSOには、「今、このドライブが暗号化されているか?」を判断する知能はない。

プロフェッショナルな設計では、作業員(FSO)に指示を出す前に、監査役(WMI)を介入させる必要がある。

  • 監査役 (WMI/Win32_Volume): システムの構成情報、セキュリティポリシー、暗号化状態を検証する。
  • 作業員 (FSO): 監査役が「GO」を出した場合のみ、物理的なI/O操作を実行する。

この「検証と実行の分離(Separation of Concerns)」こそが、バグを抑え込み、セキュリティリスクを最小化する鍵となる。

2. WMIによるBitLocker状態の正確な捕捉

BitLockerの状態を確認するために `Win32_LogicalDisk` を選ぶ者がいるが、それは初歩的なミスだ。高度なボリューム情報は `Win32_Volume` クラスに集約されている。

特に注目すべきは `ProtectionStatus` プロパティである。

  • `0`: 保護されていない(暗号化されていない、または無効)
  • `1`: 保護されている(暗号化が有効で、保護がアクティブ)

この値を正確に読み取り、スクリプトの実行フローを制御するロジックを組み込まなければならない。

3. プロダクション・グレードの実装例

以下に、実務でそのまま利用可能な、堅牢な設計思想に基づいたVBScriptコードを示す。このコードは、単なるコピペ用ではない。エラーハンドリング、オブジェクトのライフサイクル管理、およびセキュリティチェックのロジックが統合されている。

' ==============================================================================
' Script Name: SecureFileTransfer_BitLockerCheck.vbs
' Description: BitLockerの保護状態を確認し、安全な場合のみFSOでファイルを操作する
' Author: Lead Architect
' ==============================================================================
Option Explicit

' --- 定数定義 ---
Const WMI_NAMESPACE = "root\cimv2"
Const STATUS_PROTECTED = 1  ' BitLocker ProtectionStatus = 1 (Protected)
Const STATUS_UNPROTECTED = 0 ' BitLocker ProtectionStatus = 0 (Unprotected)

' --- メインルーチン ---
Main

Sub Main()
    Dim targetDrive, sourceFile, destFile
    targetDrive = "D:"  ' 検証対象のドライブ
    sourceFile  = "C:\Temp\SensitiveData.xlsx"
    destFile    = "D:\SecureStorage\SensitiveData.xlsx"

    On Error Resume Next

    ' 1. セキュリティ監査フェーズ
    If CheckBitLockerStatus(targetDrive) = STATUS_PROTECTED Then
        WScript.Echo "[INFO] Security Check Passed: Drive " & targetDrive & " is encrypted."
        
        ' 2. ファイル操作フェーズ
        ExecuteSecureCopy sourceFile, destFile
    Else
        ' 3. セキュリティ違反フェーズ
        WScript.StdErr.WriteLine "[ERROR] Security Violation: Drive " & targetDrive & " is NOT protected!"
        WScript.Quit 1 ' 非ゼロの終了コードで異常終了を通知
    End If

    If Err.Number <> 0 Then
        WScript.StdErr.WriteLine "[CRITICAL] Unexpected Error: " & Err.Description
        WScript.Quit 1
    End If
    On Error GoTo 0
End Sub

' ==============================================================================
' 関数: CheckBitLockerStatus
' 目的: WMIを使用して指定されたドライブのBitLocker保護状態を取得する
' 引数: driveLetter (例: "D:")
' 戻り値: ProtectionStatus (0 or 1)
' ==============================================================================
Function CheckBitLockerStatus(driveLetter)
    Dim objWMIService, colVolumes, objVolume
    Dim status
    status = STATUS_UNPROTECTED ' デフォルトは「保護なし」とする(Fail-Safe設計)

    On Error Resume Next
    Set objWMIService = GetObject("winmgmts:\\.\" & WMI_NAMESPACE)
    
    If Err.Number <> 0 Then
        WScript.StdErr.WriteLine "[ERROR] WMI Connection Failed."
        CheckBitLockerStatus = STATUS_UNPROTECTED
        Exit Function
    End If

    ' Win32_Volumeを使用して詳細なボリューム情報をクエリ
    ' DriveLetterは "D:" の形式で指定
    Set colVolumes = objWMIService.ExecQuery _
        ("SELECT ProtectionStatus FROM Win32_Volume WHERE DriveLetter = '" & driveLetter & "'")

    For Each objVolume In colVolumes
        status = objVolume.ProtectionStatus
        Exit For ' 最初に見つかった一致するボリュームを対象とする
    Next

    If Err.Number <> 0 Then
        WScript.StdErr.WriteLine "[ERROR] WMI Query Failed for drive " & driveLetter
        status = STATUS_UNPROTECTED
    End If
    On Error GoTo 0

    CheckBitLockerStatus = status
    
    ' オブジェクトの解放(メモリリーク防止)
    Set colVolumes = Nothing
    Set objWMIService = Nothing
End Function

' ==============================================================================
' サブルーチン: ExecuteSecureCopy
' 目的: FSOを使用して安全にファイルをコピーする
' ==============================================================================
Sub ExecuteSecureCopy(src, dest)
    Dim fso
    Set fso = CreateObject("Scripting.FileSystemObject")

    On Error Resume Next
    
    ' コピー先フォルダが存在しない場合のハンドリング
    Dim destFolder
    destFolder = fso.GetParentFolderName(dest)
    If Not fso.FolderExists(destFolder) Then
        WScript.StdErr.WriteLine "[ERROR] Destination folder does not exist: " & destFolder
        Exit Sub
    End If

    ' ファイルコピー実行
    fso.CopyFile src, dest, True ' True = 上書き許可

    If Err.Number <> 0 Then
        WScript.StdErr.WriteLine "[ERROR] File Copy Failed: " & Err.Description
    Else
        WScript.Echo "[SUCCESS] File securely copied to " & dest
    End If

    On Error GoTo 0
    Set fso = Nothing
End Sub

4. 設計における極限の注意点

このコードを実戦投入するにあたり、以下の3点を徹底せよ。

① Fail-Safe(フェイルセーフ)設計

`CheckBitLockerStatus` 関数内で、エラーが発生した場合には必ず `STATUS_UNPROTECTED`(保護なし)を返すように設計している。
「エラーが起きたから、とりあえず進める」という判断は、自動化エンジニアにとっての死を意味する。「不明な状態は、危険な状態である」という原則をコードに刻み込め。

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

VBScriptはメモリ管理が自動だが、WMIのようなCOMオブジェクトを扱う場合、明示的な `Set obj = Nothing` による解放は必須だ。特に、ループ内でWMIクエリを回すような設計にする場合、解放を怠るとリソース枯渇によるシステム遅延を招き、業務システム全体のパフォーマンスを毀損する。

③ エラー出力の分離

`WScript.Echo` は標準出力(コンソール)用だが、エラーは `WScript.StdErr.WriteLine` を使って標準エラー出力へ流すべきだ。これにより、ジョブスケジューラやログ収集エージェントが「正常終了」と「異常終了」を明確に区別できるようになる。

結論

真に価値のある自動化エンジニアは、コードの「短さ」ではなく、そのコードが「どのような環境下でも、意図した通りに安全に振る舞うか」を追求する。

WMIによるシステム状態の把握と、FSOによる操作の分離。この二段構えのアーキテクチャをマスターすることで、君のスクリプトは単なる「便利な道具」から、エンタープライズに耐えうる「信頼のインフラ」へと昇華する。

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