SCOM Advanced Authoring: Windows Automatic Services Monitoring using Powershell

Monitoring services in windows computers is available out of box in SCOM through Service Monitoring Template. But in a large enterprise with over 1000s of windows computers and 100s of applications, it is difficult to list out all services that needs to be monitored in each computer and create monitoring using template. Consider monitoring on average 30 services in 1000 computers would result on 30,000 instances added to SCOM DB. This will create numerous classes, discoveries and cause bloating of instance space which will make SCOM less responsive.

Also, we cannot create a monitor for each service and target it across all computers as each service may be present on bunch of computers and not on others. Thus targeting unanimously will result in false alarms and again, we may need 30+ windows service monitors targeted to all windows computers which will create overhead on agents and thus on the computers running the agent.

So, What is the solution?

Optimal solution would be creating a single rule to monitor all automatic services in each computer and alert on those which are not running. This can be accomplished using Powershell script with property bag output.

The rule runs on each computer at specific time interval, creates property bags for each service which is set to automatic but not running and an alert is generated for each property bag.

A catch to note in this monitoring scenario is not to alert on services that are stopped only for a moment. To overcome the issue, we will use consolidator condition. So only if the service is failed for ‘n’ consecutive samples, we will alert.

This solution, though optimal pose another challenge – What if we do not want to monitor a service which is set to automatic in one or few of computers.

This can be handled using a centrally located file with details of service and the computers to be excluded from monitoring.

We will see how to construct the Management Pack XML to accomplish this. You can also create MP using Visual Studio, MP Studio or Authoring Console.

Step 1:

Add references to the Management pack.

<ManagementPack ContentReadable="true" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <Manifest>
    <Identity>
      <ID>GKLab.Windows.Automatic.Service.Monitoring</ID>
      <Version>1.0.0.0</Version>
    </Identity>
    <Name>GKLab Windows Automatic Service Monitoring</Name>
    <References>
      <Reference Alias="SC">
        <ID>Microsoft.SystemCenter.Library</ID>
        <Version>6.1.7221.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Windows">
        <ID>Microsoft.Windows.Library</ID>
        <Version>6.1.7221.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Health">
        <ID>System.Health.Library</ID>
        <Version>6.1.7221.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="System">
        <ID>System.Library</ID>
        <Version>6.1.7221.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
      <Reference Alias="Performance">
        <ID>System.Performance.Library</ID>
        <Version>6.1.7221.0</Version>
        <PublicKeyToken>31bf3856ad364e35</PublicKeyToken>
      </Reference>
    </References>
  </Manifest>

Step 2:

Now create a Powershell property bag probe script. The Powershell script fetches list for all services that are set to start automatic and checks for the current status. For each service that are set to Automatic but not running, a property bag is created.

To exclude some services from being monitored, a centrally located CSV file is used and the path of file is passed as parameter to the script. The script reads list of services to be excluded from monitoring from CSV file and compares it with the list of services in the target computer. The property bag for excludes services are not created.

param (
    [string] $excludeservicelist
)
if (test-path $excludeservicelist) {
write-eventlog -logname "Operations Manager" -Source "Health Service Script" -EventID 776 -Message "WindowsAutomaticServiceMonitoring.ps1 - Accessing Exclusion List CSV" -EntryType Information
    $contents = Import-Csv $excludeservicelist
}
$TargetComputer = hostname
$api = New-Object -comObject 'MOM.ScriptAPI'
$auto_services = Get-WmiObject -Class Win32_Service -Filter "StartMode='Auto'"
foreach ($service in $auto_services)
{
    $isExcluded = 0
    $state = $service.state
    $name = $service.DisplayName
    If ($Contents){
        $contents | ForEach-Object{
            $ExcludeServiceDisplayName = $_.ServiceToExclude
            $ExcludeComputerName = $_.ComputersToExclude
            if (($name -match $ExcludeServiceDisplayName) -and (($TargetComputer -match $ExcludeComputerName) -or ($ExcludeComputerName -match "ALL_COMPUTERS"))){
                $isExcluded = 1
                write-eventlog -logname "Operations Manager" -Source "Health Service Script" -EventID 777 -Message "WindowsAutomaticServiceMonitoring.ps1 - Excluded Service Name - $ExcludeServiceDisplayName, Excluded Computer Name - $ExcludeComputerName" -EntryType Information
            }
        }
    }
    if (($isExcluded -eq 0) -and ($state -eq "Stopped")){
        write-eventlog -logname "Operations Manager" -Source "Health Service Script" -EventID 778 -Message "WindowsAutomaticServiceMonitoring.ps1 - Windows Service set to Automatic but Not Running - $name" -EntryType Information
        $bag = $api.CreatePropertyBag()    
        $bag.AddValue("ServiceName", $name)
        $bag.AddValue("Status", $state)
        $bag
    }
}

