第二篇 - 擷取資料到指令碼中

作者: Brian Wren,Microsoft Consulting Services (南加州) 的資深顧問

本文是有關在 Microsoft Operations Manager (MOM) 2005 中撰寫指令碼之四篇系列中的第二篇。此系列文章的主題在於比較 MOM 中的指令碼以及專為 Windows Script Host 撰寫的指令碼,以便充分運用現有的 Windows Script Host 相關指令碼的豐富知識及資料。

第 1 篇 - 基本概念

介紹 MOM 編寫指令碼背後的概念,並與在 Windows Script Host (WSH) 中編寫指令碼的概念相比較,除此還說明兩者所使用物件的異同。您將學會如何將指令碼的輸出資料擷取到 MOM 工作流程。

第 2 篇 - 擷取資料到指令碼中

著重於擷取資料到 MOM 指令碼中。其中包括使用參數,以及從啟動指令碼的 MOM 物件擷取資訊。

第 3 篇 – 撰寫和偵錯

深入探討在 MOM 中撰寫及偵錯指令碼的後勤工作。本文涵蓋使用不同的編輯器與公用程式來執行這些功能。

第 4 篇 – 最佳實作方法

討論最佳實作方法以及解答一般問題,例如:何時應該使用 MOM 指令碼來產生警示 (而非產生事件) 呢?MOM 指令碼應該要多複雜才需要分成多個部分呢?安全性方面呢?


本页内容

擷取資料到 MOM 指令碼中
參數 (而非引數)
從規則提供指令碼參數值
從工作提供指令碼參數值
存取 MOM 叫用物件
事件參數
停用使用者帳戶範例
結論


擷取資料到 MOM 指令碼中

使用 Windows Script Host (WSH) 時,您通常是使用 WScript.Arguments 將資訊傳遞到指令碼中。MOM 中的對等功能稱為 ScriptContext.Parameters,可讓您為在呼叫指令碼的規則中已定義的參數提供值。其他擷取資料到 MOM 指令碼的方法並沒有直接對等的 WSH 功能 (我們稍後會證明),這些方法可讓我們從啟始指令碼的事件、警示或效能資料物件中提取資訊。在這總共四篇系列文章裡的第 2 篇中,我們將個別討論這些方法,因為每種方法的情況各有不同,而且許多指令碼都會併用這兩種方法。

參數 (而非引數)

WSH 指令碼接受使用 WScript.Arguments 的引數,而 MOM 指令碼則接受使用 ScriptContext.Parameters 的參數。這兩者幾乎是完全對等,但有些微差異。WScript.Arguments 會直接傳回引數陣列,而 ScriptContext.Parameters 則需要我們呼叫 Get 方法來擷取每個具名參數。在 MOM 指令碼中,我們還需要先在指令碼的 [參數] 索引標籤上定義每個參數,之後指令碼才能使用這些參數,但是 WScript.Arguments 則不同,它會欣然接受您在命令列中輸入的任何東西。

以下是一個簡短的 ScriptContext.Parameters 範例。這段程式碼可接受兩個指令碼參數 (Parameter1 及 Parameter2),並將他們指派給可用於指令碼中的簡單變數。


strParameter1 = ScriptContext.Parameters.Get("Parameter1")
strParameter2 = ScriptContext.Parameters.Get("Parameter2")


您可以將這個範例細分成幾個步驟,首先是建立 Parameters 物件,其次是在該物件上使用 Get 方法。下列程式碼片段的作用與上例完全一樣。


Set objParams = ScriptContext.Parameters
strParameter1 = objParams.Get("Parameter1")
strParameter2 = objParams.Get("Parameter2")


這兩段範例程式碼片段都假設參數已在指令碼的 [參數] 索引標籤中加以定義 (請參閱 [圖 1])。只有已經在該對話方塊中定義的參數,才能用於指令碼。

定義指令碼參數時,您可以為其指派值,此時這個值將成為指派指令碼給規則時的預設值。但您還是可以選擇為任何啟動指令碼的規則或工作設定值。

乍聽之下可能有點令人困惑,但是看一下範例就能瞭解了。下列終止處理序 WSH 指令碼將要終止的處理序名稱 – notepad.exe – 固定編寫在指令碼中。


strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colProcessList = objWMIService.ExecQuery _
    ("Select * from Win32_Process Where Name = 'Notepad.exe'")

