Part 1 - The Basics

By Brian Wren, Senior Consultant, Microsoft Consulting Services (Southern California)

This article is the first of a four-part series on writing scripts in Microsoft Operations Manager (MOM) 2005. The purpose of the series is to compare scripts in MOM to those written for Windows Script Host (WSH) so administrators can leverage the large amount of knowledge and materials already invested on scripts for Windows Script Host. While background information and references to supporting documentation will be provided as appropriate, the series does assume a working knowledge of MOM 2005 and basic script-writing using Windows Script Host.

Here is a brief synopsis of each of the articles in this four-part series:

Part 1 - The Basics

Introduces the concepts behind MOM scripts compared to those in Windows Script Host and the common and different objects used by each. You will learn how to get output data from a script into the MOM workflow.

Part 2 - Getting Data into a Script

Focuses on getting data into a MOM script. This includes working with parameters and retrieving information from the MOM object that launches the script.

Part 3 - Writing and Debugging

Insight into the logistics of writing and debugging a script in MOM. This article covers the use of different editors and utilities for performing these functions.

Part 4 - Best Practices

Discussion of best practices and answers to general questions, such as: When should you generate an alert as opposed to an event with a MOM script? How complex should a MOM script be before you break it into multiple parts? What about security?

On This Page

Where Are All the MOM Scripts?
Windows Script Host Versus MOM Scripts
Meet ScriptContext
Replacing WScript.Echo
Creating Events
Creating Performance Data
ScriptContext.Echo
Task Output
Conclusion

Where Are All the MOM Scripts?

So you’re working with MOM and you’ve found that, among other things, it’s a great tool for executing scripts on remote computers. MOM actually stores the scripts for you, delivers them to remote computers, launches them locally on the target computer, and sends back results. You can launch scripts on a regular schedule, in response to some event, or on demand through a Task. This is great!Once you get over your initial excitement, you start looking around for some sample scripts to plug into MOM. Of course, you’re going to start at the single best repository of Windows scripts…the Script Center.

Wait a minute though….there aren’t any scripts there that refer to MOM. There are some great sample scripts and utilities but absolutely nothing saying they’re for MOM. What gives? Did they forget about MOM? Is scripting in MOM so difficult that we have to leave it to professional management pack authors? Why doesn’t Script Center have any MOM scripts?

The answer is actually pretty straightforward: all of the scripts in the Script Center can be MOM scripts (well, most of them anyway). Whether they are all relevant in MOM is a different question. I doubt that you’re interested, for example, in launching a script to create an Active Directory site in an automated fashion from MOM, but you could! I’ll leave it to you to figure out what you’re interested in launching from MOM. What’s important is that the entire set of scripts in Script Center can be leveraged for execution in MOM.

Wait though! Before you start copying and pasting away, you need to know that it isn’t quite as simple as dumping a script directly into MOM and then launching it through a scheduled event rule. No - I didn’t lie to you, they are all useable in MOM. You just need to make some slight modifications first. Thus, the point of this article.

For the purpose of the article, I’m going to assume that you know how to write a script in WSH. If you don’t then you should start with the basics before tackling a MOM script. The Script Center is a great place to start, and virtually all of the training, documentation, and samples for writing Windows scripts focus on the Windows Script Host. This series will tell you how to modify a WSH script to work in MOM.

Windows Script Host Versus MOM Scripts

When you learn how to write a script in Windows, you invariably start with Windows Script Host. This makes sense since WSH is very flexible, allowing you to execute a script on a command line using a number of different useful technologies such as Active Directory Service Interfaces (ADSI) and Windows Management Instrumentation (WMI). What happens though is that we might start to assume that WSH is synonymous with scripting and that it’s the only scripting environment in town.

What exactly is the relationship between WSH and a MOM script? Is MOM calling WSH to run scripts for it? Does MOM duplicate the entire WSH environment in order to perform similar functions? How common are the scripts between the two? Do we get to use the same languages? We would assume VBScript and JScript will work, but what about other languages like PerlScript? And then the ultimate question…do I have to change a WSH script to work in MOM and, if so, what changes do I have to make?Okay, enough questions - let’s see if we can provide some answers.

WSH is built on a modular architecture leveraging scripting components contained in Windows. It can get confusing what the term “Windows Script Host” refers to since there are several components that participate in the execution of a script. All these components sometimes fall under the WSH umbrella. It’s not going to be valuable to exhaustively identify each component, but we will instead just refer in this article to the actual executables - wscript.exe and cscript.exe - as WSH. The rest of the components are shared by other scripting environments. This is illustrated (a bit simplistically) in Figure 1.

