Category Archives: Authoring

Timed Script Discovery (vbscript)

AKA: Microsoft.Windows.TimedScript.DiscoveryProvider

If you are following these VSAE Fragment posts in order, you’ll see they are tied together in the same example management pack. This fragment demonstrates the timed script discovery provider, and discovers the application component class discussed in the previous post.

What does it do?

This discovery uses a script to read a local csv file (c:\ScomSkills.csv). The file is expected to have two entries, separated by a comma. Doesn’t matter what the entries are, as they will simply be discovered as class properties.

One thing I’d like to callout here is the discovery of Entity\DisplayName, highlighted below. It is important to discover PrincipalName or NetbiosName for Entity\DisplayName, because this is what the source will be when monitors targeting this class generate an alert. Ever see an alert generated by "c:" or "Windows Server 2008"? Not very useful while looking at a long list of alerts in the console, and doesn’t help much if you have other processes that are expecting computer name values for source. This is also discussed here.

 

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Monitoring>
    <Discoveries>
      <Discovery ID="SCOMskills.Demo.TimedScript.Discovery" ConfirmDelivery="false" Enabled="true" Priority="Normal" Remotable="true" Target="SCOMskills.Demo.LocalApplication.Class">
        <Category>Discovery</Category>
        <DiscoveryTypes>
          <DiscoveryClass TypeID="SCOMskills.Demo.ApplicationComponent.Class">
            <Property TypeID="SCOMskills.Demo.ApplicationComponent.Class" PropertyID="CsvField1"/>
            <Property TypeID="SCOMskills.Demo.ApplicationComponent.Class" PropertyID="CsvField2"/>
            <Property TypeID="System!System.Entity" PropertyID="DisplayName"/>
          </DiscoveryClass>
        </DiscoveryTypes>
        <DataSource ID="DS" TypeID="Windows!Microsoft.Windows.TimedScript.DiscoveryProvider">
          <IntervalSeconds>60</IntervalSeconds>
          <SyncTime/>
          <ScriptName>SCOMskills.Demo.Discovery.vbs</ScriptName>
          <Arguments>$MPElement$ $Target/Id$ $Target/Host/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$ c:\ScomSkillsDemo.csv</Arguments>
          <ScriptBody>
            <![CDATA[
Option Explicit

Dim oArgs
Set oArgs = WScript.Arguments
if oArgs.Count < 4 Then
   Wscript.Quit -1
End If

Dim SourceID, ManagedEntityId, TargetComputer, SourceFile

SourceId = oArgs(0)
ManagedEntityId = oArgs(1)
TargetComputer = oArgs(2)
SourceFile = oArgs(3)

Dim oAPI, oDiscoveryData, oInst
Set oAPI = CreateObject("MOM.ScriptAPI")
set oDiscoveryData = oAPI.CreateDiscoveryData(0, SourceId, ManagedEntityId)

Dim oFso, oFile, aField
Set oFso = CreateObject("Scripting.FileSystemObject")

If (oFso.FileExists(SourceFile)) Then
Set oFile = oFso.OpenTextFile(SourceFile)
aField = split(oFile.ReadLine,",")

set oInst = oDiscoveryData.CreateClassInstance("$MPElement[Name='SCOMskills.Demo.ApplicationComponent.Class']$")
call oInst.AddProperty("$MPElement[Name='Windows!Microsoft.Windows.Computer']/PrincipalName$", TargetComputer)
call oInst.AddProperty("$MPElement[Name='SCOMskills.Demo.ApplicationComponent.Class']/CsvField1$", aField(0))
call oInst.AddProperty("$MPElement[Name='SCOMskills.Demo.ApplicationComponent.Class']/CsvField2$", aField(1))
call oInst.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", TargetComputer)
call oDiscoveryData.AddInstance(oInst)

Else

Wscript.Quit -1

End If

Call oAPI.Return(oDiscoveryData) 
]]>
          </ScriptBody>
          <TimeoutSeconds>30</TimeoutSeconds>
        </DataSource>
      </Discovery>
    </Discoveries>
  </Monitoring>
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="SCOMskills.Demo.TimedScript.Discovery">
          <Name>SCOMskills Timed Script Discovery</Name>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>

Microsoft Windows Application Component class type

AKA: Microsoft.Windows.ApplicationComponent

Here is another post in the VSAE Fragment category, building on the Microsoft Windows Local Application class in the previous example. This is another common class type to use while authoring a custom management pack, as discuss here. Use this class to discover components hosted on your custom Local Application class. This could also be considered a type (or server role) of the hosting class.

What does it do?