For Each objProcess in colProcessList
    objProcess.Terminate()
Next


使用此指令碼時,我們必須針對每個要終止的處理序分別撰寫指令碼,這種做法沒有什麼彈性。如果我們能以引數的形式來提供處理序名稱,就可以重複使用此指令碼。在 MOM 中,透過參數來定義處理序即可定義多個規則,使用相同指令碼但終止不同處理序。我們也可以建立一個呼叫指令碼的工作,並讓運算子在指令碼執行時提供處理序名稱。

修改後的指令碼看起來如下所示。


strProcess = ScriptContext.Parameters.Get("ProcessName")

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colProcessList = objWMIService.ExecQuery _
    ("Select * from Win32_Process Where Name = '" & strProcess & "'")

For Each objProcess in colProcessList
    objProcess.Terminate()
Next


名為 ProcessName 的參數必須在指令碼上定義,而且這個用來指定要終止之處理序名稱的參數值必須在規則或執行指令碼的工作上定義。我們應該不會在指令碼上指定這個參數的預設值,因為定義一個要終止的預設處理序實在沒有道理。

我們再將範例延伸一下,加入一個參數來指定是否要指令碼產生事件。如果打算終止處理序,我們可提供事件來指定是否已確實找到處理序,以及此處理序有多少執行個體被終止。我們要讓系統管理員選擇是否要建立此事件。通常在這種情況下我們會希望建立事件,因此我將預設值設為 True。

[圖 1] 顯示我們在 [指令碼屬性] 對話方塊中定義的兩個參數。

終止處理序

[圖 1] 終止處理序指令碼上的參數。

使用這兩個新參數的指令碼如下所示。我使用 CBool 函式將 GenerateEvent 參數轉換成 True 或 False 的布林值。如同 VBScript 中的變數,MOM 事件參數也屬於 Variant 型別,而且在此例中的 True 值將被解讀為字串。我們想要的是真正的 True 或 False 布林值,所以必須執行這個轉換動作。

為了建立 MOM 事件,我提取了 CreateEvent 副程式 (我們曾在第 1 篇中見過) 以及其相關常數。只有當 GenerateEvent 指令碼參數為 True 時,才會呼叫 CreateEvent 副程式。


Const EVENT_TYPE_SUCCESS = 0
Const EVENT_TYPE_ERROR   = 1
Const EVENT_TYPE_WARNING = 2
Const EVENT_TYPE_INFORMATION = 4
Const EVENT_TYPE_AUDITSUCCESS = 8
Const EVENT_TYPE_AUDITFAILURE = 16

strProcessName = ScriptContext.Parameters.Get("ProcessName")
bolGenerateEvent = CBool(ScriptContext.Parameters.Get("GenerateEvent"))

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colProcessList = objWMIService.ExecQuery _
    ("Select * from Win32_Process Where Name = '" & strProcessName & "'")

For Each objProcess in colProcessList
        objProcess.Terminate()
Next

If bolGenerateEvent = True Then
        CreateEvent 100,EVENT_TYPE_INFORMATION, "Process Monitoring", _
"Terminated " & colProcessList.Count & " instances of process " _
 & strProcessName & "."
End If

Sub CreateEvent(intEventNumber,intEventType,strEventSource,strEventMessage)
        Set objEvent = ScriptContext.CreateEvent()
        objEvent.EventNumber = intEventNumber
        objEvent.EventType = intEventType 
        objEvent.EventSource = strEventSource
        objEvent.Message = strEventMessage
        ScriptContext.Submit objEvent
End Sub


如此一來指令碼就完成了 – 現在我們只要啟動指令碼就可以。無論從規則或工作呼叫,這個特定指定碼都非常好用,所以我們現在來研究一下這兩種情況。

從規則提供指令碼參數值

如果從事件規則呼叫此「終止處理序」指令碼,我們就可以在規則定義中,提供指令碼參數的最後值。為了詳加說明,假設有一個特定處理序,我們希望在它一啟動時便立即終止。為了舉例方便,我們把這個討厭的處理序稱為 root.exe。

若要從規則啟動指令碼,我們需要另一個規則來偵測處理序 root.exe 是否已啟動。我不想偏離主題,不過在 MOM 中最容易判斷處理序啟動與否的方法就是使用 WMI 事件提供者。如果您熟悉這個概念,就會懂我的意思。如果不熟悉,可直接複製 [圖 2] 中提供者內顯示的資訊,並針對啟動「終止處理序」指令碼的事件規則使用該提供者即可。本文的相關 AKM 範例中也包含這個提供者。

