Category Archives: VSAE Fragment

Health Service Watcher Group based on Domain Name

Just added a fragment file to GitHub that demonstrates how to add a health service watcher group based on domain name. This can be useful for multi-tenant environments that need to implement user role scoping and notifications where the customer wants to see health service heartbeat failure alerts.

Just update the ID’s, display names, and the regular expression pattern to fit your environment.

You can view and download the fragment here.

 

🙂

Monitor Percent Memory Used

I’ve seen this in the forums quite a bit, so I felt I should write a pack that monitor for Windows % memory used. I basically just took the script from the Windows packs, that claims (in the script comments) to work across all base Windows OS’s, and plugged it into a data source. Then I created a monitor type and a unit monitor.

I will post the xml here, but you can also download the management pack at the end of the post. I have only tested this on Windows 2008 and Windows 2012. As the base OS script says, it’s compatible with all versions. The default target of the unit monitor is Windows Server Operating System, so keep that in mind. The unit monitor is enabled by default. Because it target Windows Server Operating System, the Source of the alert will be just that; it will not be the computer name.

By default, it runs every 15 minutes and has a threshold of 90%. It will alert after 2 intervals over threshold. You can override interval, threshold, and match count. MatchCount is the number of intervals over threshold before generating an alert. You choose how you want to implement it 🙂

Here is the code:

<?xml version="1.0" encoding="utf-8"?>
<ManagementPack SchemaVersion="2.0" ContentReadable="true" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Manifest>
<Identity>
<ID>Windows.Monitoring.Extended</ID>
<Version>1.0.0.19</Version>
</Identity>
<Name>Windows.Monitoring.Extended</Name>
<References>
<Reference Alias="Windows">
<ID>Microsoft.Windows.Library</ID>
<Version>7.5.8501.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias="Health">
<ID>System.Health.Library</ID>
<Version>7.0.8432.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias="System">
<ID>System.Library</ID>
<Version>7.5.8501.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
<Reference Alias="Perf">
<ID>System.Performance.Library</ID>
<Version>7.0.8432.0</Version>
<PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
</Reference>
</References>
</Manifest>
<TypeDefinitions>
<ModuleTypes>
<DataSourceModuleType ID="Windows.Monitoring.Extended.DataSource.PercentMemoryUsed" Accessibility="Public" Batching="false">
<Configuration>
<xsd:element minOccurs="1" name="PhysicalMemory" type="xsd:double" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
<xsd:element minOccurs="1" name="IntervalSeconds" type="xsd:integer" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int" />
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<DataSource ID="PerfDS" TypeID="Perf!System.Performance.DataProvider">
<CounterName>Available MBytes</CounterName>
<ObjectName>Memory</ObjectName>
<InstanceName />
<AllInstances>false</AllInstances>
<Frequency>$Config/IntervalSeconds$</Frequency>
</DataSource>
<ProbeAction ID="ScriptDS" TypeID="Windows!Microsoft.Windows.ScriptPropertyBagProbe">
<ScriptName>Microsoft.Windows.Server.OperatingSystem.PercentMemoryUsed.vbs</ScriptName>
<Arguments>$Data/Value$ $Config/PhysicalMemory$</Arguments>
<ScriptBody>
'Copyright (c) Microsoft Corporation. All rights reserved.

'*************************************************************************
' $ScriptName: "Microsoft.Windows.Server.Common"$
'
' Purpose: To have one place for common stuff across various BaseOS VBScripts
'
' $File: Microsoft.Windows.Server.Common.vbs$
'*************************************************************************

Option Explicit

SetLocale("en-us")

' LogScripEvent Constants
Const lsEventError = 1
Const lsEventWarning = 2
Const lsEventInformation = 3

' WMI Constants
Const wbemCimtypeUseDefault = 0 'Use Default Type CIM type - Custom
Const wbemCimtypeSint16 = 2 'Signed 16-bit integer
Const wbemCimtypeSint32 = 3 'Signed 32-bit integer
Const wbemCimtypeReal32 = 4 '32-bit real number
Const wbemCimtypeReal64 = 5 '64-bit real number
Const wbemCimtypeString = 8 'String
Const wbemCimtypeBoolean = 11 'Boolean value
Const wbemCimtypeObject = 13 'CIM object
Const wbemCimtypeSint8 = 16 'Signed 8-bit integer
Const wbemCimtypeUint8 = 17 'Unsigned 8-bit integer
Const wbemCimtypeUint16 = 18 'Unsigned 16-bit integer
Const wbemCimtypeUint32 = 19 'Unsigned 32-bit integer
Const wbemCimtypeSint64 = 20 'Signed 64-bit integer
Const wbemCimtypeUint64 = 21 'Unsigned 64-bit integer
Const wbemCimtypeDatetime = 101 'Date/time value
Const wbemCimtypeReference = 102 'Reference to a CIM object
Const wbemCimtypeChar16 = 103 '16-bit character

Const ErrAction_None = 0
Const ErrAction_Trace = 1
Const ErrAction_ThrowError = 16
Const ErrAction_Abort = 32
Const ErrAction_ThrowErrorAndAbort = 48

Dim g_ErrorEventNumber, g_TraceEventNumber, g_DebugFlag
g_ErrorEventNumber = 4001
g_TraceEventNumber = 4002
g_DebugFlag = False

'---------------------------------------------------------------------------
' Returns WMI Instance requested. Tries to execute WMI query a N times.
'---------------------------------------------------------------------------
Function WMIGetInstanceExTryN(oWMI, ByVal sInstance, ByVal N)
Dim oInstance, nInstanceCount
Dim e, i
Set e = New Error

For i = 0 To i &lt; N
On Error Resume Next
Set oInstance = oWMI.InstancesOf(sInstance)
e.Save
On Error Goto 0
If IsEmpty(oInstance) Or e.Number &lt;&gt; 0 Then
If i = N - 1 Then
ThrowScriptError "The class name '" &amp; sInstance &amp; "' returned no instances. Please check to see if this is a valid WMI class name.", e
End If
Else
On Error Resume Next
nInstanceCount = oInstance.Count
e.Save
On Error Goto 0
If e.Number &lt;&gt; 0 Then
If i = N - 1 Then
ThrowScriptError "The class name '" &amp; sInstance &amp; "' did not return any valid instances. Please check to see if this is a valid WMI class name.", e
End If
Else
Exit For
End If
End If
WScript.Sleep(1000)
Next