Step 3:

Create a data source module incorporating the above written Powershell script. We will use consolidator condition as discussed in solution part to alert only on valid service failures.

  <TypeDefinitions>
    <ModuleTypes>
      <DataSourceModuleType ID="GKLab.Windows.Auto.Service.Monitoring.DataSource" Accessibility="Internal" Batching="false">
        <Configuration>
          <xsd:element minOccurs="1" name="ExcludeServiceList" type="xsd:string" />
          <xsd:element minOccurs="1" name="IntervalSeconds" type="xsd:integer" />
          <xsd:element minOccurs="1" name="ConsolidationInterval" type="xsd:integer" />
          <xsd:element minOccurs="1" name="Count" type="xsd:integer" />
        </Configuration>
        <OverrideableParameters>
          <OverrideableParameter ID="IntervalSeconds" Selector="$Config/IntervalSeconds$" ParameterType="int" />
          <OverrideableParameter ID="Count" Selector="$Config/Count$" ParameterType="int" />
          <OverrideableParameter ID="ConsolidationInterval" Selector="$Config/ConsolidationInterval$" ParameterType="int" />
        </OverrideableParameters>
        <ModuleImplementation Isolation="Any">
          <Composite>
            <MemberModules>
              <DataSource ID="Trigger" TypeID="System!System.SimpleScheduler">
                <IntervalSeconds>$Config/IntervalSeconds$</IntervalSeconds>
                <SyncTime>00:00</SyncTime>
              </DataSource>
              <ProbeAction ID="Probe" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagProbe">
                <ScriptName>WindowsAutomaticServicesMonitoring.ps1</ScriptName>
                <ScriptBody><![CDATA[
param (
    [string] $excludeservicelist
)
if (test-path $excludeservicelist) {
write-eventlog -logname "Operations Manager" -Source "Health Service Script" -EventID 776 -Message "WindowsAutomaticServiceMonitoring.ps1 - Accessing Exclusion List CSV" -EntryType Information
    $contents = Import-Csv $excludeservicelist
}
$TargetComputer = hostname
$api = New-Object -comObject 'MOM.ScriptAPI'
$auto_services = Get-WmiObject -Class Win32_Service -Filter "StartMode='Auto'"
foreach ($service in $auto_services)
{
    $isExcluded = 0
    $state = $service.state
    $name = $service.DisplayName
    If ($Contents){
        $contents | ForEach-Object{
            $ExcludeServiceDisplayName = $_.ServiceToExclude
            $ExcludeComputerName = $_.ComputersToExclude
            if (($name -match $ExcludeServiceDisplayName) -and (($TargetComputer -match $ExcludeComputerName) -or ($ExcludeComputerName -match "ALL_COMPUTERS"))){
                $isExcluded = 1
                write-eventlog -logname "Operations Manager" -Source "Health Service Script" -EventID 777 -Message "WindowsAutomaticServiceMonitoring.ps1 - Excluded Service Name - $ExcludeServiceDisplayName, Excluded Computer Name - $ExcludeComputerName" -EntryType Information
            }
        }
    }
    if (($isExcluded -eq 0) -and ($state -eq "Stopped")){
        write-eventlog -logname "Operations Manager" -Source "Health Service Script" -EventID 778 -Message "WindowsAutomaticServiceMonitoring.ps1 - Windows Service set to Automatic but Not Running - $name" -EntryType Information
        $bag = $api.CreatePropertyBag()    
        $bag.AddValue("ServiceName", $name)
        $bag.AddValue("Status", $state)
        $bag
    }
}
             ]]></ScriptBody>
                <Parameters>
                  <Parameter>
                    <Name>ExcludeServiceList</Name>
                    <Value>$Config/ExcludeServiceList$</Value>
                  </Parameter>
                </Parameters>
                <TimeoutSeconds>300</TimeoutSeconds>
              </ProbeAction>
              <ConditionDetection ID="Consolidator" TypeID="System!System.ConsolidatorCondition">
                <Consolidator>
                  <ConsolidationProperties>
                    <PropertyXPathQuery>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</PropertyXPathQuery>
                    <PropertyXPathQuery>Property[@Name='ServiceName']</PropertyXPathQuery>
                  </ConsolidationProperties>
                  <TimeControl>
                    <WithinTimeSchedule>
                      <Interval>$Config/ConsolidationInterval$</Interval>
                    </WithinTimeSchedule>
                  </TimeControl>
                  <CountingCondition>
                    <Count>$Config/Count$</Count>
                    <CountMode>OnNewItemTestOutputRestart_OnTimerSlideByOne</CountMode>
                  </CountingCondition>
                </Consolidator>
              </ConditionDetection>
            </MemberModules>
            <Composition>
              <Node ID="Consolidator">
                <Node ID="Probe">
                  <Node ID="Trigger" />
                </Node>
              </Node>
            </Composition>
          </Composite>
        </ModuleImplementation>
        <OutputType>System!System.ConsolidatorData</OutputType>
      </DataSourceModuleType>
    </ModuleTypes>
  </TypeDefinitions>