處理序啟動

[圖 2] 偵測處理器啟動的 WMI 事件提供者。

[表 1] 提供使用此提供者和啟動「終止處理序」指令碼的事件詳細資訊 (這也包含在 AKM中)。請注意,這個事件並不需要任何準則。WMI 事件提供者已經指定我們要找的處理序,只要發現 root.exe 被啟動,便會立即啟動指令碼。

[表 1] 偵測和終止指定處理序的範例事件規則詳細資訊

索引標籤

屬性

一般 (General)

名稱

已偵測到處理序 root.exe,將予以終止。

資料提供者 (Data Provider)

提供者名稱

處理序 root.exe 已啟動 (WMI 事件提供者)

警示 (Alert)

產生警示

已核取

警示 (Alert)

警示嚴重性

警告

回應 (Response)

啟動指令碼

終止處理序


在規則定義的 [回應] 索引標籤中選取要啟動的指令碼時,[啟動指令碼] 對話方塊中的參數便會使用在指令碼中為每個參數定義的預設值 (若有定義預設值的話)。如需變更預設值 (或在沒有預設值的情況下提供值),請反白顯示適當的參數,並按一下 [編輯參數] 按鈕。我們的測試事件的指令碼參數如 [圖 3] 所示。我把 GenerateEvent 參數設定為其預設值,但把 ProcessName 的參數值設定為 root.exe。

規則上的參數值

[圖 3] 指定規則上的參數值。

若要進行測試,可在裝有 MOM 代理程式的任一電腦上部署此規則。如果您在該電腦上啟動稱為 root.exe 的任何處理序 (只要將 notepad.exe 或其他一些小型應用程式複製到 root.exe 並執行它即可),該處理序應該會在幾秒內自動關閉,並產生 MOM 警示。

從工作提供指令碼參數值

工作會給您兩種機會將值提供給指令碼參數。第一種是在建立工作的時後,這會為工作定義一個預設值,跟指令碼本身裡面的參數定義十分類似。另一種修改值的機會則是每次執行工作的時候。

於工作中使用「終止處理序」指令碼是延續第 1 篇「列出處理序擁有者」範例而自然發展出來的結果。分析特定代理程式電腦時,我們可以從工作執行「列出處理序擁有者」,並識別不應執行的處理序。為了回應該資訊,我們可能想要啟動另一個工作來終止這個不當的處理序。這個機制就像我們在上述範例中,從事件規則呼叫指令碼一樣,只不過本質上比較像臨機操作。

若要依此方式測試「終止處理序」指令碼,請建立啟動指令碼的工作 (沒錯 – 這也在 AKM 範例中)。在代理程式電腦上啟動工作時,會出現「啟動工作精靈」(Launch Task Wizard),裡面 (按下幾次 [下一步] 之後) 會提供該指令碼的參數清單。這時,您可以指定想終止的處理序名稱 (如 [圖 4] 所示)。

專為工作執行

[圖 4] 為工作定義指令碼執行的參數值。

這比從規則執行指令碼的測試更容易。在代理程式電腦上啟動稱為 root.exe 的處理序 (您剛才複製的 Notepad.exe),然後在該電腦啟動此工作。處理序應該會在幾秒內終止,並產生 MOM 警示。

存取 MOM 叫用物件

有時候我們可以在建立規則或工作時提供指令碼資訊,也就是能夠使用指令碼參數的時候。但是世事難料,有時我們需要從叫用指令碼啟動的物件取得資訊。幸好 ScriptContext 提供三個屬性讓我們存取這些物件:Alert、Event 和 PerfData。弔詭的地方在於,一次最多只有一個物件有效,而實際上,可能沒有一個有效。

如果對錯誤物件執行指令碼會發生什麼事呢?例如,您寫了一個指令碼,預期它會由事件起始,因此理所當然使用了 Event 物件。但是在後來的情況裡,還是啟動這個指令碼來回應警示規則比較適合。如果從警示規則呼叫該指令碼,則那個 Event 物件根本不會存在,而您的指令碼就會失敗。

