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.

_

45 thoughts on “Monitor Percent Memory Used”

      1. Ok thanks for the hint. The reason I’d like to do this, is because we’re creating different Groups for Windows Server to apply different thresholds because of different critically of the Servers.

        So how would you apply this?

  1. Hi Jonathan,
    This monitor can be used to replace the default memory monitor, right That one only checks physical memory and does not include pagefile usage for example.

    BTW, the download link is dead.
    Regards
    Tommy

    1. Consider this an enhancement. A lot of customers ask to monitor this, but not necessarily as a replacement to anything.

      The download link works for me, but it’s on google drive and might need to login. I will put this up on github and provide the link.

  2. Retro – where you ever able to target a specific group? I am trying to do the same and not having any real good results.

    Thanks,
    Tom

    1. Hi Tom,

      yes you have to seal the MP and reimport it into SCOM. Then you are able to target the group.
      It’s not possible to target an unsealed group from an unsealed MP.

  3. As I am not familiar with you script, is it possible to amend it to two monitor, one is for WARNING (e.g. 80% and the other is ERROR (e.g. 90%)

    Thanks

  4. It doesn’t appear that my alerts are returning back to healthy for any of the Windows Percent Alerts. I have verified several times that the percentage has returned to the healthy range. Today is 1/13/2016 and I have an alert from 12/29/2015 still sitting in my Active alerts.

    Anyone have any ideas whats going on?

    I can only think that given others do not appear to be having this problem that it has to be a parameter that I have not set? I did not create any overrides after import.

    1. Hi Jessica. Is this happening across all computers, or does it seem to be a problem with just this one? A couple things to check or try. First, try overriding the threshold for that instance to something like 99 and see if it flips to healthy. Also check whether WMI is working on that machine, because the data source script implements WMI calls to get memory values.

      I have tested the script on 2008 and 2012, and Microsoft documentation states it will work across all versions to date.

      1. Hi Jonathan, Thank you for your speedy response.

        There were, were being the key word, three machines alerting. After further investigating I was able to determine that these three boxes are SQL Database hosts. Very memory hungry machines.

        I’ve created an override specifically for our SQL machines now to rest at 95%.

        Thank you again for your speedy response.

    1. @Justin – There is no reference to the 2003 pack in the XML here. Why do you think this requires the 2003 pack?

  5. When I import the MP, it fails. It says it needs the Microsoft.Windows.Server.2003 v6.0.6989.0 is missing. If I try to resolve it can’t get it from the catalog.

    1. Ok – I might be missing something here, but it’s not called out as a dependency/reference anywhere in this custom pack that I can see. I’ll take a look if time permits later – hopefully someone else from the community can chime in if they are having a similar issue.

    1. @Sunil – you’ll need to modify the monitor type to include a “CD_Between” expression filter, and update the unit monitor to use this new warning state.

      1. Hi John,

        Could you please elaborate further. are you mentioning to make changes in vb script or the xml written

    2. @Sunil – the script will need to be updated to add a third property bag that would be filtered in the new “CD_Between” expression filter.

  6. Hi John,
    Its been months , since I have been trying to update it to three state, by any chance do you have updated script to check for three state monitor

  7. Buenas tardes Jonathan,

    Muchas gracias por todo este conocimiento que compartes.

    Será que me puedes guiar en algo:
    Yo necesito mostrar en un dashboard el porcentaje de memoria en uso, los monitores solo me traen Los MB disponibles. Con este MP que tu compartes como hago para mostrar el uso de la ram en un performance widget.

    Muchas gracias desde ya

  8. Hi,

    we have got a requirement in which UNIX Team want us to create a custom alert for available memory. As per them for all REHL7 Server SCOM should only trigger an alert when available megabytes is low (it should only consider available megabytes nothing else).

    Is there a way to create such custom alert then kindly help me and I would really thankful to you.

    1. Yes, you can implement it for Linux. Fork the memory monitors in the Linux pack, just like I did here with the memory monitor from the Windows pack, and implement the modules any way you want. Use my example here as a guide. Good luck!

  9. Hi
    Does it works for windows 7 clients ?
    I tried to override to windows 7 computer
    But no alerts with a 5 percent threshold (for te Sting)

    1. Yes, but you’ll need to change the “Target” in the unit monitor to something like Windows Operating System. Currently, as you can see from the code example, the Target is Windows Server Operating System, so client computers will not receive this monitor at all in this case. Good luck!

  10. A question. Have you seen instances where you get massive numbers? I get 4E+16 on some servers for no apparent reason 🙂

  11. Hi Jonathan

    Can we have the Diagnostic task with the unit monitor to get top 5 Memory utilization process that will be a great help

  12. I am getting dependency error as follows:
    The dependencies for this management pack cannot be located.

    To remove this management pack from the import list, right-click the management pack and click Remove. To see the list of missing dependencies, click Error in the Status column.

    It is looking for Windows 2003 and 2008.Monitoring. I don’t have either in my environment. how do i resolve this? Please help.

  13. Hello Jonathan,

    I am a newbie to SCOM, does the above MP suitable for windows server 2016 and 2019?

    Thank you.

Comments welcome (links require moderation)