Set WMIGetInstanceExTryN = oInstance
End Function

'---------------------------------------------------------------------------
' Returns WMI Instance requested.
'---------------------------------------------------------------------------
Function WMIGetInstanceEx(oWMI, ByVal sInstance)
Dim oInstance, nInstanceCount
Dim e
Set e = New Error

On Error Resume Next
Set oInstance = oWMI.InstancesOf(sInstance)
e.Save
On Error Goto 0
If IsEmpty(oInstance) Or e.Number &lt;&gt; 0 Then
ThrowScriptError "The class name '" &amp; sInstance &amp; "' returned no instances. Please check to see if this is a valid WMI class name.", e
End If

'Determine if we queried a valid WMI class - Count will return 0 or empty
On Error Resume Next
nInstanceCount = oInstance.Count
e.Save
On Error Goto 0
If e.Number &lt;&gt; 0 Then
ThrowScriptError "The class name '" &amp; sInstance &amp; "' did not return any valid instances. Please check to see if this is a valid WMI class name.", e
End If

Set WMIGetInstanceEx = oInstance
End Function

'---------------------------------------------------------------------------
' Connect to WMI.
'---------------------------------------------------------------------------
Function WMIConnect(ByVal sNamespace)
Dim oWMI
Dim e
Set e = New Error
On Error Resume Next
Set oWMI = GetObject(sNamespace)
e.Save
On Error Goto 0
If IsEmpty(oWMI) Then
ThrowScriptError "Unable to open WMI Namespace '" &amp; sNamespace &amp; "'. Check to see if the WMI service is enabled and running, and ensure this WMI namespace exists.", e
End If
Set WMIConnect = oWMI
End Function

'---------------------------------------------------------------------------
' Returns WMI Instance requested.
'---------------------------------------------------------------------------
Function WMIGetInstance(ByVal sNamespace, ByVal sInstance)
Dim oWMI, oInstance
Set oWMI = WMIConnect(sNamespace)
Set oInstance = WMIGetInstanceEx(oWMI, sInstance)
Set WMIGetInstance = oInstance
End Function

'---------------------------------------------------------------------------
' Returns WMI Instance requested.
'---------------------------------------------------------------------------
Function WMIGetInstanceNoAbort(ByVal sNamespace, ByVal sInstance)
Dim oWMI, oInstance, nInstanceCount

On Error Resume Next
Set oWMI = GetObject(sNamespace)
If Not IsEmpty(oWMI) Then
Set oInstance = oWMI.InstancesOf(sInstance)
If Not IsEmpty(oInstance) And Err.Number = 0 Then
'Determine if we queried a valid WMI class - Count will return 0 or empty
nInstanceCount = oInstance.Count
If Err.Number = 0 Then
Set WMIGetInstanceNoAbort = oInstance
On Error Goto 0
Exit Function
End If
End If
End If

On Error Goto 0
Set WMIGetInstanceNoAbort = Nothing
End Function

'---------------------------------------------------------------------------
' Executes the WMI query and returns the result set.
'---------------------------------------------------------------------------
Function WMIExecQuery(ByVal sNamespace, ByVal sQuery)
Dim oWMI, oQuery, nInstanceCount
Dim e
Set e = New Error
On Error Resume Next
Set oWMI = GetObject(sNamespace)
e.Save
On Error Goto 0
If IsEmpty(oWMI) Then
ThrowScriptError "Unable to open WMI Namespace '" &amp; sNamespace &amp; "'. Check to see if the WMI service is enabled and running, and ensure this WMI namespace exists.", e
End If

On Error Resume Next
Set oQuery = oWMI.ExecQuery(sQuery)
e.Save
On Error Goto 0
If IsEmpty(oQuery) Or e.Number &lt;&gt; 0 Then
ThrowScriptError "The Query '" &amp; sQuery &amp; "' returned an invalid result set. Please check to see if this is a valid WMI Query.", e
End If

'Determine if we queried a valid WMI class - Count will return 0 or empty
On Error Resume Next
nInstanceCount = oQuery.Count
e.Save
On Error Goto 0
If e.Number &lt;&gt; 0 Then
ThrowScriptError "The Query '" &amp; sQuery &amp; "' did not return any valid instances. Please check to see if this is a valid WMI Query.", e
End If

Set WMIExecQuery = oQuery
End Function

'---------------------------------------------------------------------------
' Executes the WMI query and returns the result set, no abort version.
'---------------------------------------------------------------------------
Function WMIExecQueryNoAbort(ByVal sNamespace, ByVal sQuery)
Dim oWMI, oQuery
Set oWMI = GetObject(sNamespace)
Set oQuery = oWMI.ExecQuery(sQuery)
Set WMIExecQueryNoAbort = oQuery
End Function

'---------------------------------------------------------------------------
' Retrieves WMI property.
'---------------------------------------------------------------------------
Function GetWMIProperty(oWmi, sPropName, nCIMType, ErrAction)
Dim sValue, oWmiProp, oError
Set oError = New Error

' Check that object is valid.
If Not IsValidObject(oWmi) Then
If (ErrAction And ErrAction_ThrowError) = ErrAction_ThrowError Then _
ThrowScriptErrorNoAbort "Accessing property on invalid WMI object.", oError
If (ErrAction And ErrAction_Abort) = ErrAction_Abort Then _
Quit()

GetWMIProperty = ""
Exit Function
End If

' Get properties...
On Error Resume Next
Set oWmiProp = oWmi.Properties_.Item(sPropName)
oError.Save
If oError.Number &lt;&gt; 0 Then
If (ErrAction And ErrAction_ThrowError) = ErrAction_ThrowError Then _
ThrowScriptErrorNoAbort "An error occurred while accessing WMI property: '" &amp; sPropName &amp; "'.", oError
If (ErrAction And ErrAction_Abort) = ErrAction_Abort Then _
Quit()
End If
On Error Goto 0

If IsValidObject(oWmiProp) Then
sValue = oWmiProp.Value