為了識別起始指令碼的 MOM 物件類型,ScriptContext 物件提供 IsAlert、IsEvent 和 IsPerfData 方法。跟名稱的字面意思一樣, 這些方法會根據起始指令碼執行的物件類型,傳回 True 或 False。[圖 5] 舉例說明了此概念。

呼叫物件屬性

[圖 5] 呼叫物件屬性。

因此在我們的範例中,如果您的指令碼設計是要特別針對某種物件類型而執行,則當指令碼啟動錯誤時,您可以採用適當的方法來決定要執行程式碼或建立錯誤事件。另一種情況可能是,您的指令碼可以成功回應事件或警示而執行。此時可以使用 IsEvent 和 IsAlert 方法來確定要執行的程式碼區塊,如下列虛擬程式碼所示。


If ScriptContext.IsEvent = True Then
    Set objEvent = ScriptContext.Event
    <Code using objEvent>
Else If ScriptContext.IsAlert = True Then
    Set objAlert = ScriptContext.Alert
    <Code using objAlert>
Else
    <Create an error event saying that the script isn’t allowed to be
     executed by a performance rule or a task.> 
End If


我們照樣以範例來說明這些概念。我會再次運用第 1 篇中出現過的「效能資料」範例。該指令碼會根據指定檔案的大小,來建立效能資料片段。假設我們希望設定該效能計數器的臨界值,而且在超過特定大小時保存此檔案。

我們可以使用第 1 篇中 [表 5] 定義的臨界值規則來達成此功能。此規則會在檔案大小到達 1 MB 時產生警示。不過我們不要發出警示,而是改用發出指令碼來作為回應。另外再增加一點複雜性,讓此規則針對任何記錄檔引發,而不是單純地固定編寫靜態名稱。[表 2] 顯示新規則的詳細資訊,修改細節以粗體表示。

[表 2] 啟動檔案移動指令碼的效能規則詳細資料。

索引標籤

屬性

一般 (General)

名稱

應用程式記錄檔的大小已超過臨界值。檔案將移至保存。

資料提供者 (Data Provider)

提供者名稱

指令碼產生的資料

準則 (Criteria)

執行個體

符合萬用字元 ‘c:\logs\*’

準則 (Criteria)

物件

等於 ‘File’

準則 (Criteria)

計數器

等於 ‘File Size’

臨界值 (Threshold)

取樣值

已核取

臨界值 (Threshold)

大於下列值

1000000

警示 (Alert)

產生警示

未核取

回應 (Response)

回應

TechNet:Scripting MOM:Move File
(任意指令碼名稱。您可以自行選擇名稱)。

回應 (Response)

類型

指令碼


這樣將對任何以「c:\logs」為路徑開頭的檔案引發規則,說得白話一點,也就是 c:\logs 目錄中的所有檔案。不過您閱讀本文的目的並非處理規則 – 指令碼才是您感興趣的主題。我們可以從 Script Center 的移動檔案指令碼說起。


Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.MoveFile "C:\FSO\ScriptLog.log" , "D:\Archive"


很簡單的一小段指令碼 – 只要您知道要移除之檔案的路徑和檔名以及目的地就沒問題了。在我們的例子中,我們必須等到指令碼實際啟動之後,才會知道檔案名稱。多虧有我們剛才建立的規則,我們可以依賴新的移動指令碼 (馬上就會看到) 針對效能計數器來啟動,而且我們可以使用 ScriptContext.PerfData 屬性來取得該效能計數器。檔案路經剛好在 PerfData 物件的 InstanceName 屬性中,我們可以將該名稱傳遞到 MoveFile 方法。另外,既然我們知道如何使用指令碼參數,就應該透過指令碼參數來提供保存路徑。新的指令碼看起來像這樣:


Set objPerfData = ScriptContext.PerfData
strFilePath = objPerfData.InstanceName
strArchivePath = ScriptContext.Parameters.Get("ArchivePath")

Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.MoveFile strFilePath , strArchivePath


為了更加完備,我們加入一些錯誤檢查。如果不小心從事件規則、警示規則或工作呼叫此指令碼,便會導致失敗並產生如 [圖 6] 所示的錯誤。

錯誤事件

[圖 6] 使用不正確物件而產生的錯誤事件。