Scripting Environments

Figure 1. Scripting environments.

Both WSH and MOM are scripting environments. Both work against the same scripting services in Windows. Both can run scripts written in VBScript, JScript, or any other scripting language you have loaded on the computer executing the script. It’s no coincidence that WSH and MOM work so similarly - they are working against identical objects, and those objects are doing most of the work. As a result, they can both run almost identical scripts without modification. Almost.

Did it ever occur as odd to you that you don’t have to create a WScript object before using it? Before you use an object in a script, you have to create it using a CreateObject or a GetObject, but WScript seems to just be there. Consider the following simple script:

WScript.Echo "Hello world."

Useless as that script is, it will work if you launch it in Windows Script Host. There isn’t a single CreateObject to be found, yet we are able to use the WScript object. This is because WSH automatically provides the WScript object to us to represent the script host itself and to provide some core functionality specific to WSH scripts. Other scripting environments won’t have access to the WScript object, but they will probably provide something similar.

Look carefully at Figure 2. This figure illustrates that MOM and WSH scripts both have access to a plethora of common objects. (Note that the list of objects in this figure is by no means all inclusive. It is only intended to identify some of the more commonly used scriptable objects.) These include ADSI, WMI, and any other scriptable object you’re used to using. The difference is that a MOM script can’t use the WScript object. Similarly, WSH can’t use the ScriptContext object which is available only in the MOM environment (more on that later).

Objects in MOM and WSH

Figure 2. Objects available to MOM and WSH.

One thing to note though, is that while you can’t use WScript itself in MOM, you can use objects created from WScript. Any object that you create in a script using the CreateObject or GetObject commands is still going to work. This includes WScript.Shell and WScript.Network. These objects are considered a logical part of the script host, but they are separate from the WScript object itself and can be used in any other Windows scripting environment, like MOM.

This may seem a little confusing, but let’s net it down to a basic rule: If an object can be used in a script without first creating it with CreateObject or GetObject, then it will be available to only a single scripting environment. If the object is created with CreateObject or GetObject, then it will be available to any scripting environment that leverages Windows script technologies.

So technically, the only change you are probably going to have to make to any WSH script is to remove any use of the WScript object. That’s right - all the WScript.Echo commands that are so loved by Script Center can’t be used. You also won’t be receiving arguments with WScript.Arguments, and you certainly won’t be returning the name of the script with WScript.ScriptName.

Meet ScriptContext

Enough of what we can’t do. Our old friend WScript isn’t going to do anything for us in MOM, so let’s just let it go. We’re going to make a new friend to take its place: ScriptContext. You probably won’t write a single MOM script that doesn’t have ScriptContext in it, so it’s pretty important that you understand what it provides.

It may help to think of ScriptContext as the functional equivalent of WScript, and it has some common methods that might help you feel warm and fuzzy. It has the equivalent primary function of WScript - providing access to the MOM scripting environment itself and providing functions that are unique to MOM. Rather than being executed by an interactive user staring at a screen though, MOM scripts are typically executed in some automated fashion (scheduled basis, in response to an event, etc.) on a remote computer somewhere - potentially several computers at one time. A MOM script can’t simply send its output back to the screen but must somehow report it through the MOM workflow engine. ScriptContext gives us methods to do just that.

Table 1 provides a comparison of some of the more common ScriptContext and WScript methods and properties. This is not a complete list, but you can find the full documentation on ScriptContext in the MOM 2005 SDK. I chose this list primarily to illustrate the differences and similarities between the two objects.

Table 1. Comparison of ScriptContext and WScript Methods and Properties .

ScriptContext

WScript

Description

Event

-

Returns an object representing the Event that triggered the script execution.

Alert

-

Returns an object representing the Alert that triggered the script execution.

PerfData

-

Returns an object representing the PerfData that triggered the script execution.

IsEvent

-

Returns True if the script was executed in response to an event.

IsAlert

-

Returns True if the script was executed in response to an alert.

IsPerfData

-

Returns True if the script was executed in response to PerfData.

CreateEvent

-

Creates a new Event object.

CreateAlert

-

Creates a new Alert object.

CreatePerfData

-

Creates a new PerfData object.

Submit

-

Submits an object to the MOM data stream.

Echo

Echo

WScript: Sends output to the screenMOM: Sends text output to debug file or to Task event.

Name

ScriptName

Returns the name of the currently running script.

Parameters

Arguments