If IsNull(sValue) Then ' If value is null, return blank to avoid any issues
GetWMIProperty = ""
Else
Select Case (oWmiProp.CIMType)
Case wbemCimtypeString, wbemCimtypeSint16, wbemCimtypeSint32, wbemCimtypeReal32, wbemCimtypeReal64, wbemCimtypeSint8, wbemCimtypeUint8, wbemCimtypeUint16, wbemCimtypeUint32, wbemCimtypeSint64, wbemCimtypeUint64:
If Not oWmiProp.IsArray Then
GetWMIProperty = Trim(CStr(sValue))
Else
GetWMIProperty = Join(sValue, ", ")
End If
Case wbemCimtypeBoolean:
If sValue = 1 Or UCase(sValue) = "TRUE" Then
GetWMIProperty = "True"
Else
GetWMIProperty = "False"
End If
Case wbemCimtypeDatetime:
Dim sTmpStrDate

' First attempt to convert the whole wmi date string
sTmpStrDate = Mid(sValue, 5, 2) &amp; "/" &amp; _
Mid(sValue, 7, 2) &amp; "/" &amp; _
Left(sValue, 4) &amp; " " &amp; _
Mid (sValue, 9, 2) &amp; ":" &amp; _
Mid(sValue, 11, 2) &amp; ":" &amp; _
Mid(sValue, 13, 2)
If IsDate(sTmpStrDate) Then
GetWMIProperty = CDate(sTmpStrDate)
Else
' Second, attempt just to convert the YYYYMMDD
sTmpStrDate = Mid(sValue, 5, 2) &amp; "/" &amp; _
Mid(sValue, 7, 2) &amp; "/" &amp; _
Left(sValue, 4)
If IsDate(sTmpStrDate) Then
GetWMIProperty = CDate(sTmpStrDate)
Else
' Nothing works - return passed in string
GetWMIProperty = sValue
End If
End If
Case Else:
GetWMIProperty = ""
End Select
End If
Else
If (ErrAction And ErrAction_ThrowError) = ErrAction_ThrowError Then _
ThrowScriptErrorNoAbort "An error occurred while accessing WMI property: '" &amp; sPropName &amp; "'.", oError
If (ErrAction And ErrAction_Abort) = ErrAction_Abort Then _
Quit()

GetWMIProperty = ""
End If

If (ErrAction And ErrAction_Trace) = ErrAction_Trace Then _
WScript.Echo " + " &amp; sPropName &amp; " :: '" &amp; GetWMIProperty &amp; "'"
End Function

'---------------------------------------------------------------------------
' Class for error handling.
'---------------------------------------------------------------------------
Class Error
Private m_lNumber
Private m_sSource
Private m_sDescription
Private m_sHelpContext
Private m_sHelpFile
Public Sub Save()
m_lNumber = Err.number
m_sSource = Err.Source
m_sDescription = Err.Description
m_sHelpContext = Err.HelpContext
m_sHelpFile = Err.helpfile
End Sub
Public Sub Raise()
Err.Raise m_lNumber, m_sSource, m_sDescription, m_sHelpFile, m_sHelpContext
End Sub
Public Sub Clear()
m_lNumber = 0
m_sSource = ""
m_sDescription = ""
m_sHelpContext = ""
m_sHelpFile = ""
End Sub
Public Default Property Get Number()
Number = m_lNumber
End Property
Public Property Get Source()
Source = m_sSource
End Property
Public Property Get Description()
Description = m_sDescription
End Property
Public Property Get HelpContext()
HelpContext = m_sHelpContext
End Property
Public Property Get HelpFile()
HelpFile = m_sHelpFile
End Property
End Class

'---------------------------------------------------------------------------
' Creates an event and sends it back to the mom server.
'---------------------------------------------------------------------------
Function ThrowScriptErrorNoAbort(ByVal sMessage, ByVal oErr)
' Retrieve the name of this (running) script
Dim FSO, ScriptFileName
Set FSO = CreateObject("Scripting.FileSystemObject")
ScriptFileName = FSO.GetFile(WScript.ScriptFullName).Name
Set FSO = Nothing

If Not IsNull(oErr) Then _
sMessage = sMessage &amp; ". " &amp; oErr.Description

On Error Resume Next
Dim oAPITemp
Set oAPITemp = CreateObject("MOM.ScriptAPI")
oAPITemp.LogScriptEvent ScriptFileName, g_ErrorEventNumber, lsEventError, sMessage
On Error Goto 0

WScript.Echo sMessage
End Function

'---------------------------------------------------------------------------
' Creates an event and sends it back to the mom server.
'---------------------------------------------------------------------------
Function ThrowScriptError(Byval sMessage, ByVal oErr)
On Error Resume Next
ThrowScriptErrorNoAbort sMessage, oErr
Quit()
End Function

'---------------------------------------------------------------------------
' Creates automation objects and returns it.
'---------------------------------------------------------------------------
Function MomCreateObject(ByVal sProgramId)
Dim oError
Set oError = New Error

On Error Resume Next
Set MomCreateObject = CreateObject(sProgramId)
oError.Save
On Error Goto 0

If oError.Number &lt;&gt; 0 Then
ThrowScriptError "Unable to create automation object '" &amp; sProgramId &amp; "'", oError
End If
End Function

'---------------------------------------------------------------------------
' Quits the script.
'---------------------------------------------------------------------------
Function Quit()
WScript.Quit()
End Function

'---------------------------------------------------------------------------
' Checks whether oObject is valid.
'---------------------------------------------------------------------------
Function IsValidObject(ByVal oObject)
IsValidObject = False
If IsObject(oObject) Then
If Not oObject Is Nothing Then
IsValidObject = True
End If
End If
End Function

'---------------------------------------------------------------------------
' Outputs arguments for debugging purposes
'---------------------------------------------------------------------------
Function TraceLogArguments
Dim oArgs
Set oArgs = WScript.Arguments
Dim i, sArgs
For i = 0 To oArgs.Count - 1
sArgs = sArgs &amp; " {" &amp; oArgs(i) &amp; "}"
Next
TraceLogMessage "Arguments:" &amp; sArgs
End Function