Step 4:

Next we will create a rule using the data source. Below configuration needs to be customized according to the need.

  • IntervalSeconds – Polling Interval in Seconds
  • Count – Number of polls, the service should fail to alert. (Minimum 2)
  • ConsolidationInterval – The interval time within which the service status fails ‘n’ number of times to generate alert.  (Minimum value = (n-1) * IntervalSeconds where n = count)
  • ExcludeServiceList – the UNC path for the excluded services list file (in CSV format).

Sample CSV provided below. CSV has two headers.

  • “ServiceToExclude” – Display name of service.
  • “ComputersToExclude” – NetBIOS Name of computer. If two or more computers, it can be specified as individual entry or using regular expression syntax. If need to exclude in all computers, the value should be ALL_Computers
ServiceToExclude,ComputersToExclude
Distributed Transaction Coordinator,SCOM2012R2
Windows Audio,Win2k12-DC
Remote Registry,ALL_Computers
Software Protection,SCOM2012R2|Win2k12-DC
<Monitoring>
    <Rules>
      <Rule ID="GKLab.Windows.AutomaticService.Monitoring.Rule" Enabled="true" Target="Windows!Microsoft.Windows.Computer" ConfirmDelivery="true" Remotable="true" Priority="Normal" DiscardLevel="100">
        <Category>Alert</Category>
        <DataSources>
          <DataSource ID="DS" TypeID="GKLab.Windows.Auto.Service.Monitoring.DataSource">
            <ExcludeServiceList>\\SCOM2012R2\Configs\WindowsAutomaticServiceMonitoringExclusionList.csv</ExcludeServiceList>
            <IntervalSeconds>300</IntervalSeconds>
            <ConsolidationInterval>600</ConsolidationInterval>
            <Count>2</Count>
          </DataSource>
        </DataSources>
        <WriteActions>
          <WriteAction ID="Alert" TypeID="Health!System.Health.GenerateAlert">
            <Priority>1</Priority>
            <Severity>2</Severity>
            <AlertMessageId>$MPElement[Name="GKLab.Windows.AutomaticService.Monitoring.Rule.AlertMessage"]$</AlertMessageId>
            <AlertParameters>
              <AlertParameter1>$Data/Context/DataItem/Property[@Name='ServiceName']$</AlertParameter1>
            </AlertParameters>
            <Suppression>
              <SuppressionValue>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</SuppressionValue>
              <SuppressionValue>$Data/Context/DataItem/Property[@Name='ServiceName']$</SuppressionValue>
            </Suppression>
          </WriteAction>
        </WriteActions>
      </Rule>
    </Rules>
  </Monitoring>

Step 5:

Final step is to construct XML for presentation and language packs. Ensure the close the <ManagementPack> tag.

  <Presentation>
    <StringResources>
      <StringResource ID="GKLab.Windows.AutomaticService.Monitoring.Rule.AlertMessage" />
    </StringResources>
  </Presentation>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
    <DisplayString ElementID="GKLab.Windows.Automatic.Service.Monitoring">
          <Name>GKLab Windows Automatic Service Monitoring</Name>
          <Description>GKLab Windows Automatic Service Monitoring Management Pack</Description>
        </DisplayString>
        <DisplayString ElementID="GKLab.Windows.Auto.Service.Monitoring.DataSource">
          <Name>GKLab Windows Automatic Service Monitoring Data Source</Name>
          <Description>GKLab Windows Automatic Service Monitoring Data Source</Description>
        </DisplayString>
        <DisplayString ElementID="GKLab.Windows.AutomaticService.Monitoring.Rule">
          <Name>Windows Automatic Services Monitoring Rule</Name>
          <Description>Windows Automatic Services Monitoring Rule</Description>
        </DisplayString>
        <DisplayString ElementID="GKLab.Windows.AutomaticService.Monitoring.Rule" SubElementID="Alert">
          <Name>Alert</Name>
        </DisplayString>
        <DisplayString ElementID="GKLab.Windows.AutomaticService.Monitoring.Rule" SubElementID="DS">
          <Name>GKLab Windows Automatic Service Monitoring Data Source</Name>
        </DisplayString>
        <DisplayString ElementID="GKLab.Windows.AutomaticService.Monitoring.Rule.AlertMessage">
          <Name>Windows Automatic Services Monitoring Alert</Name>
          <Description>Windows Service {0} is set to auto-start but is currently not running.</Description>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPack>

Step 6:

Deploy the MP in lab and check for alerts. You can download the XML here which you can import in to any authoring tool. Customize as per your needs and have fun.

Happy SCOMing…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.