Provides access to script arguments (WScript) or parameters (MOM).

Quit

Quit

Immediately stops execution of the script.

Sleep

Sleep

Suspends script execution for the specified number of seconds.

-

FullName

Returns the path to the host executable.

-

Name

Returns the name of the WScript object.

-

ScriptFullName

Returns the full path to the currently running script.

-

StdIn

Exposes the input stream for the current script.

-

StdOut

Exposes the output stream for the current script.

-

StdErr

Exposes the error output stream for the current script.

-

Version

Returns the version of the Windows Script Host.

The important point to take from this table is the properties and methods unique to each object compared to those with direct equivalents. Note that the WScript properties with no direct equivalent in ScriptContext really don’t have relevance in MOM anyway, and any reference to them can usually be removed. Sleep and Quit are direct equivalents, and you can simply replace one with the other. Accessing parameters with ScriptContext.Parameters will be covered in Part 2 of this series. That leaves Echo…

Replacing WScript.Echo

The most common use of the WScript object is WScript.Echo, which is used to return information to the screen, and it is used by almost all scripts in Script Center.

Yes, we do have a ScriptContext.Echo in MOM, but it’s primarily intended for debugging and is not the most common way to get data back from a MOM script. I’m going to hold off and cover ScriptContext.Echo later in the article. First, I want to cover the most common method for replacing those WScript.Echo statements.

Keep in mind that MOM scripts are usually run in response to some event rule without anyone watching. Even if there were a command line to send output to, no one would be looking at it. Instead, we need to have our script create either an event or a piece of performance data. Let’s look at each of these options separately.

Creating Events

MOM events are created using the ScriptContext.CreateEvent method. You just need to set a few core properties on the Event object and then submit it to MOM. Any properties you don’t set will take a default value. While this requires just a few lines, it is a little more complicated than a simple WScript.Echo. To make your life easier, I’m going to give you a subroutine to create an event. If you’re not familiar with subroutines, you can get a quick rundown in the Windows 2000 Scripting Guide. We’ll be passing values into the subroutine which makes it a little more complex, but this is also documented in the guide.

The reason that I am using a subroutine is so that you can simply paste it into any script that needs an event created and then issue a single command for each event. You don’t even necessarily need to know how the subroutine is working to use it effectively.

Here you go….glad to be of service. Don’t think I’m all that profound though - you can find a similar routine in several MOM scripts from management packs you probably already have loaded.

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

The first line of the subroutine creates a new Event object and assigns it to an object variable. We then assign values to different properties of the Event object and then finally submit it to the MOM workflow with ScriptContext.Submit.

There are other properties of the Event object that you may want to set, but I included the most common. These should be sufficient for most scripts you write. I’ll leave it to you to check the MOM SDK for others. Simply set their values before the ScriptContext.Submit command. Table 2 describes each of the properties used in the subroutine.

Table 2. Common Event Properties.

Property

Description

EventNumber

Arbitrary number assigned to the event. Primarily used for criteria in event rules.

EventType

Integer value for the event type. Must be one of the following values:
0 - success
1 - error
2 - warning
4 - information
8 - audit success
16 - audit failure

EventSource

Source of the event. Typically consistent for similar events. Used for criteria on event rules and for grouping of common events.

Message

The detailed message that will appear in the event description field.

To call the CreateEvent subroutine, use the following syntax in your script - this will be the line that will replace WScript.Echo.

CreateEvent <EventNumber>,<EventType>,<EventSource>,<Message>

Let’s start with a simple example and change our Hello World script to send an event. The script itself is useless, but it does illustrate a concept for us. In addition to the subroutine, I added some constants for the different event type values. Feel free to copy these for your purposes as well.

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

CreateEvent 100,EVENT_TYPE_INFORMATION,"Script Test","Hello world."

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

Whew! We just inflated a single-line script into 15 lines of code. Keep in mind though that the constant definitions and CreateEvent subroutine can simply be copied into the script. We still only wrote a single valid line of code. That’s actually not too bad.

The function of this script is to issue an Information event with an event number of 100, a source of “Script Test”, and a single line of text in the message - “Hello world.”

Now that we have the subroutine, let’s try it out in a real script that does something useful. We’ll start with the following script, Verify Computer Availability, which uses WMI to perform a ping against a network device to verify its availability. This script might be used to validate the presence of a computer or other device not monitored by MOM - maybe the default gateway of a computer or a critical router.

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colPingedComputers = objWMIService.ExecQuery _
    ("Select * from Win32_PingStatus Where Address = '192.168.1.37'")