'---------------------------------------------------------------------------
' Verifies that number of arguments is correct
'---------------------------------------------------------------------------
Function VerifyNumberOfArguments(ByVal NumberOfArguments)
Dim oArgs
Set oArgs = WScript.Arguments
If oArgs.Count &lt;&gt; NumberOfArguments Then
Dim i, sArgs
For i = 0 To oArgs.Count - 1
sArgs = sArgs &amp; " {" &amp; oArgs(i) &amp; "}"
Next
ThrowScriptError "Invalid number of arguments (" &amp; oArgs.Count &amp; " instead of " &amp; NumberOfArguments &amp; "). Arguments:" &amp; sArgs, Null
End If
End Function

'---------------------------------------------------------------------------
' Outputs to file and echo for debugging purposes
'---------------------------------------------------------------------------
Function TraceLogMessage(ByVal sMessage)
WScript.Echo sMessage

If g_DebugFlag = True Then
' Retrieve the name of this (running) script
Dim FSO, ScriptFileName
Set FSO = CreateObject("Scripting.FileSystemObject")
ScriptFileName = FSO.GetFile(WScript.ScriptFullName).Name
Set FSO = Nothing

On Error Resume Next
Dim oAPITemp
Set oAPITemp = MOMCreateObject("MOM.ScriptAPI")
oAPITemp.LogScriptEvent ScriptFileName, g_TraceEventNumber, lsEventInformation, sMessage
On Error Goto 0
End If
End Function

'---------------------------------------------------------------------------
' Verifies the expression. If equals to False then generates an error and quits the script
' Usage:
' Verify Not WMISet Is Nothing, "WMISet is invalid!"
' Verify WMISet.Count = 1, "Invalid quantity of services with name 'Server' (qty = " &amp; WMISet.Count &amp; ")."
'---------------------------------------------------------------------------
Function Verify(ByVal bBool, ByVal sMessage)
If bBool = False Then
ThrowScriptError sMessage, Null
End If
End Function


Function GetRegistryKeyValue(ByVal keyPath, ByVal key)
Dim oReg, strKeyValue

Set oReg = MOMCreateObject("WScript.Shell")
On Error Resume Next

strKeyValue = oReg.RegRead(keyPath &amp; key)
If Err.Number &lt;&gt; 0 Then
ThrowScriptError "An error occurred while reading the registry: '" &amp; keyPath &amp; key &amp; "'", Err.Description
strKeyValue = ""
End If

' resume error
On Error Goto 0

GetRegistryKeyValue = strKeyValue
End Function
'Copyright (c) Microsoft Corporation. All rights reserved.

'*************************************************************************
' $ScriptName: "Microsoft.Windows.Server.OperatingSystem.PercentMemoryUsed"$
'
'
' $File: Microsoft.Windows.Server.OperatingSystem.PercentMemoryUsed.vbs$
'*************************************************************************

' Parameters that should be passed to this script
' 0 Available Physical memory MBytes.
' 1 Total amount of Physical Memory in KBytes.

Const sCounterName = "PercentMemoryUsed"
Const sObjectName = "Memory"

Call Main()

Sub Main

VerifyNumberOfArguments(2)

Dim oArgs, sErrorDescription
Set oArgs = WScript.Arguments

Dim nAvailableMBytes, nPhysicalMemoryKBytes, nPhysicalMemoryMBytes, oAPI, nResult, oBag

nAvailableMBytes = oArgs(0)
nPhysicalMemoryKBytes = oArgs(1)
nResult = 0
sErrorDescription = "Invalid arguments are detected: " &amp; nAvailableMBytes &amp; " " &amp; nPhysicalMemoryKBytes

If Not (IsNumeric(nAvailableMBytes) And IsNumeric(nPhysicalMemoryKBytes)) Then
ThrowScriptError sErrorDescription, Null
End If

nPhysicalMemoryMBytes = CDbl(nPhysicalMemoryKBytes/1024)

If (CDbl(nAvailableMBytes) &gt; 0) Then

If Not (CDbl(nAvailableMBytes) &lt; CDbl(nPhysicalMemoryMBytes)) Then
ThrowScriptError sErrorDescription, Null
End If

Set oAPI = MOMCreateObject("MOM.ScriptAPI")
Set oBag = oAPI.CreateTypedPropertyBag(2)

nResult = CDbl(100 - (nAvailableMBytes/(nPhysicalMemoryMBytes)*100))

With oBag
.AddValue "PerfCounter", sCounterName
.AddValue "PerfValue", nResult
End With

oAPI.AddItem oBag

oAPI.ReturnItems

End If