Discovers a component (or type/role) that is hosted by the seed class discovered in the previous example. You will notice here the highlighted hosting relationship also needs to be discovered, since this isn’t automatically included in the Microsoft Windows Application Component class type.  Monitors targeting this class will not automatically rollup to the Windows computer, so long as you do not create a dependency or hosting relationship on local application. This class requires a relationship with it’s base class, which is typically the seed class. If the seed class is local application, then it will automatically rollup to windows computer, and in some cases this is not what you want. In many cases, you might want to target your base class, which would be targeting Windows operating system (more on that later – look at the best practices category).

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TypeDefinitions>
<EntityTypes>
<ClassTypes>
<ClassType ID="SCOMskills.Demo.ApplicationComponent.Class" Abstract="false" Accessibility="Public" Base="Windows!Microsoft.Windows.ApplicationComponent" Hosted="true" Singleton="false">
<Property ID="CsvField1" Key="false" Type="string"/>
<Property ID="CsvField2" Key="false" Type="string"/>
</ClassType>
</ClassTypes>
<RelationshipTypes>
<RelationshipType ID="LocalApplicationHostsApplicationComponent" Base="System!System.Hosting" Accessibility="Public">
<Source ID="LocalApplication" Type="SCOMskills.Demo.LocalApplication.Class"/>
<Target ID="ApplicationComponent" Type="SCOMskills.Demo.ApplicationComponent.Class"/>
</RelationshipType>

</RelationshipTypes>
</EntityTypes>
</TypeDefinitions>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<DisplayStrings>
<DisplayString ElementID="SCOMskills.Demo.ApplicationComponent.Class">
<Name>SCOMskills Application Component Class</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.ApplicationComponent.Class" SubElementID="CsvField1">
<Name>CSV Field 1</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.ApplicationComponent.Class" SubElementID="CsvField2">
<Name>CSV Field 2</Name>
</DisplayString>
<DisplayString ElementID="LocalApplicationHostsApplicationComponent">
<Name>Local Application class hosts Application Component class</Name>
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>

Filtered Registry Discovery Provider

AKA: Microsoft.Windows.FilteredRegistryDiscoveryProvider

This is the third post in the new VSAE Fragment category. The provider is used to discover a class type and its properties by reading keys and values from the Windows registry.

What does it do?

This particular fragment can be used to discover the class type discuss here. It demonstrates how to discover different types of properties: Integer, String, and Double (or decimal).

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Monitoring>
<Discoveries>
<Discovery ID="SCOMskills.Demo.FilteredRegistry.Discovery" Target="Windows!Microsoft.Windows.Computer" Remotable="true" Enabled="true">
<Category>Discovery</Category>
<DiscoveryTypes>
<DiscoveryClass TypeID="SCOMskills.Demo.FilteredRegistry.Class">
<Property PropertyID="IntegerProperty"/>
<Property PropertyID="StringProperty"/>
<Property PropertyID="DoubleProperty"/>
<Property TypeID="System!System.Entity" PropertyID="DisplayName"/>
</DiscoveryClass>
</DiscoveryTypes>
<DataSource ID="DS" TypeID="Windows!Microsoft.Windows.FilteredRegistryDiscoveryProvider">
<ComputerName>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/NetworkName$</ComputerName>
<RegistryAttributeDefinitions>
<!--===============================================================================-->
<!--Define registry keys, values, and types. -->
<!--Path and attribute types: http://msdn.microsoft.com/en-us/library/jj130492.aspx-->
<!--===============================================================================-->
<RegistryAttributeDefinition>
<AttributeName>SCOMskillsKeyExists</AttributeName>
<Path>SOFTWARE\SCOMskills</Path>
<PathType>0</PathType>
<AttributeType>0</AttributeType>
</RegistryAttributeDefinition>
<RegistryAttributeDefinition>
<AttributeName>IntegerValue</AttributeName>
<Path>SOFTWARE\SCOMskills\Integer</Path>
<PathType>1</PathType>
<!--reg_dword, any integer value-->
<AttributeType>2</AttributeType>
</RegistryAttributeDefinition>
<RegistryAttributeDefinition>
<AttributeName>StringValue</AttributeName>
<Path>SOFTWARE\SCOMskills\String</Path>
<PathType>1</PathType>
<!--reg string-->
<AttributeType>1</AttributeType>
</RegistryAttributeDefinition>
<RegistryAttributeDefinition>
<AttributeName>DoubleValue</AttributeName>
<Path>SOFTWARE\SCOMskills\Double</Path>
<PathType>1</PathType>
<!--reg string, value can be a double or integer-->
<AttributeType>3</AttributeType>
</RegistryAttributeDefinition>
</RegistryAttributeDefinitions>
<Frequency>60</Frequency>
<ClassId>$MPElement[Name="SCOMskills.Demo.FilteredRegistry.Class"]$</ClassId>
<!--========================================-->
<!--Map registry filters to class properties-->
<!--========================================-->
<InstanceSettings>
<Settings>
<Setting>
<Name>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Name>
<Value>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/PrincipalName$</Value>
</Setting>
<Setting>
<Name>$MPElement[Name="SCOMskills.Demo.FilteredRegistry.Class"]/IntegerProperty$</Name>
<Value>$Data/Values/IntegerValue$</Value>
</Setting>
<Setting>
<Name>$MPElement[Name="SCOMskills.Demo.FilteredRegistry.Class"]/StringProperty$</Name>
<Value>$Data/Values/StringValue$</Value>
</Setting>
<Setting>
<Name>$MPElement[Name="SCOMskills.Demo.FilteredRegistry.Class"]/DoubleProperty$</Name>
<Value>$Data/Values/DoubleValue$</Value>
</Setting>
<Setting>
<Name>$MPElement[Name="System!System.Entity"]/DisplayName$</Name>
<Value>$Target/Property[Type="Windows!Microsoft.Windows.Computer"]/NetbiosComputerName$</Value>
</Setting>
</Settings>
</InstanceSettings>
<!--=========================================================-->
<!--Discover class instance if SCOMskills registry key exists-->
<!--=========================================================-->
<Expression>
<SimpleExpression>
<ValueExpression>
<XPathQuery Type="Boolean">Values/SCOMskillsKeyExists</XPathQuery>
</ValueExpression>
<Operator>Equal</Operator>
<ValueExpression>
<Value Type="Boolean">true</Value>
</ValueExpression>
</SimpleExpression>
</Expression>
</DataSource>
</Discovery>
</Discoveries>
</Monitoring>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<!--============================================================-->
<!--Provide friendly display names for elements in this fragment-->
<!--============================================================-->
<DisplayStrings>
<DisplayString ElementID="SCOMskills.Demo.FilteredRegistry.Discovery">
<Name>Filtered registry class discovery.</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.FilteredRegistry.Discovery" SubElementID="DS">
<Name>Filtered registry discovery data source</Name>
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>

