Category Archives: VSAE Fragment

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.

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 🙂

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

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

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

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

' 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()

'--------------------------------------------------------------------------- ' 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

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

oAPI.AddItem oBag

oAPI.ReturnItems

End If

End Sub</ScriptBody><TimeoutSeconds>300</TimeoutSeconds></ProbeAction><ConditionDetectionID="PerfMapper"TypeID="Perf!System.Performance.DataGenericMapper"><ObjectName>Memory</ObjectName><CounterName>$Data/Property[@Name='PerfCounter']$</CounterName><InstanceName/><Value>$Data/Property[@Name='PerfValue']$</Value></ConditionDetection><ConditionDetectionID="IsNullCD"TypeID="System!System.ExpressionFilter"><Expression><SimpleExpression><ValueExpression><XPathQueryType="String">/DataItem/IsNull</XPathQuery></ValueExpression><Operator>Equal</Operator><ValueExpression><ValueType="String">false</Value></ValueExpression></SimpleExpression></Expression></ConditionDetection></MemberModules><Composition><NodeID="PerfMapper"><NodeID="ScriptDS"><NodeID="IsNullCD"><NodeID="PerfDS"/></Node></Node></Node></Composition></Composite></ModuleImplementation><OutputType>Perf!System.Performance.Data</OutputType></DataSourceModuleType></ModuleTypes><MonitorTypes><UnitMonitorTypeID="Windows.Monitoring.Extended.MonitorType.PercentMemoryUsed"Accessibility="Public"><MonitorTypeStates><MonitorTypeStateID="MTS_Over"/><MonitorTypeStateID="MTS_Under"/></MonitorTypeStates><Configuration><xsd:elementminOccurs="1"name="PhysicalMemory"type="xsd:double"xmlns:xsd="http://www.w3.org/2001/XMLSchema"/><xsd:elementminOccurs="1"name="IntervalSeconds"type="xsd:integer"xmlns:xsd="http://www.w3.org/2001/XMLSchema"/><xsd:elementminOccurs="1"name="Threshold"type="xsd:double"xmlns:xsd="http://www.w3.org/2001/XMLSchema"/><xsd:elementminOccurs="1"name="MatchCount"type="xsd:integer"xmlns:xsd="http://www.w3.org/2001/XMLSchema"/></Configuration><OverrideableParameters><OverrideableParameterID="IntervalSeconds"Selector="$Config/IntervalSeconds$"ParameterType="int"/><OverrideableParameterID="Threshold"Selector="$Config/Threshold$"ParameterType="double"/><OverrideableParameterID="MatchCount"Selector="$Config/MatchCount$"ParameterType="int"/></OverrideableParameters><MonitorImplementation><MemberModules><DataSourceID="Script"TypeID="Windows.Monitoring.Extended.DataSource.PercentMemoryUsed"><PhysicalMemory>$Config/PhysicalMemory$</PhysicalMemory><IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds></DataSource><ConditionDetectionID="CD_Under"TypeID="System!System.ExpressionFilter"><Expression><SimpleExpression><ValueExpression><XPathQueryType="Double">Value</XPathQuery></ValueExpression><Operator>Less</Operator><ValueExpression><ValueType="Double">$Config/Threshold$</Value></ValueExpression></SimpleExpression></Expression></ConditionDetection><ConditionDetectionID="CD_Over"TypeID="System!System.ExpressionFilter"><Expression><SimpleExpression><ValueExpression><XPathQueryType="Double">Value</XPathQuery></ValueExpression><Operator>GreaterEqual</Operator><ValueExpression><ValueType="Double">$Config/Threshold$</Value></ValueExpression></SimpleExpression></Expression><SuppressionSettings><MatchCount>$Config/MatchCount$</MatchCount></SuppressionSettings></ConditionDetection></MemberModules><RegularDetections><RegularDetectionMonitorTypeStateID="MTS_Under"><NodeID="CD_Under"><NodeID="Script"/></Node></RegularDetection><RegularDetectionMonitorTypeStateID="MTS_Over"><NodeID="CD_Over"><NodeID="Script"/></Node></RegularDetection></RegularDetections></MonitorImplementation></UnitMonitorType></MonitorTypes></TypeDefinitions><Monitoring><Monitors><UnitMonitorID="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><AlertSettingsAlertMessage="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><OperationalStateID="OpState_Healthy"HealthState="Success"MonitorTypeStateID="MTS_Under"/><OperationalStateID="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><StringResourceID="Windows.Monitoring.Extended.AlertMessage.PercentMemoryUsed"/></StringResources></Presentation><LanguagePacks><LanguagePackID="ENU"IsDefault="true"><DisplayStrings><DisplayStringElementID="Windows.Monitoring.Extended.Monitor.PercentMemoryUsed"><Name>Windows Percent Memory Used Monitor</Name></DisplayString><DisplayStringElementID="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>

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.

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).

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.

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.

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.

GRANTEXECUTEON 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!

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.

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 onlythese parameters in the data source criteria:

Event Source

Event Id

Event Level

Event Category

Event Parameter [0-..]

Furthermore, I suggest including onlythese 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…

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.

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 an instance group using VSAE

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.

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.