當指令碼嘗試存取 ScriptContext.PerfData 卻發現無法使用時,便會建立此錯誤。不過這個錯誤的說明並不太清楚。最好能檢查指令碼是否是為回應效能資料 (Perf Data) 而啟動,如果是錯誤呼叫,最好能提供適當的錯誤訊息。我們可以使用 IsPerfData 方法並建立傳達錯誤的事件,藉此達到這個目的。若要建立事件,我們將貼上第 1 篇中的 CreateEvent 副程式和常數。


Const EVENT_TYPE_SUCCESS = 0
Const EVENT_TYPE_ERROR   = 1
Const EVENT_TYPE_WARNING = 2
Const EVENT_TYPE_INFORMATION = 4
Const EVENT_TYPE_AUDITSUCCESS = 8
Const EVENT_TYPE_AUDITFAILURE = 16

If ScriptContext.IsPerfData = False Then
        CreateEvent 200, EVENT_TYPE_ERROR, "Scripting MOM", "The script " & _
        ScriptContext.Name & " may only be executed in response to a performance rule. "
Else
        Set objPerfData = ScriptContext.PerfData
        strFilePath = objPerfData.InstanceName
        strArchivePath = ScriptContext.Parameters.Get("ArchivePath")

        Set objFSO = CreateObject("Scripting.FileSystemObject")
        objFSO.MoveFile strFilePath , strArchivePath
End If

Sub CreateEvent(intEventNumber,intEventType,strEventSource,strEventMessage)
        Set objEvent = ScriptContext.CreateEvent()
        objEvent.EventNumber = intEventNumber
        objEvent.EventType = intEventType 
        objEvent.EventSource = strEventSource
        objEvent.Message = strEventMessage
        ScriptContext.Submit objEvent
End Sub


從工作或根據事件或警示規則的回應來執行指令碼,您將看到比較妥當的錯誤訊息,如 [圖 7] 所示。

自訂錯誤事件

[圖 7] 使用不正確物件而產生的自訂錯誤事件。

執行指令碼以回應效能規則 (正如 AKM 範例所提供的例子),當檔案超過我們指定的臨界值時,它應該會移至保存位置。

事件參數

在 MOM 中,最常引發指令碼的方法可能就是事件規則。現在我們知道可以使用 ScriptContext.Event 來存取指令碼的事件資訊,但是我們所需的資訊往往埋藏在事件訊息中。我們可以使用各種 Mid、Left、Right 及 InStr 函式來取得資料片段,但我們不必這麼做。

事件通常有隨附的參數。請不要將事件參數與本文第一節所述的指令碼參數搞混。指令碼參數與其值是由系統管理員在建立啟動指令碼的規則時所定義的,而事件參數則是 MOM 產生的某些事件所附帶的個別資料片段。

Windows 中的事件一般是由靜態文字和眾多參數所組成,這些參數會包含每個發生事件的特定資訊。例如,「安全性事件記錄檔」中的事件通常包含起始事件建立的使用者及使用者帳戶網域名稱等參數。這些就是 MOM 指令碼可以存取的事件參數。事件訊息通常包含參數值,但是這些參數值往往混雜在說明文字中。單獨存取參數會簡單的多。

停用使用者帳戶範例

曾經有位客戶要求我讓 MOM 監視使用者清除網域控制站上的安全性事件記錄檔的動作。如果發生這種動作,便會停用違規使用者的使用者帳戶。他的想法是這種行為可能會危及系統管理員帳戶,而犯罪者會藉由清除安全記錄檔來掩蓋其行徑。結果我發現這是用來說明事件參數使用的最好例子,而且我將藉此機會介紹一些使用 Active Directory Services Interface (ADSI) 的成果。

我會很快提出一些使用指令碼與 Active Directory 的相關概念。也許您還不熟悉這些概念,這樣的話,您可以參考《Microsoft Windows 2000 Scripting Guide》(英文) 中有關 ADSI 的詳盡討論。我們的事件參數範例使用 ADSI 很合情合理,因為我們常使用事件參數來擷取使用者帳戶資料,而且您會經常遇到我們即將討論的問題。即使對 ADSI 沒什麼興趣,您還是可以從範例中清楚瞭解如何使用事件參數。

另一件要提醒的事情是,本範例要求使用網域控制站上的 Action Account (執行帳戶),才能授權停用 Active Directory 中的使用者帳戶。若您不熟悉 Action Account 的概念,請參閱《MOM Security Guide》(英文)。

