【テクニカル・上級編】CurrentDb.Containersを活用したデータベース内のオブジェクト権限と所有者の管理 – Access VBA解析バイブル

スポンサーリンク

Access VBAを掌握する極限の知見:CurrentDb.Containersによるセキュリティ監査の深層

レガシーシステムの最前線に立ち続ける我々にとって、Microsoft Accessは単なる「簡易データベース」ではない。適切に調教されれば、基幹システムを支える堅牢なフロントエンド・バックエンドとして機能する強力なアーキテクチャである。

しかし、現場で長年運用されてきたAccess MDB/ACCDBファイルにおいて、最大のブラックボックスとなるのが「オブジェクトの所有者と権限管理」だ。

「誰がどのテーブルにアクセスできるのか?」
「退職した旧担当者のアカウントがACL(アクセスコントロールリスト)に残っていないか?」

こうしたセキュリティ監査の要求に対し、システムテーブル(`MSysObjects`など)を直接SQLで叩くアプローチは、Accessの内部仕様変更リスクや整合性の観点から愚策と言わざるを得ない。我々はDAO(Data Access Objects)が提供する正規のオブジェクトモデル、すなわち `CurrentDb.Containers` を使って、安全かつ網羅的にメタデータを抽出しなければならない。

今回は、Accessのセキュリティモデルの深層に踏込み、メモリ管理の鉄則を押さえた極限の監査コードを提示する。

1. Accessセキュリティモデルの構造とDAO Containers

Accessのセキュリティとオブジェクト管理は、DAOの `Container` オブジェクトと `Document` オブジェクトによって階層的に管理されている。

  • Containers コレクション: データベース内のオブジェクトの種類(Tables, Queries, Forms, Reports, Modules, Databasesなど)を格納する。
  • Documents コレクション: 各コンテナ内に存在する個々のオブジェクト(具体的なテーブル名やフォーム名など)を格納する。

ここで重要なのは、「何でもかんでも `CurrentDb` を生で叩くな」という原則だ。
`CurrentDb` は呼び出すたびに新しいデータベースのインスタンス(`Database` オブジェクト)をヒープ上に生成する。これをループ内で安易に使い回すと、COMの参照カウントが崩壊し、Access特有の「メモリリーク」「リソース不足エラー」「謎の動作不良」を引き起こす。

シニアエンジニアであれば、`CurrentDb` は必ずローカル変数に一度だけキャッシュし、処理終了後は明示的にオブジェクトを解放する(`Nothing`を代入する)鉄の意志を持つべきだ。

2. 実装:オブジェクトの所有者と権限を完全抽出するVBAプロシージャ

以下に、データベース内の全コンテナとドキュメントを走査し、所有者(Owner)およびパーミッション(Permissions)をイミディエイトウィンドウに出力、あるいはログテーブルへ書き出すための実用コードを示す。

Option Compare Database
Option Explicit

‘ =========================================================================
‘ 処理名 : Audit_DatabasePermissions
‘ 概要 : CurrentDb.Containersを走査し、オブジェクトの所有者と権限を監査する
‘ 備考 : DAOのライフサイクルを厳密に管理し、メモリリークを完全阻止する
‘ =========================================================================
Public Sub Audit_DatabasePermissions()
Dim dbs As DAO.Database
Dim cnts As DAO.Containers
Dim cnt As DAO.Container
Dim docs As DAO.Documents
Dim doc As DAO.Document

Dim targetContainerNames(3) As String
Dim i As Long

‘ 監査対象とする主要なコンテナ(Tablesはシステム都合で挙動が異なるため注意)
targetContainerNames(0) = “Tables”
targetContainerNames(1) = “Queries”
targetContainerNames(2) = “Forms”
targetContainerNames(3) = “Reports”

On Error GoTo ErrorHandler

‘ 【鉄則】CurrentDbは必ず変数に格納し、多重呼び出しによるメモリ肥大化を防ぐ
Set dbs = CurrentDb
Set cnts = dbs.Containers

Debug.Print “=== Access Security & Ownership Audit Start ===”
Debug.Print “Timestamp: ” & Now
Debug.Print “————————————————–”

