发布者 Brian Wren, Senior Consultant, Microsoft Consulting Services(南加利福尼亚)
介绍如何在 Microsoft Operations Manager (MOM) 中编写脚本的丛书由四部分组成,本文是第二部分。本系列丛书的主旨是比较 MOM 脚本与为 Windows Script Host 编写的脚本,以便利用大量已经集中到 Windows Script Host 脚本中的知识和材料。
|
第一部分 - 基础
|
介绍与 Windows Script Host 中的脚本相比,MOM 脚本所隐藏的概念,以及两者使用的共同和不同对象。您将学会如何将脚本输出数据引入 MOM 工作流。
|
|
第二部分 - 将数据引入脚本
|
重点讨论将数据引入 MOM 脚本。这一部分包括使用参数和从启动脚本的 MOM 对象检索信息。
|
|
第三部分 - 编写与调试
|
洞察 MOM 脚本编写与调试的内部事务处理。本文讲述如何使用不同编辑器和实用程序完成上述功能。
|
|
第四部分 - 最佳方法
|
讨论最佳方法,回答常见问题,例如:您应何时用 MOM 脚本生成警报而非事件?在将 MOM 脚本分成多个部分前,它有多复杂?安全性如何?
|
本页内容
将数据引入 MOM 脚本
参数(而非自变量)
从规则提供脚本参数值
从任务提供脚本参数值
访问调用 MOM 的对象
事件参数
禁用用户帐户示例
小结
将数据引入 MOM 脚本
对于 Windows Script Host (WSH),通常使用 WScript.Arguments 将信息传递到脚本中。MOM 中的功能性等效方法为 ScriptContext.Parameters,可为在调用脚本的规则中定义的参数提供值。将数据引入 MOM 脚本的其他方法(这些方法没有直接的 WSH 对应,而且将用位进行标识)允许从事件、警报或启动脚本的性能数据对象提取信息。在这一部分(由四个部分组成的系列丛书的第二部分)中,由于这些方法中的每一种方法都有不同的情形,而且许多脚本将使用它们的组合,因此将分别讨论每一种方法。
参数(而非自变量)
WSH 脚本使用 WScript.Arguments 接收自变量,而 MOM 脚本使用 ScriptContext.Parameters 接收参数。这两者几乎直接对应,但是存在一些细微差别。WScript.Arguments 只返回自变量数组,而 ScriptContext.Parameters 需要调用 Get 方法才可检索每个命名的参数。在 MOM 中,还需要在脚本的 Parameters(参数)选项卡上定义每个参数,然后脚本才能使用这些参数,而 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")
这两段示例代码都假设参数在脚本的 Parameters(参数)选项卡上定义(参见图 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 显示在 Script Properties(脚本属性)对话框中定义的两个参数。
图 1. 终止进程脚本上的参数
下面显示的是使用两个新参数的新脚本。我使用了 CBool 函数将 GenerateEvent 参数转换为 True 或 False 的布尔值。与 VBScript 中的变量相似,MOM 事件参数的类型可变,本例中 True 值将被视为字符串。我们需要真正的 True 或 False 布尔值,因此需要执行此转换。
为了创建 MOM 事件,我将引入 CreateEvent 子例程(见第一部分)及其相关常量。仅当 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(常规)
|
Name(名称)
|
Process root.exe was detected and will be terminated.(已检测到并将终止进程 root.exe。)
|
|
Data Provider(数据提供程序)
|
Provider Name(提供程序名称)
|
Process root.exe started (WMI Event Provider) [进程 root.exe 已启动(WMI 事件提供程序)]
|
|
Alert(警报)
|
Generate Alert(生成警报)
|
Checked(已选中)
|
|
Alert(警报)
|
Alert Severity(警报严重度)
|
Warning(警告)
|
|
Responses(响应)
|
Launch Script(启动脚本)
|
Terminate a process(终止进程)
|
在规则定义的 Responses(响应)选项卡中选择要启动的脚本时,Launch a Script(启动脚本)(如果定义了默认值)。如果要更改默认值(或者在没有默认值时提供值),则在突出显示适当参数的情况下单击 Edit Parameter(编辑参数)按钮。测试事件的脚本参数显示在图 3 中。保留了 GnerateEvent 的默认值,但为 rocessName 提供了值 root.exe。
图 3. 指定规则上的参数值。
如果要测试此项,请将规则部署到具有 MOM 代理的任何计算机。如果启动该计算机上名为 root.exe 的任何进程(只需将 notepad.exe 或其他一些小应用程序复制到 root.exe 并运行),该进程应在几秒钟内自动关闭,然后生成一个 MOM 警报。
从任务提供脚本参数值
您有两次机会从任务为脚本参数提供值。第一次是在创建任务时。与在脚本本身定义参数很相似,这种方法为任务定义一个默认值。另一个机会是每次执行任务时修改该值。
在任务中使用“终止进程”脚本是第一部分中的“列出进程所有者”示例的自然而然的发展历程。分析特定的代理计算机时,可从任务执行“列出进程所有者”并且识别不应运行的进程。响应该信息时,可能要启动另一任务以终止有问题的进程。这与在上一个从事件规则调用脚本的示例中实现的功能相似,但是更特殊一些。
要以这种方式验证“终止进程”脚本,需创建一个启动该脚本的任务(它也包含在示例 AKM 中)。从代理计算机启动任务时,将显示 Launch Task Wizard(启动任务向导),该向导在经过若干次单击 Next(下一步)后,将显示脚本的参数列表。那时,可以指定要终止的进程的名称(如图 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
我们同样是通过示例说明这些概念。我将利用在第一部分中使用的“性能数据”示例。该脚本创建大小为指定文件大小的性能数据块。假设要为该性能计数器设置阈值,而且在文件超过一定大小时将它移动到档案中。
可以使用在第一部分中的表 5 中定义的阈值规则实现此功能。该规则将在文件大小达到 1 MB 时生成警报。我们可以将该规则改为在响应时启动脚本,而非发出警报。还可以采取稍微复杂一些的做法,根据任何日志文件而非硬编码静态名称来引发此规则。新规则的详细信息显示在表 2 中,其中修改的详细信息以粗体显示。
表 2
。启动“文件移动”脚本的“性能规则”的详细信息。
|
选项卡
|
属性
|
值
|
|
General(常规)
|
Name(名称)
|
The size of an application log has exceeded its threshold value.File will be moved to archive.(应用程序日志的大小已超过其阈值。文件将被移动到档案中。)
|
|
Data Provider(数据提供程序)
|
Provider Name(提供程序名称)
|
Script-generated data(脚本生成的数据)
|
|
Criteria(条件)
|
Instance(实例)
|
Matches wildcard ‘c:\logs\*’(匹配通配符 ‘c:\logs\*’)
|
|
Criteria(条件)
|
Object(对象)
|
equals ‘File’(等于 ‘File’)
|
|
Criteria(条件)
|
Counter(计数器)
|
equals ‘File Size’(等于 ‘File Size’)
|
|
Threshold(阈值)
|
the sampled value(采样值)
|
Checked(已选中)
|
|
Threshold(阈值)
|
Greater than the following value(大于以下值)
|
1000000
|
|
Alert(警报)
|
Generate alert(生成警报)
|
Unchecked(取消选中)
|
|
Responses(响应)
|
Responses(响应)
|
TechNet:Scripting MOM:移动文件
(任意脚本名称。可以自由选择要使用的名称。)
|
|
Responses(响应)
|
Type(类型)
|
Script(脚本)
|
这将导致为路径以“c:\logs”开始的任何文件(即 c:\logs 目录中的任何文件)引发规则。不过,您阅读此文章并不是因为对处理规则感兴趣,而是要阅读脚本。我们可以从“脚本中心”的移动文件(英文)脚本开始。
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,但发现它不可用时,将产生此错误。但此错误不是很具有说明性。最好检查是否启动了可响应性能数据的脚本,并检查在脚本被错误调用时是否提供相应的错误消息。这可通过使用 IsPerfData 方法并创建传送错误的事件实现。要创建该事件,可粘贴第一部分中的 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 服务接口 (ADSI) 的内容。
同时还将提出一些有关与 Active Directory 共用的脚本的概念。您可能不熟悉这些概念,可以参考 Microsoft Windows 2000 Scripting Guide(英文),了解有关 ADSI 的完整讨论。但是,ADSI 对我们的事件参数示例有意义,因为我们通常使用事件参数检索用户帐户数据,而且您会经常遇到我们正打算着手解决的问题。如果您对 ADSI 根本不感兴趣,仍应能够从示例中清楚了解到如何使用事件参数。
另一件要注意的事情是本例将要求域控制器上的“操作帐户”有权禁用 Active Directory 中的用户帐户。如果不熟悉“操作帐户”的概念,请查阅 MOM 安全指南(英文)。
现在,让我们言归正传,讨论解决方案。通过监视安全事件 517 可以检测到正在清除“安全事件日志”的用户。要查看此事件的大致情形,可创建一个收集规则(示例 AKM 中提供相应示例),然后继续并清除日志。生成的事件如图 8 所示。
图 8. 安全事件示例。
可以看到清除日志的用户的名称位于文本“Client User Name:”(客户端用户名:)旁的事件说明中,其正下方为域名。可以尝试使用带有 Event 对象的 Message 属性的 InStr 和 Mid 函数生成命令,以获取这些值,不过,还有更简单的方法。如果单击同一事件的 Events(事件)选项卡,将显示图 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
|
重要信息:在了解如何测试此脚本之前,请小心,因为您可能将自己锁定在网络之外。要么创建一个测试用户帐户用于禁用,要么令 Active Directory 用户与计算机 MMC 一直打开,以便可以立即重新启用自己。
|
可以通过创建类似表 3 的规则并将它部署到一个或多个域控制器中,以验证此脚本。只需清除“安全事件”日志,就会发现用户帐户被禁用。
表 3
- 检测禁用用户帐户以便响应清除安全事件日志的事件规则的详细信息
|
选项卡
|
属性
|
值
|
|
General(常规)
|
Name(名称)
|
Security Event Log was cleared.User account will be disabled.(安全事件日志已清除。用户帐户将被禁用。)
|
|
Data Provider(数据提供程序)
|
Provider Name(提供程序名称)
|
Security(安全)
|
|
Criteria(条件)
|
来自源
|
安全
|
|
条件
|
With event ID(具有事件 ID)
|
517
|
|
Alert(警报)
|
Generate alert(生成警报)
|
Checked(已选中)
|
|
Alert(警报)
|
Alert severity(警报严重度)
|
Security Issue(安全问题)
|
|
Responses(响应)
|
Response(响应)
|
TechNet:禁用用户帐户
|
|
Responses(响应)
|
Type(类型)
|
Script(脚本)
|
为使此解决方案更加完善,禁用帐户后立即注销用户会很有帮助。如果恶意用户已经登录,而且在他选择注销之前可以随心所欲地进行破坏,则禁用用户帐户没有太大意义。但是,我不打算包括这一问题,因为从说明主要概念的角度来讲,我们已经做得足够。而且那样也会使测试脚本更具危险性。如果您感兴趣,可以在 Microsoft Windows 2000 Scripting Guide(英文)中找到可关闭计算机或者只注销当前用户的脚本。只需要将该脚本添加到刚刚创建的脚本的后部。
如果可以,我还要进行最后一项声明。狡猾的恶意用户可能会在执行类似清除“安全事件”日志的操作之前禁用 MOM 服务。这种情况下,将永远不能引发脚本。这个示例不是要设计成坚如磐石的安全增强示例,而是作为整体安全计划的组成部分的若干增强和审核示例。但是,如果禁用了 MOM 服务,将在 MOM 中收到失败的警告,可能会开始进行调查。
小结
现在,我们知道了如何通过为 MOM 脚本动态提供数据而使其用途更多、响应更快。可以通过在调用规则或任务时定义的值提供该数据,也可以使脚本提取调用对象的元素。结合与本系列丛书的第一部分中的信息,现在您应能够在 MOM 中编写一些引人注目的脚本。
在第三部分中,我们将回过头来从脚本本身开始,讨论编写和调试 MOM 脚本的过程。这一部分将包括编写代码的技巧以及进行测试和故障排除的不同方法。
相关链接