End Sub
</ScriptBody>
<TimeoutSeconds>300</TimeoutSeconds>
</ProbeAction>
<ConditionDetection ID="PerfMapper" TypeID="Perf!System.Performance.DataGenericMapper">
<ObjectName>Memory</ObjectName>
<CounterName>$Data/Property[@Name='PerfCounter']$</CounterName>
<InstanceName />
<Value>$Data/Property[@Name='PerfValue']$</Value>
</ConditionDetection>
<ConditionDetection ID="IsNullCD" TypeID="System!System.ExpressionFilter">
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="String">/DataItem/IsNull</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="String">false</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
</ConditionDetection>
</MemberModules>
<Composition>
<Node ID="PerfMapper">
<Node ID="ScriptDS">
<Node ID="IsNullCD">
<Node ID="PerfDS" />
</Node>
</Node>
</Node>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>Perf!System.Performance.Data</OutputType>
</DataSourceModuleType>
</ModuleTypes>
<MonitorTypes>
<UnitMonitorType ID="Windows.Monitoring.Extended.MonitorType.PercentMemoryUsed" Accessibility="Public">
<MonitorTypeStates>
<MonitorTypeState ID="MTS_Over" />
<MonitorTypeState ID="MTS_Under" />
</MonitorTypeStates>
<Configuration>
<xsd:element minOccurs="1" name="PhysicalMemory" type="xsd:double" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
<xsd:element minOccurs="1" name="IntervalSeconds" type="xsd:integer" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
<xsd:element minOccurs="1" name="Threshold" type="xsd:double" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
<xsd:element minOccurs="1" name="MatchCount" type="xsd:integer" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int" />
<OverrideableParameter ID="Threshold" Selector="$Config/Threshold$" ParameterType="double" />
<OverrideableParameter ID="MatchCount" Selector="$Config/MatchCount$" ParameterType="int" />
</OverrideableParameters>
<MonitorImplementation>
<MemberModules>
<DataSource ID="Script" TypeID="Windows.Monitoring.Extended.DataSource.PercentMemoryUsed">
<PhysicalMemory>$Config/PhysicalMemory$</PhysicalMemory>
<IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>
</DataSource>
<ConditionDetection ID="CD_Under" TypeID="System!System.ExpressionFilter">
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="Double">Value</XPathQuery>
</ValueExpression>
<Operator>Less</Operator>
<ValueExpression>
<Value Type="Double">$Config/Threshold$</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
</ConditionDetection>
<ConditionDetection ID="CD_Over" TypeID="System!System.ExpressionFilter">
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="Double">Value</XPathQuery>
</ValueExpression>
<Operator>GreaterEqual</Operator>
<ValueExpression>
<Value Type="Double">$Config/Threshold$</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
<SuppressionSettings>
<MatchCount>$Config/MatchCount$</MatchCount>
</SuppressionSettings>
</ConditionDetection>
</MemberModules>
<RegularDetections>
<RegularDetection MonitorTypeStateID="MTS_Under">
<Node ID="CD_Under">
<Node ID="Script" />
</Node>
</RegularDetection>
<RegularDetection MonitorTypeStateID="MTS_Over">
<Node ID="CD_Over">
<Node ID="Script" />
</Node>
</RegularDetection>
</RegularDetections>
</MonitorImplementation>
</UnitMonitorType>
</MonitorTypes>
</TypeDefinitions>
<Monitoring>
<Monitors>
<UnitMonitor ID="Windows.Monitoring.Extended.Monitor.PercentMemoryUsed" Accessibility="Public" Enabled="true" ParentMonitorID="Health!System.Health.PerformanceState" Priority="Normal" Target="Windows!Microsoft.Windows.Server.OperatingSystem" TypeID="Windows.Monitoring.Extended.MonitorType.PercentMemoryUsed">
<Category>PerformanceHealth</Category>
<AlertSettings AlertMessage="Windows.Monitoring.Extended.AlertMessage.PercentMemoryUsed">
<AlertOnState>Error</AlertOnState>
<AutoResolve>true</AutoResolve>
<AlertPriority>Normal</AlertPriority>
<AlertSeverity>Error</AlertSeverity>
<AlertParameters>
<AlertParameter1>$Data/Context/Value$</AlertParameter1>
</AlertParameters>
</AlertSettings>
<OperationalStates>
<OperationalState ID="OpState_Healthy" HealthState="Success" MonitorTypeStateID="MTS_Under" />
<OperationalState ID="OpState_Unhealty" HealthState="Error" MonitorTypeStateID="MTS_Over" />
</OperationalStates>
<Configuration>
<PhysicalMemory>$Target/Property[Type="Windows!Microsoft.Windows.OperatingSystem"]/PhysicalMemory$</PhysicalMemory>
<IntervalSeconds>900</IntervalSeconds>
<Threshold>90</Threshold>
<MatchCount>2</MatchCount>
</Configuration>
</UnitMonitor>
</Monitors>
</Monitoring>
<Presentation>
<StringResources>
<StringResource ID="Windows.Monitoring.Extended.AlertMessage.PercentMemoryUsed" />
</StringResources>
</Presentation>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<DisplayStrings>
<DisplayString ElementID="Windows.Monitoring.Extended.Monitor.PercentMemoryUsed">
<Name>Windows Percent Memory Used Monitor</Name>
</DisplayString>
<DisplayString ElementID="Windows.Monitoring.Extended.AlertMessage.PercentMemoryUsed">
<Name>Windows Memory Over Threshold</Name>
<Description>Windows memory is currently at 0: {0}%</Description>
</DisplayString>
</DisplayStrings>
<KnowledgeArticles></KnowledgeArticles>
</LanguagePack>
</LanguagePacks>
</ManagementPack>

Here is the link to the full management pack xml download.

 

If you have a request for a new rule or unit monitor, ask in the comments section and I’ll do what I can to expand on this pack.

_

Report Fragment–Visual Studio Authoring Extensions

Developing reports in SCOM is quite a bit different than developing any type of monitoring workflow. You really need to ramp up your skills on a couple different tools and languages to become a good report developer.

In this post, I will cover a typical VSAE fragment that provides for deploying the report and stored procedure files – of course, the report files are deployed to the report server and the stored procedure is installed on the data warehouse.

This post covers the fragment essentials – it does not get into report or stored procedure development. It is intended to be a quick reference for those developers out there to quickly plug in the necessary elements to push the rdl and sql resource files in their management pack.

At the end, I will provide some essential elements that need to be included in your sql file that will satisfy "install", "upgrade", and "uninstall" functionality, as well as set the right execution permissions that will allow the data reader account to run the report in a generic environment.

In this example, there is a main report and a detail report. The detail report may be launched by clicking on an element in the main report – consider this a linked report.