For Each objComputer in colPingedComputers
    If objComputer.StatusCode = 0 Then
        Wscript.Echo "Remote computer responded."
    Else
        Wscript.Echo "Remote computer did not respond."
   End If
Next

Scanning this script, we see two WScript.Echo lines. We should be able to make this script work in MOM by pasting in our CreateEvent subroutine and then replacing the WScript.Echo lines with calls to CreateEvent. The solution here is to generate an event indicating whether the ping was successful or not. We would have event rules in place to capture these events and potentially generate an alert. In order for those events to clearly distinguish between a success and failure event, we should assign unique event numbers to each type.

The new script is shown here:

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

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colPingedComputers = objWMIService.ExecQuery _
    ("Select * from Win32_PingStatus Where Address = '192.168.1.37'")

For Each objComputer in colPingedComputers
    If objComputer.StatusCode = 0 Then
        CreateEvent 100,EVENT_TYPE_SUCCESS,"Ping Check","Remote computer responded."
    Else
        CreateEvent 200,EVENT_TYPE_ERROR,"Ping Check","Remote computer did not respond."
   End If
Next

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

You’ll note that we have event codes 100 and 200 in the CreateEvent statements. For this script, I arbitrarily decided that I would use 100 for success events and 200 for error events. If the script became more complex, I might use 201, 202, etc., for other error events. This allows us to create event rules using the event number as criteria - similar to how most of the event rules work in MOM. I also arbitrarily used “Ping Check” for the event source - again so that this could be included in the criteria of the event rule. The most common criteria for an event rule in MOM are event source and number, so while you may arbitrarily assign these values, you should choose them carefully.

Figure 3 shows an example of an event generated by our modified script.

Generated event

Figure 3. Sample script-generated event.

Speaking of event rules, let’s finish this example by creating an event rule to generate an alert from the event that our modified script might create. We probably wouldn’t create an event rule to capture the success event from this script as we are usually interested only in generating an alert from an error.

Any MOM event generated by a script is assigned a provider of Script-generated Data, and this is the provider that the event rule should use. Table 3 identifies the attributes of the event rule that would generate an alert from the error event in Figure 3.

Table 3. Details of Event rule to capture sample event

Tab

Property

Value

General

Name

Remote computer appears to be unavailable

Data Provider

Provider name

Script-generated data

Criteria

From source

Ping Check

Criteria

With event ID

200

Alert

Generate alert

Checked

Alert

Alert severity

Critical Error

With an event rule like this in place, we can launch our script at regular intervals from a timed rule and ensure that we will receive an alert should our ping test ever fail.

Creating Performance Data

Now that we’ve covered events, let’s create some performance data. We’ll start with a CreatePerfData subroutine similar to the CreateEvent subroutine we wrote in the previous section. Like the CreateEvent subroutine, CreatePerfData can be simply pasted into any script that needs it and then called with a single line of code.

Sub CreatePerfData(strObjectName,strCounterName,strInstanceName,numValue)
    Set objPerfData = ScriptContext.CreatePerfData
    objPerfData.ObjectName = strObjectName
    objPerfData.CounterName =strCounterName
    objPerfData.InstanceName = strInstanceName
    objPerfData.Value = numValue
    ScriptContext.Submit objPerfData
End Sub

This subroutine creates a new piece of performance data, sets different properties of the object and then submits it to the MOM workflow. These properties are relatively self-explanatory, but just in case, they are defined in Table 4.

Table 4. Performance Data Properties

Property

Description

ObjectName

Name of the performance object

CounterName

Name of the performance counter

InstanceName

Name of the performance instance

Value

Numeric value of the data

To call the subroutine, use the following syntax:

CreatePerfData <ObjectName>,<CounterName>,<InstanceName>,<Value>

Each piece of performance data in MOM is identified by object name, counter name, and instance name - just like performance data collected from standard counters in Windows. When you create a Windows NT Performance Counter provider in MOM, you are limited to actual counters installed on the server. When you create perf data from a script in MOM you can define any names for these values that you want.

I’ll use the script List File Properties for our example this time. The original script is shown here:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile("c:\windows\system32\scrrun.dll")

Wscript.Echo "Date created: " & objFile.DateCreated
Wscript.Echo "Date last accessed: " & objFile.DateLastAccessed
Wscript.Echo "Date last modified: " & objFile.DateLastModified
Wscript.Echo "Drive: " & objFile.Drive
Wscript.Echo "Name: " & objFile.Name
Wscript.Echo "Parent folder: " & objFile.ParentFolder
Wscript.Echo "Path: " & objFile.Path
Wscript.Echo "Short name: " & objFile.ShortName
Wscript.Echo "Short path: " & objFile.ShortPath
Wscript.Echo "Size: " & objFile.Size
Wscript.Echo "Type: " & objFile.Type