現在就來進行我們的解決方案。藉由監視安全性事件 517,我們可以偵測使用者清除安全性事件記錄檔的動作。若要查看此事件,請建立收集規則 (AKM 範例中提供此範例) 然後直接清除記錄檔。所得的事件會看起來像 [圖 8] 中所示。

安全性事件範例

[圖 8] 安全性事件範例。

您可以看到,在事件說明中有清除記錄檔的使用者名稱,旁邊是「Client User Name:」(用戶端使用者名稱:) 這段文字,而底下正好是網域名稱。我可以嘗試使用 InStr 和 Mid 函式與 Event 物件的 Message 屬性來擷取這些值,但是另有更簡單的作法。按一下相同事件的 [事件] 索引標籤,您將看到 [圖 9] 的畫面。

事件參數範例

[圖 9] 事件參數範例。

這些是事件參數,您可以看到參數 4 和 5 旁邊正好是我們所需的使用者名稱與網域。比起在訊息中挖掘,直接抓取這些值要輕鬆多了。

而您要如何知道事件可以包含哪些參數,還有哪個值要放哪個位置呢?您可以參閱產生事件之應用程式的說明文件,或者直接為所要的事件建立事件收集規則 – 像我們剛才那樣。然後從收集的事件中檢查所需資訊。請記得在規則中要選取收集所有參數這個選項,否則將不會收集任何參數 – 而只有事件本身。

這類的事件參數在安全性稽核事件中很常用。我們常遇到挑戰是,為了使用 ADSI 取得 Active Directory 物件,我們需要物件的辨別名稱,其中包括整個 OU 及網域路徑。稽核事件一般只會提供帳戶名稱和網域名稱。基於這點,我們的指令碼將包含兩個步驟 – 首先是尋找要停用的使用者物件,第二步是停用該使用者。

第一個工作可以利用搜尋 Active Directory 中的使用者帳戶指令碼來完成。這段指令碼可接受使用者帳戶名稱,然後報告是否在目錄中找到該使用者。


strUserName = "kenmyer"
dtStart = TimeValue(Now())
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
 
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
 
objCommand.CommandText = _
    "<LDAP://dc=fabrikam,dc=com>;(&(objectCategory=User)" & _
         "(samAccountName=" & strUserName & "));samAccountName;subtree"
  
Set objRecordSet = objCommand.Execute
 
If objRecordset.RecordCount = 0 Then
    WScript.Echo "sAMAccountName: " & strUserName & " does not exist."
Else
    WScript.Echo strUserName & " exists."
End If
 
objConnection.Close


我們需要修改此指令碼,以便使用自己的事件參數來擷取所要的使用者帳戶及網域名稱。就本範例而言,所需資訊就在事件的參數 4 和 5 中 (查看 [圖 9] 就知道)。另外,除了只有報告使用者是否存在 (這需要使用 WScript.Echo,我們必須移除此命令以便執行 MOM 中的指令碼),我們還需要將使用者的辨別名稱放入變數中,以便在第二個步驟中使用。

指定使用者帳戶之後,我們需要修改指定搜尋準則那一行。很明顯地,我們得要變更網域名稱,另外還得指定我們希望查詢傳回 distinguishedName 而非 samAccountName。最後,我們需要加入一行程式碼,以便擷取查詢結果集中的 distinguishedName,並將之指派到變數。

修改後的指令碼版本如下所示:


Set objEvent = ScriptContext.Event
strUserName = objEvent.EventParameter(4)
dtStart = TimeValue(Now())
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
 
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
 
objCommand.CommandText = _
    "<LDAP://" & objEvent.EventParameter(5) & ">;(&(objectCategory=User)" & _
         "(samAccountName=" & strUserName & "));distinguishedName;subtree"
  
Set objRecordSet = objCommand.Execute
 
strDistinguishedName = objRecordset("distinguishedName")
 
objConnection.Close


既然我們已取得使用者的辨別名稱,我們可以使用停用使用者帳戶指令碼來停用帳戶,如下所示。


Const ADS_UF_ACCOUNTDISABLE = 2
 
Set objUser = GetObject _
    ("LDAP://cn=myerken,ou=management,dc=fabrikam,dc=com")
intUAC = objUser.Get("userAccountControl")
 
objUser.Put "userAccountControl", intUAC OR ADS_UF_ACCOUNTDISABLE
objUser.SetInfo