Here’s the entire fragment

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Reporting>
    <DataWarehouseScripts>
      <DataWarehouseScript ID="MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script" Accessibility="Public">
        <InstallScript>Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Install</InstallScript>
        <UninstallScript>Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Uninstall</UninstallScript>
        <UpgradeScript>Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Upgrade</UpgradeScript>
      </DataWarehouseScript>
      <DataWarehouseScript ID="MyReports.Deploy.MyReportAvailabilityDataGet.Script" Accessibility="Public">
        <InstallScript>Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Install</InstallScript>
        <UninstallScript>Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Uninstall</UninstallScript>
        <UpgradeScript>Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Upgrade</UpgradeScript>
      </DataWarehouseScript>
    </DataWarehouseScripts>
    <Reports>
      <Report ID="MyReports.Availability.Main" Accessibility="Public" Visible="true">
        <Dependencies>
          <DataWarehouseScript>MyReports.Deploy.MyReportAvailabilityDataGet.Script</DataWarehouseScript>
        </Dependencies>
        <ReportDefinition>Res.MyReports.Availability.Main</ReportDefinition>
      </Report>
      <Report ID="MyReports.Availability.Detail" Accessibility="Public" Visible="true">
        <Dependencies>
          <DataWarehouseScript>MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script</DataWarehouseScript>
          <Report>MyReports.Availability.Main</Report>
        </Dependencies>
        <ReportDefinition>Res.MyReports.Availability.Detail</ReportDefinition>
      </Report>
    </Reports>
  </Reporting>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="MyReports">
          <Name>MyReports</Name>
          <Description>This management pack contains all data warehouse and reporting elements for custom MyReports.</Description>
        </DisplayString>
        <DisplayString ElementID="MyReports.Availability.Main">
          <Name>Availability Main</Name>
          <Description>Availability MyReport Main</Description>
        </DisplayString>
        <DisplayString ElementID="MyReports.Availability.Detail">
          <Name>Availability Detail</Name>
          <Description>Availability MyReport Detail</Description>
        </DisplayString>
        <DisplayString ElementID="MyReports.Deploy.MyReportAvailabilityDataGet.Script">
          <Name>Deploy MyReport Availability Data Get Script</Name>
        </DisplayString>
        <DisplayString ElementID="MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script">
          <Name>Deploy MyReport Availability Data Detail Get Script</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
  <Resources>
    <Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Install" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Install.sql" HasNullStream="false" />
    <Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Uninstall" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Uninstall.sql" HasNullStream="false" />
    <Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Upgrade" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Upgrade.sql" HasNullStream="false" />
    <Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Install" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Install.sql" HasNullStream="false" />
    <Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Uninstall" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Uninstall.sql" HasNullStream="false" />
    <Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Upgrade" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Upgrade.sql" HasNullStream="false" />
    <ReportResource ID="Res.MyReports.Availability.Detail" Accessibility="Public" FileName="Res.MyReports.Availability.Detail.rdl" HasNullStream="false" MIMEType="application/octet-stream" />
    <ReportResource ID="Res.MyReports.Availability.Main" Accessibility="Public" FileName="Res.MyReports.Availability.Main.rdl" HasNullStream="false" MIMEType="application/octet-stream" />
  </Resources>
</ManagementPackFragment>

 

Let’s break it down.

 

Data Warehouse Scripts

<DataWarehouseScript ID="MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script" Accessibility="Public">
  <InstallScript>Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Install</InstallScript>
  <UninstallScript>Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Uninstall</UninstallScript>
  <UpgradeScript>Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Upgrade</UpgradeScript>
</DataWarehouseScript>

This is where the resource file pointers are defined – your sql stored procedures. You will need each version of the stored procedure to install, upgrade, and uninstall the stored procedure. These pointers reference the actual sql file resource later.

Note: I have yet to see the uninstall work (I think this is a bug in the sdk, but I won’t go there now).

 

Reports

<Report ID="MyReports.Availability.Main" Accessibility="Public" Visible="true">
  <Dependencies>
    <DataWarehouseScript>MyReports.Deploy.MyReportAvailabilityDataGet.Script</DataWarehouseScript>
  </Dependencies>
  <ReportDefinition>Res.MyReports.Availability.Main</ReportDefinition>
</Report>

This section defines the report id, the report dependencies, and the report definition resource (this points to the actual rdl file later).

 

Resource Files (skipping language packs, as we know what that’s for)

<Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Install" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Install.sql" HasNullStream="false" />
<Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Uninstall" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Uninstall.sql" HasNullStream="false" />
<Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Upgrade" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataGet.Script.Upgrade.sql" HasNullStream="false" />
<Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Install" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Install.sql" HasNullStream="false" />
<Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Uninstall" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Uninstall.sql" HasNullStream="false" />
<Resource ID="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Upgrade" Accessibility="Public" FileName="Res.MyReports.Deploy.MyReportAvailabilityDataDetailGet.Script.Upgrade.sql" HasNullStream="false" />
<ReportResource ID="Res.MyReports.Availability.Detail" Accessibility="Public" FileName="Res.MyReports.Availability.Detail.rdl" HasNullStream="false" MIMEType="application/octet-stream" />
<ReportResource ID="Res.MyReports.Availability.Main" Accessibility="Public" FileName="Res.MyReports.Availability.Main.rdl" HasNullStream="false" MIMEType="application/octet-stream" />

This section ties the resource id’s from above to actual physical files you will include in your solution – these are the .rdl and .sql files.

 

Stored Procedure Necessities

I mentioned earlier that I would discuss a couple things you will need in your stored procedure. Namely, you will need to specify the action of the procedure (install, upgrade, uninstall) and you need to assign permissions to execute the procedure (otherwise it will not work and you’ll get errors).

MSDN has a mediocre description for deploying stored procedures, but it falls short with real world examples. So I’ll give an example of each here.

Install

Basically, you need to first create the procedure, and then alter the procedure as follows. Don’t worry about the parameter declarations – it’s just an example in case you have them.

IF NOT EXISTS (SELECT * FROM sysobjects WHERE type = 'P' AND name = 'MyReport_AvailabilityDataGet')
BEGIN
EXECUTE ('CREATE PROCEDURE dbo.MyReport_AvailabilityDataGet AS RETURN 1')
END
GO

ALTER PROCEDURE dbo.MyReport_AvailabilityDataGet
    @StartDate datetime,
    @EndDate datetime,
    @ObjectList xml,
    @MonitorName nvarchar(256),
    @DataAggregation tinyint = 0,
    @LanguageCode varchar(3) = 'ENU'
AS
BEGIN
SET NOCOUNT ON
{your procedure goes here}
 

Upgrade

The only thing that needs to be changed for the upgrade procedure is to remove the entire first section of the install procedure (the first 5 lines). Everything else stays the same – just start your procedure with the ALTER PROCUDURE section.

 

Uninstall

This is actually very simple – just perform a drop.

IF EXISTS (SELECT * FROM sysobjects WHERE type = 'P' AND name = 'MyReport_AvailabilityDataGet')
BEGIN
        DROP PROCEDURE dbo.[MyReport_AvailabilityDataGet]
END
GO
 

Assign Permissions to the DataReader account

This is required at the end of both the install and upgrade scripts. The OpsMgrReader account is a standard role that is created during setup, so unless you have some custom configuration in your environment, this will work for you.

GRANT EXECUTE ON MyReport_AvailabilityDataGet TO OpsMgrReader
GO

 

And that’s about it. Now go write some reports and deploy them with your management pack, like a pro!

