A really useful feature of Health Explorer in Operations Manager is the ability to force the agent recalculate health of a particular monitor. One the most common uses for this is when you want to confirm that the action that you took actually fixed the problem. If you have implemented your management pack correctly all you need to do is select the unit monitor in health explorer and click on the "Recalculate Health" button.

In order to make this button actually force your monitor to recalculate health state, you must do some extra work in your management pack, or else nothing will happen. This is especially important if you are either creating a new monitor type or creating a script based monitor. Basically when creating a new monitor type or creating a script based monitor, unless you do some extra work "Recalculate Health" will not do much for you and the users of your MP.
Before we dive into the details of what needs to be done, let's first review a standard two state script based monitor. This monitor uses a script to determine the health state of a service (in a shipping MP I would use a different monitor type, but this was but this was convenient for demonstration purposes).
<UnitMonitor ID="Sample.ServiceStatus.Monitor2" Accessibility="Public" Enabled="true" Target="Sample.ServerRole" ParentMonitorID="Health!System.Health.AvailabilityState" Remotable="true" Priority="Normal" TypeID="Windows!Microsoft.Windows.TimedScript.TwoStateMonitorType" ConfirmDelivery="false">
<Category>Custom</Category>
<OperationalStates>
<OperationalState ID="Error" MonitorTypeStateID="Error" HealthState="Error" />
<OperationalState ID="Success" MonitorTypeStateID="Success" HealthState="Success" />
</OperationalStates>
<Configuration>
<IntervalSeconds>180</IntervalSeconds>
<SyncTime />
<ScriptName>GetServiceStatus2.vbs</ScriptName>
<Arguments>$Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$ dhcp</Arguments>
<ScriptBody>
<![CDATA[
call Main()
Sub Main()
Dim oAPI
Dim oBag
Dim serviceName
Dim computerName
Dim statusColl
Dim serviceStatus
computerName = WScript.Arguments(0)
serviceName = WScript.Arguments(1)
Set wmi = GetObject("winmgmts:\\" & computerName & "\root\cimv2")
wql = "SELECT * FROM Win32_Service WHERE Name='" & serviceName & "'"
Set statusColl = wmi.ExecQuery(wql)
If statusColl.Count = 1 Then
Dim service
Set service = statusColl.ItemIndex(0)
serviceStatus = service.State
End If
Set oAPI = CreateObject("MOM.ScriptAPI")
Set oBag = oAPI.CreatePropertyBag()
oBag.AddValue "ServiceStatus", serviceStatus
oAPI.Return(oBag)
End Sub
]]>
</ScriptBody>
<TimeoutSeconds>120</TimeoutSeconds>
<ErrorExpression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="String">Property[@Name='ServiceStatus']</XPathQuery>
</ValueExpression>
<Operator>NotEqual</Operator>
<ValueExpression>
<Value Type="String">Running</Value>
</ValueExpression>
</SimpleExpression>
</ErrorExpression>
<SuccessExpression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="String">Property[@Name='ServiceStatus']</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="String">Running</Value>
</ValueExpression>
</SimpleExpression>
</SuccessExpression>
</Configuration>
</UnitMonitor>
This monitor looks totally fine and will work properly by running the script and interpreting the results of the script every 180 seconds. The problem is that if you try to recalculate the health state of this monitor using health explorer, nothing will happen. This is because the underlying monitor type which in essence acts as the foundation for this monitor does not implement an "On Demand Detection". The "On Demand Detection" section of the monitor type provides additional information to OpsMgr in order to allow it to recalculate the health state at any point in time rather than waiting for the 180 seconds interval. Now lets look at the steps that need to be taken in order to implement a monitor which will react to a request to recalculate its health state:
1 - Implement a probe which is just responsible for retrieving the particular piece of instrumentation
<ProbeActionModuleType ID="Sample.ServiceStatus.Probe" Accessibility="Public" Batching="false" PassThrough="false">
<Configuration>
<xsd:element minOccurs="1" name="TimeoutSeconds" type="xsd:integer" />
<xsd:element minOccurs="1" name="ComputerPrincipalName" type="xsd:string" />
<xsd:element minOccurs="1" name="ServiceName" type="xsd:string" />
</Configuration>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<ProbeAction ID="PassThrough" TypeID="System!System.PassThroughProbe" />
<ProbeAction ID="Script" TypeID="Windows!Microsoft.Windows.ScriptPropertyBagProbe">
<ScriptName>GetServiceStatus.vbs</ScriptName>
<Arguments>$Config/ComputerPrincipalName$ $Config/ServiceName$</Arguments>
<ScriptBody>
<![CDATA[
call Main()
Sub Main()
Dim oAPI
Dim oBag
Dim serviceName
Dim computerName
Dim statusColl
Dim serviceStatus
computerName = WScript.Arguments(0)
serviceName = WScript.Arguments(1)
Set wmi = GetObject("winmgmts:\\" & computerName & "\root\cimv2")
wql = "SELECT * FROM Win32_Service WHERE Name='" & serviceName & "'"
Set statusColl = wmi.ExecQuery(wql)
If statusColl.Count = 1 Then
Dim service
Set service = statusColl.ItemIndex(0)
serviceStatus = service.State
End If
Set oAPI = CreateObject("MOM.ScriptAPI")
Set oBag = oAPI.CreatePropertyBag()
oBag.AddValue "ServiceStatus", serviceStatus
oAPI.Return(oBag)
End Sub
]]>
</ScriptBody>
<TimeoutSeconds>$Config/TimeoutSeconds$</TimeoutSeconds>
</ProbeAction>
</MemberModules>
<Composition>
<Node ID="Script">
<Node ID="PassThrough" />
</Node>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.PropertyBagData</OutputType>
<TriggerOnly>true</TriggerOnly>
</ProbeActionModuleType>
2 - Implement a composite data source module which would combine the probe with a scheduler. Its not a must, but I would recommend you do this to simplify the implementation of the monitor type in step 3:
<DataSourceModuleType ID="Sample.ServiceStatus.DataSource" Accessibility="Internal" Batching="false">
<Configuration>
<xsd:element minOccurs="1" name="IntervalSeconds" type="xsd:integer" />
<xsd:element minOccurs="1" name="TimeoutSeconds" type="xsd:integer" />
<xsd:element minOccurs="1" name="ComputerPrincipalName" type="xsd:string" />
<xsd:element minOccurs="1" name="ServiceName" type="xsd:string" />
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int" />
<OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int" />
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<DataSource ID="Scheduler" TypeID="System!System.Discovery.Scheduler">
<Scheduler>
<SimpleReccuringSchedule>
<Interval Unit="Seconds">$Config/IntervalSeconds$</Interval>
</SimpleReccuringSchedule>
<ExcludeDates />
</Scheduler>
</DataSource>
<ProbeA