Microsoft Windows Local Application class type

AKA: Microsoft.Windows.LocalApplication

This is the second post in the new VSAE Fragment category. The purpose of these types of posts is to provide a practical demonstration of how to create your own fragment library. I find this useful in management pack authoring, because it’s much easier to add and modify an existing fragment than to create elements and modules from scratch each time.

What does it do?

This may be the most common base classes used for a seed class. It’s easy to implement, because it doesn’t require you to create a hosting relationships. This base class *will* rollup to the windows computer class, as a local application. So, do not choose this as a base if you do not want your application to rollup to windows computer! This is considered a concrete class, and rolls up to windows computer.

Go here for a fragment that will discover this class type using the filtered registry discovery module.

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TypeDefinitions>
<EntityTypes>
<ClassTypes>
<!--=======================================-->
<!--Define application class and properties-->
<!--=======================================-->
<ClassType ID="SCOMskills.Demo.FilteredRegistry.Class" Abstract="false" Accessibility="Public" Base="Windows!Microsoft.Windows.LocalApplication" Hosted="true" Singleton="false">
<Property ID="IntegerProperty" Key="false" Type="int" />
<Property ID="StringProperty" Key="false" Type="string" />
<Property ID="DoubleProperty" Key="false" Type="double" />
</ClassType>
</ClassTypes>
</EntityTypes>
</TypeDefinitions>
<LanguagePacks>
<LanguagePack ID="ENU" IsDefault="true">
<!--============================================================-->
<!--Provide friendly display names for elements in this fragment-->
<!--============================================================-->
<DisplayStrings>
<DisplayString ElementID="SCOMskills.Demo.FilteredRegistry.Class">
<Name>SCOMskills Demo Filtered Registry Class</Name>
<Description>Demonstration of Microsoft.Windows.FilteredRegistryDiscoveryProvider</Description>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.FilteredRegistry.Class" SubElementID="IntegerProperty">
<Name>Integer Property</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.FilteredRegistry.Class" SubElementID="StringProperty">
<Name>String Property</Name>
</DisplayString>
<DisplayString ElementID="SCOMskills.Demo.FilteredRegistry.Class" SubElementID="DoubleProperty">
<Name>Double Property</Name>
</DisplayString>
</DisplayStrings>
</LanguagePack>
</LanguagePacks>
</ManagementPackFragment>

Visual Studio Authoring Extensions – Fragments, fragments, fragments!

All management pack authoring I do is by using fragments. In my opinion, fragments are the most flexible authoring element in the VSAE, as they offer full visibility into the XML code and the ability to create a library of useful fragments that can later be reused in other management packs.

I’m kicking off a new blog category named VSAE Fragment. The purpose of these types of posts is to give practical examples of using modules that are defined in OpsMgr management pack libraries, as well as how to piece together custom modules for composite workflows. MSDN already has a library of module examples here, but sometimes I find myself searching to fill in some blanks. Hopefully this will help others as much as they help me.

I’m starting the VSAE Fragment category by providing the most basic, and essential fragment I use in every new management pack: the mp.DisplayName fragment.

What does it do?

Provides a friendly management pack display name and description in the Operations console.

 

<ManagementPackFragment SchemaVersion="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <LanguagePacks>
    <LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        <DisplayString ElementID="SCOMskills.Demo">
          <Name>SCOMskills Demo</Name>
          <Description>A demo management pack.</Description>
        </DisplayString>
      </DisplayStrings>
    </LanguagePack>
  </LanguagePacks>
</ManagementPackFragment>