Event Description Pattern Matching (with minimal impact)

One thing to think about when authoring rules and monitors is performance, and Windows event monitoring is no exception. If you need to search for a string in an event description, and if said event description is not parameterized, this post is for you.

You could add this logic while creating a rule in the Operations console.

 

image

 

The reason you should not do this is because it’s going to require more compute time than is necessary. If the monitored event log typically has a steady stream of events written, a single event detection rule like this could create a significant processing bottleneck on the monitored computer.

This is because all filtering is happening at the data source level, which means every element in your expression filter is checked against every event that is written to the specified log on that computer. Searching for a string in the event description takes time, and this is why it’s a bad practice to implement event description processing in the data source module.

To implement event monitoring with minimal performance impact, I suggest including only these parameters in the data source criteria:

  • Event Source
  • Event Id
  • Event Level
  • Event Category
  • Event Parameter [0-..]

 

Furthermore, I suggest including only these operators in the data source criteria:

  • Equal
  • NotEqual
  • Greater
  • GreaterEqual
  • Less
  • LessEqual

 

So, how is it possible to implement pattern matching while still providing minimal processing impact?

The answer is to process pattern matching criteria in a condition detection module.

 

Wow, what a concept! Let the data source filter the simple event criteria, then move to the next module to process more complex criteria. That’s the cool thing about using System.ExpressionFilter. The workflow will exit if the expression <> true.

 

To understand this logic a little better, here is the processing sequence…

Without Condition Detection:

1. DataSource\EventDetection\Provider – match event log and logging computer (if true, move next)

2. DataSource\EventDetection\Filter – process all criteria (if true, move next)

3. WriteAction\Alert – generate an alert

 

With Condition Detection:

1. DataSource\EventDetection\Provider – match event log and logging computer (if true, move next)

2. DataSource\BasicEventDetection\Filter – process simple criteria (if true, move next)

3. ConditionDetection\FilterDescription – process complex criteria (if true, move next)

4. WriteAction\Alert – generate an alert

 

Unfortunately, it is not possible to implement the condition detection module in the Operations console. You will need to author this using the R2 Authoring Console, Visual Studio Authoring Extension, or directly in XML. I am providing a rule fragment you can add to your project using VSAE.

Similarly, you could implement this logic in an event detection monitor.

 

The below fragment detects event 101, with source TEST and event level ERROR in the Application log. If simple criteria matches, move the next module to process complex criteria (match SomePattern in the event description). Generate an alert at the end.

This rule fragment needs to be updated with your criteria and alert details. If you build it, alerts will come – and your manager will thank you.

 

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Monitoring>
    <Rules>
      <Rule ID="MyMp.Rule.EventDescriptionMatches" ConfirmDelivery="true" DiscardLevel="100" Enabled="true" Priority="Normal" Remotable="false" Target="Windows!Microsoft.Windows.Computer">
        <Category>Alert</Category>
        <DataSources>
          <DataSource ID="BasicEventDetection" TypeID="Windows!Microsoft.Windows.EventProvider">
            <ComputerName>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/NetworkName$</ComputerName>
            <LogName>Application</LogName>
            <Expression>
              <And>
                <Expression>
                  <SimpleExpression>
                    <ValueExpression>
                      <XPathQuery>EventSourceName</XPathQuery>
                    </ValueExpression>
                    <Operator>Equal</Operator>
                    <ValueExpression>
                      <Value>TEST</Value>
                    </ValueExpression>
                  </SimpleExpression>
                </Expression>
                <Expression>
                  <SimpleExpression>
                    <ValueExpression>
                      <XPathQuery>EventDisplayNumber</XPathQuery>
                    </ValueExpression>
                    <Operator>Equal</Operator>
                    <ValueExpression>
                      <Value>101</Value>
                    </ValueExpression>
                  </SimpleExpression>
                </Expression>
                <Expression>
                  <SimpleExpression>
                    <ValueExpression>
                      <XPathQuery>EventLevel</XPathQuery>
                    </ValueExpression>
                    <Operator>Equal</Operator>
                    <ValueExpression>
                      <Value>1</Value>
                    </ValueExpression>
                  </SimpleExpression>
                </Expression>
              </And>
            </Expression>
          </DataSource>
        </DataSources>
        <ConditionDetection ID="FilterDescription" TypeID="System!System.ExpressionFilter">
          <Expression>
            <RegExExpression>
              <ValueExpression>
                <XPathQuery>EventDescription</XPathQuery>
              </ValueExpression>
              <Operator>MatchesRegularExpression</Operator>
              <Pattern>SomePattern</Pattern>
            </RegExExpression>
          </Expression>
        </ConditionDetection>
        <WriteActions>
          <WriteAction ID="Alert" TypeID="Health!System.Health.GenerateAlert">
            <Priority>1</Priority>
            <Severity>2</Severity>
            <AlertMessageId>$MPElement[Name="MyMp.Rule.EventDescriptionMatches.AlertMessage"]$</AlertMessageId>
            <AlertParameters />
            <Suppression />
          </WriteAction>
        </WriteActions>
      </Rule>
    </Rules>
  </Monitoring>
  <Presentation>
    <StringResources>
      <StringResource ID="MyMp.Rule.EventDescriptionMatches.AlertMessage"/>
    </StringResources>
  </Presentation>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="MyMp.Rule.EventDescriptionMatches">
          <Name>Event Description Match Alert Rule</Name>
        </DisplayString>
        <DisplayString ElementID="MyMp.Rule.EventDescriptionMatches.AlertMessage">
          <Name>Event Description Match</Name>
          <Description>Event description match pattern specified in rule configuration.</Description>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 

 

 

 

__

Computer and Instance Group Fragments in VSAE

I’m updating my Technet posts about creating groups in the R2 Authoring Console. Here are the same examples using the Visual Studio Authoring Extensions. You can add these as templates to your fragment library for use in future development projects.

 

How to create a computer group using VSAE

 

Original post: How to create a computer group in the R2 Authoring Console

 