‘ コンテナコレクションの走査
For i = LBound(targetContainerNames) To UBound(targetContainerNames)
‘ コンテナが存在するか安全に取得するためエラー監視
On Error Resume Next
Set cnt = cnts(targetContainerNames(i))
If Err.Number <> 0 Then
Debug.Print “Container [” & targetContainerNames(i) & “] not found or accessible.”
Err.Clear
GoTo ContinueContainer
End If
On Error GoTo ErrorHandler

Debug.Print “Container: ” & cnt.Name

‘ ドキュメント(個別オブジェクト)の走査
Set docs = cnt.Documents
For Each doc In docs
‘ 出力項目: オブジェクト名 / 所有者 / 権限マスク
Debug.Print ” -> Object: ” & doc.Name & _
” | Owner: ” & doc.Owner & _
” | Permissions: ” & Hex(doc.Permissions)
Next doc

Debug.Print “————————————————–”

ContinueContainer:
‘ ループ内でのオブジェクト解放
Set docs = Nothing
Set cnt = Nothing
Next i

Debug.Print “=== Audit Completed Successfully ===”
GoTo Finally

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “監査モジュール”

Finally:
‘ 【最重要】オブジェクト変数の明示的な解放によるメモリ最適化
Set docs = Nothing
Set cnt = Nothing
Set cnts = Nothing
Set dbs = Nothing
End Sub

3. コードの急所:なぜこの書き方でなければならないのか

① `CurrentDb` のキャッシュ戦略

前述の通り、`CurrentDb` はスレッドローカルなDAOセッションを新規構築する。ループの条件式や内側で `CurrentDb.Containers` などと直接書くと、暗黙的に生成されたインスタンスが即座に解放されず、Accessの内部ポインタを圧迫する。
変数 `dbs` に一度だけ代入し、スコープを抜ける直前に `Set dbs = Nothing` で確実に破棄する。これが長期間稼働するAccessシステムにおけるアンチクラッシュの定石である。

② 所有者(Owner)と権限(Permissions)の解釈

`doc.Owner` プロパティは、そのオブジェクトを所有するワークグループアカウント(またはWindowsユーザー/グループ)を返す。
また、`doc.Permissions` は長整数型(Long)のビットマスクとして表現される。例えば、完全制御(dbSecFullAccess)、読取(dbSecRetrieveData)、更新(dbSecWriteData)などの権限がビット演算子で格納されている。
必要に応じて、この `doc.Permissions` を定数と比較し、不要な権限が付与されていないかをプログラム側で検知・アラートさせることが可能だ。

4. レガシー環境・システム間連携への応用

現代のインフラストラクチャにおいて、Access単体でセキュリティを完結させるケースは減少し、Active Directory(AD)やAzure AD(Entra ID)と連携したファイルサーバー上のACCDB運用、あるいはSQL Server(ODBC/MDE)への移行期における過渡期的な監査が求められる。

もしこのVBA監査結果を外部システム(Power BI、あるいは社内監視基盤)へ連携したいのであれば、イミディエイトウィンドウへの出力ではなく、ローカルの監査用一時テーブル(あるいはトランザクションログテーブル)にINSERT文で流し込むよう拡張するとよい。

‘ 例: 取得したメタデータを監査用テーブルへ永続化する場合のイメージ
Dim sql As String
sql = “INSERT INTO T_AuditLog (ObjContainer, ObjName, ObjOwner, CheckedAt) VALUES (” & _
“‘” & cnt.Name & “‘, ‘” & doc.Name & “‘, ‘” & doc.Owner & “‘, #” & Now & “#)”
dbs.Execute sql, dbFailOnError

総括

Access VBAのスキルは、単に「マクロの記録の延長」ではない。底にあるCOMのライフサイクル、DAOのオブジェクトモデル、そしてメモリ管理のメカニズムを理解して初めて「エンジニアリング」の領域に到達する。

`CurrentDb.Containers` を使いこなすことは、データベースの内部構造を掌中に収めることに他ならない。
不確実性の高いレガシーシステムであっても、正確なコードと理論に裏打ちされたアーキテクチャがあれば、完全にコントロールし続けることが可能だ。

プロフェッショナルたる者、感覚でコードを書かず、常にメモリとオブジェクトの生死を意識してキーボードを叩け。

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