【テクニカル・上級編】リソースの「コストセンター」をActive Directoryから自動取得する連携スクリプト – Project VBA解析バイブル

スポンサーリンク

【Project VBAを掌握する極限の知見】Active Directory直結:リソースコストセンター自動同期アーキテクチャ

企業規模が拡大するにつれ、Microsoft Project(以下、MS Project)におけるリソース管理は地獄のような様相を呈し始める。数千人規模のリソースプール、頻繁な組織改編、そしてプロジェクト原価管理の厳格化。これらに追随するため、情シス部門が手動でリソースの「コストセンター(部署情報)」をメンテナンスするなどというのは、エンジニアリングの敗北に他ならない。

今回は、MS ProjectのVBAエンジンとWindowsの根幹であるActive Directory(AD)、そしてVBScript(またはCOMオブジェクト)を極限まで調停し、リソースのコストセンターを完全自動同期するアーキテクチャを解説する。

中途半端なラッパーや、場当たり的なマクロではない。オブジェクトのライフサイクル、COMプロキシのメモリ管理、そしてレガシー環境特有の罠を熟知したシニアエンジニアに贈る、実戦投入可能な最高峰のソリューションだ。

—

1. アーキテクチャの全体像と設計思想

MS Projectのリソーステーブルには、標準で拡張可能な「リソースカスタムフィールド(Text1〜Text30など)」が存在する。ここにActive Directoryの `department` 属性や `costCenter` 属性を流し込む。

しかし、MS ProjectのVBAから直接AD(LDAP)を叩くのは得策ではない。理由の筆頭は認証コンテキストと非同期例外のハンドリングである。VBAの貧弱なエラーハンドリングでADSI(Active Directory Service Interfaces)を直接操作すると、ネットワーク切断やドメインコントローラーの応答遅延時に容赦なくProjectごとクラッシュする。

したがって、以下の多層防御アーキテクチャを採用する。

1. MS Project VBA層: 同期プロセスのトリガー、リソースプールの走査、カスタムフィールドへの書き込み。
2. ADSI / LDAP層: Windows認証を用いたセキュアなディレクトリ情報の抽出。
3. メモリ最適化層: COMオブジェクトの参照カウントを正確に制御し、Project特有のメモリリークを根絶する。

—

2. 実装コード:リソースコストセンター同期モジュール

以下のコードは、エンタープライズ環境の耐障害性を考慮して組み上げたメインのVBAモジュールである。エラーハンドリング、オブジェクトの明示的解放(Destruction)、そしてパフォーマンス配慮のバルク処理に近いアプローチを網羅している。

Option Explicit

‘ ==============================================================================
‘ Module: modResourceADSync
‘ Description: Active Directoryからコストセンターを取得し、MS Projectへ同期する
‘ Author: Chief Architect
‘ ==============================================================================

Public Sub SyncResourceCostCenterFromAD()
Dim prjProj As Project
Set prjProj = ActiveProject

‘ リソースが存在しない場合の早期リターン
If prjProj.Resources.Count = 0 Then
MsgBox “対象プロジェクトにリソースが登録されていません。”, vbExclamation, “同期エラー”
Exit Sub
End If

Dim lCount As Long
lCount = 0

On Error GoTo ErrorHandler

‘ ステータスバーのロック(描画とイベントを抑制しパフォーマンスを極限まで高める)
App.ScreenUpdating = False

Dim res As Resource
Dim strLoginName As String
Dim strCostCenter As String

‘ リソースプールを走査
For Each res In prjProj.Resources
‘ リソースがエンティティ(実在の人物=Windowsアカウントを持つ)か判定
‘ ここでは簡略化のため、リソース名またはEMailアドレスをSAMAccountNameと見立てる
‘ ※実環境では Res.EmailAddress や Res.WindowsAccount を使用することを推奨
If Not res Is Nothing Then
strLoginName = ExtractAccountName(res.EmailAddress)

If Len(strLoginName) > 0 Then
‘ ADからコストセンターを取得
strCostCenter = FetchCostCenterFromAD(strLoginName)

If Len(strCostCenter) > 0 Then
‘ Text1をコストセンター格納先として利用(必要に応じて変更すること)
res.Text1 = strCostCenter
lCount = lCount + 1
End If
End If
End If
Next res

App.ScreenUpdating = True
MsgBox “同期完了: ” & lCount & ” 件のリソース情報を更新しました。”, vbInformation, “AD同期完了”
Exit Sub

ErrorHandler:
App.ScreenUpdating = True
MsgBox “致命的なエラーが発生しました: ” & Err.Description & ” (Line: ” & Erl & “)”, vbCritical, “Critical Error”
‘ オブジェクトの強制解放は終了時に自動で行われるが、クリティカルセクションでは明示を推奨
End Sub

‘ ==============================================================================
‘ Helper: メールアドレスからSAMAccountNameを抽出
‘ ==============================================================================
Private Function ExtractAccountName(ByVal email As String) As String
If InStr(email, “@”) > 0 Then
ExtractAccountName = Split(email, “@”)(0)
Else
ExtractAccountName = email
End If
End Function