The main thing to focus on here is $MPElement[Name="YourMp.Class.YourClass"]$. This is referencing the class in which you want to include it’s hosting Windows Computer object.

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="YourMp.Class.YourComputerGroup" Abstract="false" Accessibility="Public" Base="SC!Microsoft.SystemCenter.ComputerGroup" Hosted="false" Singleton="true" />
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <Monitoring>
    <Discoveries>
      <Discovery ID="YourMp.Discovery.YourComputerGroup" ConfirmDelivery="false" Enabled="true" Priority="Normal" Remotable="true" Target="YourMp.Class.YourComputerGroup">
        <Category>Discovery</Category>
        <DiscoveryTypes />
        <DataSource ID="DS1" TypeID="SC!Microsoft.SystemCenter.GroupPopulator">
          <RuleId>$MPElement$</RuleId>
          <GroupInstanceId>$MPElement[Name="YourMp.Class.YourComputerGroup"]$</GroupInstanceId>
          <MembershipRules>
            <MembershipRule>
              <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass>
              <RelationshipClass>$MPElement[Name="SC!Microsoft.SystemCenter.ComputerGroupContainsComputer"]$</RelationshipClass>
              <Expression>
                <Contains>
                  <MonitoringClass>$MPElement[Name="YourMp.Class.YourClass"]$</MonitoringClass>
                </Contains>
              </Expression>
            </MembershipRule>
          </MembershipRules>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="YourMp.Class.YourComputerGroup">
          <Name>Your Computer Group</Name>
          <Description>This group contains instances of Windows Computer objects that host YourClass.</Description>
        </DisplayString>
        <DisplayString ElementID="YourMp.Discovery.YourComputerGroup">
          <Name>Your Group Discovery</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 

How to create an instance group using VSAE

 

Original post: How to create an instance group in the R2 Authoring Console

 

The main thing to focus on here is $MPElement[Name="YourMp.Class.YourFirstClass"]$. This is referencing the discovered instances in which you want to include in your group. You can add multiple classes if you need the group to contain different types of instances.

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="YourMp.Class.YourInstanceGroup" Abstract="false" Accessibility="Public" Base="MSIL!Microsoft.SystemCenter.InstanceGroup" Hosted="false" Singleton="true" />
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <Monitoring>
    <Discoveries>
      <Discovery ID="YourMp.Discovery.YourInstanceGroup" ConfirmDelivery="false" Enabled="true" Priority="Normal" Remotable="true" Target="YourMp.Class.YourInstanceGroup">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="YourMp.Class.YourFirstClass" />
          <DiscoveryClass TypeID="YourMp.Class.YourSecondClass" />
        </DiscoveryTypes>
        <DataSource ID="DS1" TypeID="SC!Microsoft.SystemCenter.GroupPopulator">
          <RuleId>$MPElement$</RuleId>
          <GroupInstanceId>$MPElement[Name="YourMp.Class.YourInstanceGroup"]$</GroupInstanceId>
          <MembershipRules>
            <MembershipRule>
              <MonitoringClass>$MPElement[Name="YourMp.Class.YourFirstClass"]$</MonitoringClass>
              <RelationshipClass>$MPElement[Name="MSIL!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>
            </MembershipRule>
            <MembershipRule>
              <MonitoringClass>$MPElement[Name="YourMp.Class.YourSecondClass"]$</MonitoringClass>
              <RelationshipClass>$MPElement[Name="MSIL!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>
            </MembershipRule>
          </MembershipRules>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="YourMp.Class.YourInstanceGroup">
          <Name>Your Instances Group</Name>
          <Description>This group contains all discovered instances referenced in the MonitoringClass(es).</Description>
        </DisplayString>
        <DisplayString ElementID="YourMp.Discovery.YourInstanceGroup">
          <Name>Your Instance Group Discovery</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 

How to create a group of Windows Computer instances that host some class and matches a property of that class.

 

Original post: How to create a group of Windows Computers based on a discovered property of virtually any class

 

The main thing to focus on here is $MPElement[Name="YourMp.Class.SomeClass"]$. This is referencing the Windows Computer hosted instances in which you want to include in your group. Also notice SomeProperty and SomePattern – this is the property you will be comparing in your regular expression.

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <TypeDefinitions>
    <EntityTypes>
      <ClassTypes>
        <ClassType ID="YourMp.Class.YourComputerGroup" Abstract="false" Accessibility="Public" Base="SC!Microsoft.SystemCenter.ComputerGroup" Hosted="false" Singleton="true" />
      </ClassTypes>
    </EntityTypes>
  </TypeDefinitions>
  <Monitoring>
    <Discoveries>
      <Discovery ID="YourMp.Discovery.YourComputerGroup" ConfirmDelivery="false" Enabled="true" Priority="Normal" Remotable="true" Target="YourMp.Class.YourComputerGroup">
        <Category>Discovery</Category>
        <DiscoveryTypes />
        <DataSource ID="DS1" TypeID="SC!Microsoft.SystemCenter.GroupPopulator">
          <RuleId>$MPElement$</RuleId>
          <GroupInstanceId>$MPElement[Name="YourMp.Class.YourComputerGroup"]$</GroupInstanceId>
          <MembershipRules>
            <MembershipRule>
              <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass>
              <RelationshipClass>$MPElement[Name="SC!Microsoft.SystemCenter.ComputerGroupContainsComputer"]$</RelationshipClass>
              <Expression>
                <Contains>
                  <MonitoringClass>$MPElement[Name="SomeMp.Class.SomeClass"]$</MonitoringClass>
                  <Expression>
                    <RegExExpression>
                      <ValueExpression>
                        <Property>$MPElement[Name="SomeMp.Class.SomeClass"]/SomeProperty$</Property>
                      </ValueExpression>
                      <Operator>MatchesRegularExpression</Operator>
                      <Pattern>SomePattern</Pattern>
                    </RegExExpression>
                  </Expression>
                </Contains>
              </Expression>
            </MembershipRule>
          </MembershipRules>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="YourMp.Class.YourComputerGroup">
          <Name>Your Computer Group</Name>
          <Description>This group contains Windows Computer instances that host some class and matches some property of that class.</Description>
        </DisplayString>
        <DisplayString ElementID="YourMp.Discovery.YourComputerGroup">
          <Name>Your Group Discovery</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

 

Check out this other post about adding Health Service objects to a group.

 

Need help developing a management pack? Drop us a line

SCOMskills_Signature.

 

 

_