This script returns all properties for a specified file, and we’re obviously going to need to get rid of all those WScript.Echo lines. Let’s suppose we’re interested in tracking the size of a particular file as performance data. Maybe this is a log file that grows continuously, and in addition to tracking its size for reporting, we would like to generate an alert if it reaches a certain size. We can reduce the number of properties referred to in the script to just Size since that’s the only piece of information we’re going to need. Paste in our subroutine, and change the WScript.Echo call to a CreatePerfData call, and we’re good to go. (I also changed the file that we are tracking the size of since the sample script uses scrrun.dll which is going to have a static size.)

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile("c:\logs\AppLog.txt")

CreatePerfData "File","File Size",objFile.Path,objFile.Size

Sub CreatePerfData(strObjectName,strCounterName,strInstanceName,numValue)
    Set objPerfData = ScriptContext.CreatePerfData
    objPerfData.ObjectName = strObjectName
    objPerfData.CounterName =strCounterName
    objPerfData.InstanceName = strInstanceName
    objPerfData.Value = numValue
    ScriptContext.Submit objPerfData
End Sub

If we are going to simply track the size of the file, we don’t need to do anything else. As soon as this script executes, the performance counter will show up in MOM. No performance collection rule is required. A screenshot from the Operator Console of the counter created by our script is shown in Figure 4.

Sample performance data

Figure 4. Sample performance data.

If we want to set a threshold rule for this counter and issue an alert if it exceeds a particular value, then we would need a threshold rule with details similar to those in Table 5.

Table 5. Details of Performance Rule for sample Perf Data

Tab

Property

Value

General

Name

The size of AppLog.txt has exceeded its threshold value.

Data Provider

Provider name

Script-generated data

Criteria

From instance

C:\logs\AppLog.txt

Criteria

Object

File

Criteria

Counter

File Size

Threshold

The sampled value

Checked

Threshold

Greater than the following value

1000000

Alert

Generate alert

Checked

Alert

Alert severity

Error

ScriptContext.Echo

I mentioned earlier that ScriptContext.Echo is the functional equivalent of WScript.Echo, but you won’t use it nearly as much. Creating Events and Perf Data are actually more common methods to get data back from a MOM script. ScriptContext.Echo is still quite useful though for two purposes: returning information from a script executed from a Task, and debugging. Debugging will be covered in Part 3 of this series, but I’ll discuss Tasks now.

Task Output

If a script is executed from a Task, then any output from ScriptContext.Echo will appear in the MOM event that indicates completion of the task. This can be used for testing a script you are going to use elsewhere in MOM or it may be the basis of a useful Task. Consider the script List Process Owners, which lists all processes currently running on a computer and the owners of those processes:

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

Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process")

For Each objProcess in colProcessList
    colProperties = objProcess.GetOwner(strNameOfUser,strUserDomain)
    Wscript.Echo "Process " & objProcess.Name & " is owned by " _ 
        & strUserDomain & "\" & strNameOfUser & "."
Next

It would be useful to launch this script with a Task to determine the processes currently running on a remote server (or multiple remote servers). This might be used in a troubleshooting scenario. We can modify this script to perform that exact function by simply changing WScript.Echo to ScriptContext.Echo.

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

Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process")

For Each objProcess in colProcessList
    colProperties = objProcess.GetOwner(strNameOfUser,strUserDomain)
    ScriptContext.Echo "Process " & objProcess.Name & " is owned by " _ 
        & strUserDomain & "\" & strNameOfUser & "."
Next

To test this script, create a Task to execute it and launch the Task against an agent computer. Within a few seconds, you should have an event returned similar to Figure 5.

Event

Figure 5. Event from List Running Processes script.

Conclusion

We’ve covered what you need to modify in WSH scripts to prepare them for MOM, and how you get information from the script into the MOM workflow. In Part 2 of this series, we’re going to discuss how to get information into the script. This includes working with parameters, which are the functional equivalent to arguments in WSH scripts. We’ll also discuss how we can access the event, performance data, or alert that launched the script, which is a concept completely unique to MOM.

About the Author

Brian Wren has been an infrastructure consultant with Microsoft Consulting Services for several years and specializes in management products and technologies.