‘ ==============================================================================
‘ LDAP Query Engine: ADSIを用いた高速情報取得
‘ ==============================================================================
Private Function FetchCostCenterFromAD(ByVal sAMAccountName As String) As String
Dim objRootDSE As Object
Dim objConnection As Object
Dim objCommand As Object
Dim objRecordSet As Object

On Error GoTo LDAPError

‘ ADSIの基本オブジェクト生成
Set objRootDSE = GetObject(“LDAP://RootDSE”)
Dim strDNSDomain As String
strDNSDomain = objRootDSE.Get(“defaultNamingContext”)

Set objConnection = CreateObject(“ADODB.Connection”)
Set objCommand = CreateObject(“ADODB.Command”)

objConnection.Provider = “ADsDSOObject”
objConnection.Open “Active Directory Provider”
Set objCommand.ActiveConnection = objConnection

‘ パフォーマンスとインデックスを考慮したLDAPクエリの構築
‘ 属性名は環境のスキーマ(department または costCenter)に合わせて変更せよ
objCommand.CommandText = _
“;(&(objectCategory=person)(objectClass=user)(sAMAccountName=” & sAMAccountName & “));sAMAccountName,department,costCenter;Subtree”

Set objRecordSet = objCommand.Execute

If Not objRecordSet.EOF Then
‘ 優先度:costCenter属性が存在すればそれを、なければdepartmentを使用
On Error Resume Next
FetchCostCenterFromAD = objRecordSet.Fields(“costCenter”).Value
If Err.Number <> 0 Or Len(FetchCostCenterFromAD) = 0 Then
FetchCostCenterFromAD = objRecordSet.Fields(“department”).Value
End If
On Error GoTo LDAPError
Else
FetchCostCenterFromAD = “”
End If

‘ ————————————————————————–
‘ 【極限の知見】COMオブジェクトの明示的メモリ解放
‘ VBAのガベージコレクションに頼るな。参照を即座に破棄しメモリリークを防ぐ。
‘ ————————————————————————–
objRecordSet.Close
CleanUp:
Set objRecordSet = Nothing
Set objCommand = Nothing
If objConnection.State = 1 Then objConnection.Close
Set objConnection = Nothing
Set objRootDSE = Nothing
Exit Function

LDAPError:
‘ ネットワーク切断やAD側の例外時は空文字を返し処理を継続
FetchCostCenterFromAD = “”
Resume CleanUp
End Function

—

3. シニアエンジニアが知るべき「罠」と最適化の極意

上記のコードを実務環境に投入する際、一般的なプログラマが必ずハマる「地雷」が存在する。アーキテクトの視点からその対策を共有する。

A. COMオブジェクトの参照カウントとメモリリークの根絶

VBAはCOMベースの言語であり、`CreateObject` や `GetObject` で生成したオブジェクトは、変数がスコープを抜けるまでメモリ上に居座り続けるケースがある。
特に `ADODB.Connection` や `RecordSet` をループ内で何千回も生成・破棄するような実装にすると、メモリリークによりMS Projectが突然落ちる(またはCOM例外 0x8001010A – サーバーが忙しすぎます)現象が発生する。
上記のコードでは、ヘルパー関数 (`FetchCostCenterFromAD`) 内で完結させ、関数を抜ける直前に `Set xxx = Nothing` を明示的に呼び出すことで、ヒープ領域の肥大化を完全に防いでいる。

B. レガシーAD環境とスキーマの差異

企業によってActive Directoryの設計はバラバラである。

  • 部署コードを `costCenter` に入れている企業
  • 組織名をそのまま `department` に入れている企業
  • 独自の拡張属性(`extensionAttribute1` など)を使っている企業

コード内のLDAPクエリ (`CommandText`) は、自社のスキーマに合わせて必ずチューニングすること。また、ドメインが複数あるマルチフォレスト環境の場合は、`LDAP://RootDSE` ではなく、グローバルカタログ(`GC://`)をプロバイダに指定する必要がある点に留意せよ。

C. `ScreenUpdating = False` による描画コストの排除

MS Projectは、リソースのプロパティ(`res.Text1 = …`)が書き換わるたびに、内部のUI再描画や再計算(スケジュールエンジンの部分的な走査)を走らせようとする。
数千人のリソースを一括更新する場合、これを放置すると数十分の時間を要する。`App.ScreenUpdating = False` によって描画イベントを完全に凍結し、処理速度を劇的に向上させるテクニックは、エンタープライズVBAにおいては常識である。

—

4. 運用自動化へのステップアップ(RPA・タスクスケジューラ連携)

もし、この同期作業すら人間の手(VBAのマクロボタン押下)で行わせたくないのであれば、VBScriptファイル(`.vbs`)として切り出し、Windowsの「タスクスケジューラ」で毎朝始業時にサイレント実行させるアプローチが最も堅牢である。

MS Projectをバックグラウンド(`Visible = False`)で起動し、ドキュメントをサイレントオープンして同期、保存してクローズする。この設計により、プロジェクトマネージャーが意識することなく、常に最新の組織図・コストセンターがリソースプールに反映された状態を保つことができる。

真のシステム自動化とは、利用者にその存在を意識させないことにある。このアーキテクチャをあなたのプロジェクト管理基盤に組み込み、レガシーな手作業を永遠に過去のものにしてほしい。

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