這比修改使用者搜尋來得簡單許多。修改過的指令碼會直接使用我們從上一段指令碼取得的變數,取代固定編寫的辨別名稱。修改過的版本如下:


Const ADS_UF_ACCOUNTDISABLE = 2
 
Set objUser = GetObject _
    ("LDAP:// " & strDistinguishedName)
intUAC = objUser.Get("userAccountControl")
 
objUser.Put "userAccountControl", intUAC OR ADS_UF_ACCOUNTDISABLE
objUser.SetInfo


最後一段指令碼就是修改過的「搜尋 Active Directory 中的使用者帳戶」指令碼,其後緊接著修改過的「停用使用者帳戶」指令碼。


Set objEvent = ScriptContext.Event
strUserName = objEvent.EventParameter(4)
dtStart = TimeValue(Now())
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open "Provider=ADsDSOObject;"
 
Set objCommand = CreateObject("ADODB.Command")
objCommand.ActiveConnection = objConnection
 
objCommand.CommandText = _
    "<LDAP://" & objEvent.EventParameter(5) & ">;(&(objectCategory=User)" & _
         "(samAccountName=" & strUserName & "));distinguishedName;subtree"
  
Set objRecordSet = objCommand.Execute
 
strDistinguishedName = objRecordset.Fields ("distinguishedName")
 
objConnection.Close

Const ADS_UF_ACCOUNTDISABLE = 2
 
Set objUser = GetObject _
    ("LDAP:// " & strDistinguishedName)
intUAC = objUser.Get("userAccountControl")
 
objUser.Put "userAccountControl", intUAC OR ADS_UF_ACCOUNTDISABLE
objUser.SetInfo


重點:在我告訴您如何測試此指令碼之前,請特別注意一點,就是您可能會鎖定自己的帳戶而無法使用網路。請建立測試使用者帳戶然後停用它,或是把 MMC 所在的 Active Directory 使用者與電腦保持開放狀態,以便能立即重新啟用自己的帳戶。


您可以建立類似 [表 3] 的規則,然後將之部署到一或多個網域控制台上,藉以測試此指令碼。只要清除安全性事件記錄檔,您就會發現您的使用者帳戶已被停用。

[表 3] - 偵測停用清除安全性事件記錄檔之使用者帳戶的事件規則詳細資料

索引標籤

屬性

一般 (General)

名稱

安全性事件記錄檔已清除。將停用使用者帳戶。

資料提供者 (Data Provider)

提供者名稱

安全性

準則 (Criteria)

從來源

安全性

準則 (Criteria)

含事件 ID

517

警示 (Alert)

產生警示

已核取

警示 (Alert)

警示嚴重性

安全性問題

回應 (Response)

回應

TechNet:Scripting MOM:Disable a User Account

回應 (Response)

類型

指令碼


為了讓這個解決方案更完善,您可以在帳戶停用後登出使用者。若等到惡意使用者已經登入而且肆意搗亂時才停用使用者帳戶,就沒有什麼用了。不過我們已經釐清主要概念,因此我不打算繼續深究下去。這也會讓指令碼的測試更危險。如果您有興趣,您可以在《Microsoft Windows 2000 Scripting Guide》(英文) 中找到關閉電腦或直接登出目前使用者的指令碼。您只需要將該指令碼加到我們剛才建立的指令碼最後面即可。

最後恕我再澄清一點。經驗老道的惡意使用者可能會在執行清除安全性事件記錄檔等動作之前,先停用 MOM 服務。如此一來,我們的指令碼就永遠不會引發。因為這個方法本來就不是完備的安全性強制範例,而只是作為整體安全性計劃的執行與稽核的一環。但是如果 MOM 服務被停用,我們會收到 MOM 中活動訊號失敗的警示,而且應該會發動調查。

結論

我們現在知道如何以動態方式提供資料,藉此提高 MOM 指令碼的靈活性及回應性。我們可以透過呼叫規則或工具上定義的值來提供這些資料,或者讓指令碼挑選呼叫物件的項目。結合本系列第 1 篇的資訊,您現在應該可以在 MOM 中撰寫出令人讚賞的指令碼。

在第 3 篇中,我們將往回退一步,從指令碼本身回頭探討撰寫與偵測 MOM 指令碼的程序。其中包括撰寫程式碼的技巧,以及各種測試和移難排解的方法。

相关链接

顯示: