• Jakub@Work

    Group Targeting Does Not Work

    • 1 Comments

    It seems like this question gets asked a lot on the newsgroups and oftentimes results in an incorrect answer being posted, so I thought I would clarify here. The underlying problem is that you want your rule to run on computers in a particular group. The simple answer that is often given (but is wrong) is to target your rule to the group (i.e. the class that is defined for that group, since a new class gets created for every group). This will simply not work the way the answer implies; what this will do is run the rule only against the single group instance of that group class (there is only one instance of this class because it is a singleton). This rule will then run on the root management server and only for one instance.

    The correct way to do this is to first target your rule to whatever makes sense; if it is a rule that is meant to run for SQL, it should be targeted to the appropriate SQL class, and so on. Next, you probably want to have your rule be disabled (i.e. Enabled="false") to start, otherwise it will immediately run on all agents where an instance of your targeted class is discovered. Finally, you will want to override your rule and set Enabled="true" with the context being your group instance (type should work also, but group instance is a much more precise definition of the override). You can also invert the process to disable a rule for a particular group, while having it run by default.

  • Jakub@Work

    Monitoring Microsoft

    • 0 Comments

    A blog about using SCOM in Microsoft IT. Thought I would share it here as a great resource on large scale enterprise use of SCOM.

  • Jakub@Work

    Tiering Management Groups in SCOM 2007

    • 0 Comments

    In MOM 2005 tiering was achieved using the Mom to Mom Connector which physically copied data from lower tiers to upper tiers. While this generally worked, it certainly posed problems around performance as well as keeping the items in sync. The main scenarios this was meant to solve were:

    1. A single console for viewing data from multiple tiers
    2. No connectivity requirement from console to lower tiers

    In SCOM 2007 the approach we took for tiering was different, although accomplishes the same goals. As opposed to copying the data from tier to tier, instead we allow the upper tier console to connect and interact with the lower tiers directly, without requiring direct access to those management groups. The way we accomplish this is by connecting to those management groups via the local management group that console is connecting to.

    In order to use this feature, the first thing required is actually setting up the tiered connection from the local management group to the management group you want to connect to. This can be accomplished in the UI via the "Connected Management Groups" settings in the Administration pane, or via the SDK. Via the SDK, you would start by retrieving the TieringAdministration class off of an instance of ManagementGroup using:

    public TieringAdministration GetTieringAdministration()

    Once you have this object there are two overloaded methods for creating a TieredManagementGroup.

    public TieredManagementGroup AddMonitoringTier(string name, ManagementGroupConnectionSettings tieredConnectionSettings)
    public TieredManagementGroup AddMonitoringTier(string name, ManagementGroupConnectionSettings tieredConnectionSettings, 
     MonitoringSecureData runAs, bool availableForConnectors)

    The first gives the tiered connection a name and specifies the connection settings to use to connect to that tier; the ServerName, UserName, Domain and Password are required on the ManagementGroupConnectionSettings object. The second overload is actually much more convenient when later using the TieredManagementGroup. If you specify a RunAs account here, you can actually have the system use that account to connect instead of having to provide the username and password each time. If you specify availableForConnectors to true, the TieringAdministration method GetMonitoringTiersForConnector() will return this tier and subsequently all "tiered' MCF overloads will include this tier in their processing. You do not actually have to make the tier available for connectors to use the RunAs feature, but most likely those go together. Also note that when requesting the stored account be used to connect, the caller must be a SCOM Administrator.

    Now, in order to actually connect to the tier, you first need to retrieve the TieredManagementGroup for the tier you want to connect to. There are a few methods on TieringAdministration that allow you to do this:

    public ReadOnlyCollection<TieredManagementGroup> GetMonitoringTiers()
    public TieredManagementGroup GetMonitoringTier(Guid id)
    public TieredManagementGroup GetMonitoringTier(string name)
    public ReadOnlyCollection<TieredManagementGroup> GetMonitoringTiersForConnectors()

    Once you have the TieredManagementGroup you want to connect to you simple call the following method on that instance:

    public ManagementGroup Connect(TieredManagementGroupConnectionSettings tieredConnectionSettings)

    The TieredManagementGroupConnectionSettings are very similar to the regular ManagementGroupConnectionSettings with a single addition ConnectForConnector property. If this property is true, than the SDK service of the local tier will use the associated RunAs account to connect to the lower tier, requiring the caller to be admin. If this is false, credentials must be provided. For security reasons, we do not use the SDK Service account to connect to the lower tier.

    Once you call the Connect method above, you get a ManagementGroup back that you can work with just like a local ManagementGroup instance, only this one is connected to a different tier via the local one.

    Largely, this is the recommended approach for interacting with different tiers as it gives you full control over handling errors in each when aggregating data across multiple tiers, however, we do provider some "tiered" methods specifically for the SCOM Connector Framework. If you look at the MonitoringConnector class found in the Microsoft.EnterpriseManagement.ConnectorFramework namespace, it has several methods names with a "ForTiers" suffix. The behavior of each is roughly the same, so I won't go over all of them, instead we'll just look at one sample:

    public ReadOnlyCollection<ConnectorMonitoringAlert> GetMonitoringAlertsForTiers(out IList<ConnectorTieredOperationFailure> failures)

    The non-tiered version of this method gets all alerts for the given connector from the local management group based on the bookmark of the connector. This method does the same, only it does it for all the tiers that have been created as "Available for Connectors" as mentioned earlier. The implementation is nothing special in that it simply uses the aforementioned tiering method to retrieved tiers, connect to them and call GetMonitoringAlerts on each, subsequently aggregating the results. Exceptions will be thrown if there are connectivity problems to the local tier, otherwise errors are captured in the out parameter.

  • Jakub@Work

    Submitting Tasks with Additional Configuration

    • 0 Comments

    I had a post earlier about submitting tasks, but I did not discuss how to specify additional configuration when submitting these tasks at that time. The two things that are configurable for tasks are credentials and overrides. Credentials allow you to change what user the task is executed as, while overrides allow you to change some of the configuration parameters that are defined as overrideable for the task.

    To determine what is overrideable on a particular task, you have to look at the task definition to find the module type the task in implementing. In the attached sample management pack, the module type used by the task, System.Health.ResetState, is System.Health.SetTargetStateAction. When you look at that module type in the sample management pack, you will notice it has a single overrideable parameter called MonitorId which is defined as a string.

    In order to provide the override to the task, you must specify, per overrideable parameter, the new value you would like to use within the MonitoringTaskConfiguration object. If the module type of the task has no overrideable parameters, you can't override anything. The below sample along with the attached management pack shows how to do this in the SDK.

    Note: I pulled this sample out of one of our existing management packs that are internal to SCOM. This task can be executed indirectly by calling the various ResetMonitoringState methods in the SDK. Also note that the sample will not actually "work" in the sense that the credentials are bogus and the monitor id I am passing in is also bogus, but it should illustrate how to do this process legitimately for other tasks.

    using System; using System.Collections.ObjectModel; using Microsoft.EnterpriseManagement; using Microsoft.EnterpriseManagement.Configuration; using Microsoft.EnterpriseManagement.Monitoring; namespace Jakub_WorkSamples { partial class Program { static void RunningTasksWithConfiguration() { // Connect to the local management group ManagementGroup mg = new ManagementGroup("localhost"); // First lets get the task we are going to execute // Note: Indexing the result like this is not necessarily the best // practice here, since there could be more than one task with this // name. MonitoringTaskCriteria taskCriteria = new MonitoringTaskCriteria( "Name = 'System.Health.ResetState'"); MonitoringTask resetStateTask = mg.GetMonitoringTasks(taskCriteria)[0]; // Configuration allows you to specify credentials to use different // from the default. It also allows you to specify overrides, if // any are available MonitoringTaskConfiguration resetStateTaskConfiguration = new MonitoringTaskConfiguration(); // Specifying credentials SecureString password = new SecureString(); password.AppendChar('p'); password.AppendChar('a'); password.AppendChar('s'); password.AppendChar('s'); password.AppendChar('w'); password.AppendChar('o'); password.AppendChar('r'); password.AppendChar('d'); resetStateTaskConfiguration.Credentials = new WindowsMonitoringTaskCredentials("Domain", "UserName", password); // Specifying the override // First we need to get the overrideable parameter that is defined // on the module type that the task uses. ManagementPackModuleType setTargetStateWriteAction = mg.GetMonitoringModuleTypes("System.Health.SetTargetStateAction")[0]; // This allows us to override the monitor id ManagementPackOverrideableParameter overrideParam = setTargetStateWriteAction.OverrideableParameterCollection[0]; // Set the value of the parameter as a string. In this case it's a Guid. resetStateTaskConfiguration.Overrides.Add( new Pair<ManagementPackOverrideableParameter, string>(overrideParam, Guid.NewGuid().ToString("B"))); // Now we need to get an instance to execute this task on. Since this task // is targeted to entity, any instance will do. MonitoringClass healthServiceClass = mg.GetMonitoringClass(SystemMonitoringClass.HealthService); // Next get the object (we'll just pick the first one) MonitoringObject healthServiceObject = mg.GetMonitoringObjects(healthServiceClass)[0]; // Now we execute the task. ReadOnlyCollection<MonitoringTaskResult> results = healthServiceObject.ExecuteMonitoringTask(resetStateTask, resetStateTaskConfiguration); } } }
  • Jakub@Work

    More on Group Membership and Calculations

    • 22 Comments

    A coworker of mine, Joel Pothering, put together a great article on groups in SCOM. I am posting it here on his behalf:

    I wanted to post a message that gives more details on how to author group membership rules. I’ll start with a review of Jakub Oleksy’s blog entry, found here:

    http://blogs.msdn.com/jakuboleksy/archive/2006/11/15/creating-and-updating-groups.aspx

    Jakub’s example shows how you can use the SDK to create a group with a corresponding membership rule, and then update it. I’ll concentrate on the different types of membership rules (or formulas) that you can create beyond what this example shows. You should still use Jakub’s example as a guide to create your own – in short, we’ll just modify the formula variable in his example.

    Simple group membership rule

    Here’s the first membership rule in Jakub’s example:

    <MembershipRule>

      <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass>

      <RelationshipClass>$MPElement[Name="InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>

    </MembershipRule>

    This is the simplest form of a group membership rule. It groups every instance (MonitoringObject) of the MonitoringClass type specified in the MonitoringClass element. What this really means is that the system will create a new instance of a relationship (MonitoringRelationshipObject) of the type specified in the RelationshipClass element for every instance of MonitoringClass. The source of the relationship is the group and the target is the MonitoringClass instance.

    Group membership rules are therefore discovery rules that “discover” relationships between a group and the instances you want it to contain. So you’re not discovering entities in your enterprise, but relationships between entities you already discovered. The target of this discovery rule is the group type itself. The data source module for the rule is Microsoft.SystemCenter.GroupPopulator, defined in the SystemCenter management pack. Let’s refer to this entire group membership rule system as simply GroupCalc.

    One other note about this simple membership rule: use this if you want to group another group, that is, create a subgroup. Simply specify the subgroup’s type in the MonitoringClass element, and its singleton instance will become a member.

    Using an expression to filter members

    You probably want to be more specific about what instances you want your group to contain beyond just their type. You can filter instances using an expression in the membership rule.  What we’ll do is add an Expression element after RelationshipClass to filter on computers with an IsVirtualMachine property value of ‘false’. The membership rule now becomes:

    <MembershipRule>

      <MonitoringClass>$MPElement[Name="Windows!Microsoft.Windows.Computer"]$</MonitoringClass>

      <RelationshipClass>$MPElement[Name="InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>

      <Expression>

        <SimpleExpression>

          <ValueExpression>

            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/IsVirtualMachine$</Property>

          </ValueExpression>

          <Operator>Equal</Operator>

          <ValueExpression>

            <Value>False</Value>

          </ValueExpression>

        </SimpleExpression>

      </Expression>

    </MembershipRule>

    The SimpleExpression element defines a left and right operand, and an operator, and GroupCalc evaluates this for every instance of computer. Here’s a simplified representation of this predicate:

    Computer.IsVirtualMachine = ‘False’

    The left operand is a Property element and therefore references a property of MonitoringClass. The right operand is a Value element, which can be a string or a number. In this case it is a string that represents a boolean value. GroupCalc knows that since IsVirtualMachine is a boolean property, ‘False’ should be converted to a boolean value of 0.

    You can apply logical operators on each predicate defined by a SimpleExpression. For example, let’s change the membership rule to filter based on this simplified representation:

    Computer.IsVirtualMachine = ‘False’ AND ( Computer.NetbiosDomainName LIKE ‘EASTCOAST’ OR Computer.NetbiosDomainName LIKE ‘WESTCOAST’ )

    The expression in the membership rule becomes:

      <Expression>

        <And>

          <Expression>

            <SimpleExpression>

              <ValueExpression>

                <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/IsVirtualMachine$</Property>

              </ValueExpression>

              <Operator>Equal</Operator>

              <ValueExpression>

                <Value>False</Value>

              </ValueExpression>

            </SimpleExpression>

          </Expression>

          <Expression>

            <Or>

              <Expression>

                <SimpleExpression>

                  <ValueExpression>

                    <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

                  </ValueExpression>

                  <Operator>Like</Operator>

                  <ValueExpression>

                    <Value>EASTCOAST</Value>

                  </ValueExpression>

                </SimpleExpression>

              </Expression>

              <Expression>

                <SimpleExpression>

                  <ValueExpression>

                    <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

                  </ValueExpression>

                  <Operator>Like</Operator>

                  <ValueExpression>

                    <Value>WESTCOAST</Value>

                  </ValueExpression>

                </SimpleExpression>

              </Expression>

            </Or>

          </Expression>

        </And>

      </Expression>

    The left side of And has an Or expression, and the parenthesis we required above is implied. The two SimpleExpressions in Or use the Like operator -- similar to T-SQL’s LIKE -- to do a case insensitive comparison.

    To see what other operators we can use with SimpleExpression, let’s examine parts of GroupCalc’s configuration schema, Microsoft.SystemCenter.GroupPopulationSchema, located in the SystemCenter management pack. Here is the definition of SimpleExpression:

      <xsd:complexType name="SimpleCriteriaType">

        <xsd:sequence maxOccurs="1" minOccurs="0">

          <xsd:annotation>

            <xsd:documentation>Expression comparing two values.</xsd:documentation>

          </xsd:annotation>

          <xsd:element name="ValueExpression" type="ValueExpressionType"/>

          <xsd:element name="Operator" type="CriteriaCompareType"/>

          <xsd:element name="ValueExpression" type="ValueExpressionType"/>

        </xsd:sequence>

      </xsd:complexType>

    Here is the definition of CriteriaCompareType:

      <xsd:simpleType name="CriteriaCompareType">

          <xsd:restriction base="xsd:string">

              <xsd:enumeration value="Like">

                  <xsd:annotation>

                      <xsd:documentation>LIKE</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="NotLike">

                  <xsd:annotation>

                      <xsd:documentation>NOT LIKE</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="Equal">

                  <xsd:annotation>

                      <xsd:documentation>Equal to.</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="NotEqual">

                  <xsd:annotation>

                      <xsd:documentation>Not equal to.</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="Greater">

                  <xsd:annotation>

                      <xsd:documentation>Greater than.</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="Less">

                  <xsd:annotation>

                      <xsd:documentation>Less than.</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="GreaterEqual">

                  <xsd:annotation>

                      <xsd:documentation>Greator than or equal to.</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

              <xsd:enumeration value="LessEqual">

                  <xsd:annotation>

                      <xsd:documentation>Less than or equal to.</xsd:documentation>

                  </xsd:annotation>

              </xsd:enumeration>

          </xsd:restriction>

      </xsd:simpleType>

    These operators should cover everything you need to do in a SimpleExpression.

    Now take a look at ExpressionType in GroupPopulationSchema to see what other expressions we can use:

    <xsd:complexType name="ExpressionType">

      <xsd:choice>

        <xsd:element name="SimpleExpression" type="SimpleCriteriaType"/>

        <xsd:element name="UnaryExpression" type="UnaryCriteriaType"/>

        <xsd:element name="RegExExpression" type="RegExCriteriaType"/>

        <xsd:element name="Contains" type="ContainsCriteriaType"/>

        <xsd:element name="NotContains" type="ContainsCriteriaType"/>

        <xsd:element name="Contained" type="ContainedCriteriaType"/>

        <xsd:element name="NotContained" type="ContainedCriteriaType"/>

        <xsd:element name="And" type="AndType"/>

        <xsd:element name="Or" type="OrType"/>

      </xsd:choice>

    </xsd:complexType>

    We already used SimpleExpression, And, and Or. The UnaryExpression is used for testing null-ness. For example, to test whether a value for Computer.NetbiosDomainName was never discovered (i.e. it is NULL), you can use this expression:

      <Expression>

        <UnaryExpression>

          <ValueExpression>

            <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

          </ValueExpression>

          <Operator>IsNull</Operator>

        </UnaryExpression>

      </Expression>

    RegExExpression allows you to use regular expression syntax to test property values. This is similar to what you use in other types of rules in Operations Manager that require regular expressions. Here’s one example that tests the value of NetbiosDomainName for the pattern ‘^WEST’:

      <Expression>

          <RegExExpression>

              <ValueExpression>

                  <Property>$MPElement[Name="Windows!Microsoft.Windows.Computer"]/NetbiosDomainName$</Property>

              </ValueExpression>

              <Operator>MatchesRegularExpression</Operator>

              <Pattern>^WEST</Pattern>

          </RegExExpression>

      </Expression>

    Containment expressions

    Contains allows you to group based on what instances MonitoringClass contains, and Contained on what instances contain MonitoringClass. These are powerful expressions that look beyond attributes of MonitoringClass and allow you to query to see how MonitoringClass is related to other instances.

    Let’s look at the SystemCenter management pack again and find an example of a group membership rule that uses Contains. The following is the membership rule for the internal group we use that contains all agent managed computers:

      <MembershipRule>

          <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass>

          <RelationshipClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ComputerGroupContainsComputer"]$</RelationshipClass>

          <Expression>

              <Contains>

                  <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.Agent"]$</MonitoringClass>

              </Contains>

          </Expression>

      </MembershipRule>

    An agent managed computer is represented in the system as a ManagedComputer instance that hosts an Agent instance. Hosting is a special type of containment that requires the host instance exists before the hosted instance can exist.

    Contains also takes an Expression element. Here’s an example from the SystemCenter management pack that groups all management servers that are not the root management server:

      <Expression>

          <Contains>

              <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.CollectionManagementServer"]$</MonitoringClass>

              <Expression>

                  <SimpleExpression>

                      <ValueExpression>

                          <Property>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.HealthService"]/IsRHS$</Property>

                      </ValueExpression>

                      <Operator>Equal</Operator>

                      <ValueExpression>

                          <Value>False</Value>

                      </ValueExpression>

                  </SimpleExpression>

              </Expression>

          </Contains>

      </Expression>

    A management server is a ManagedComputer that contains (hosts) a CollectionManagementServer, which derives from HealthService. A SimpleExpression is used to test the HealthService property IsRHS.  Notice that we now reference properties on the contained MonitoringClass, not the MonitoringClass that we are grouping.

    Any of the previously mentioned expression types can be used here, including any type of containment expression.

    Both Contains and Contained have compliments, NotContains and NotContained. For example, agentless managed computers (or remotely managed) are computers that are monitored, but do not host a health service (they aren’t agents). Let’s say we want to create a membership rule to group these. One way you can do this is group all instances of ManagedComputer that are not a member -- NotContained by -- the agent managed computer group. Here’s what the membership rule would look like:

    <MembershipRule>

      <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass>

      <RelationshipClass>$MPElement[Name="InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities"]$</RelationshipClass>

      <Expression>

        <NotContained>

          <MonitoringClass>SCLibrary!Microsoft.SystemCenter.AgentManagedComputerGroup</MonitoringClass>

        </NotContained>

      </Expression>

    </MembershipRule>

    Multiple membership rules

    You can define multiple membership rules for a single group. Each MembershipRule element is actually independent of the other. It is used primarily to create heterogeneous groups.

    Relationship source/target types and inheritance

    It’s worth reviewing how inheritance works with GroupCalc, and how you choose the RelationshipClass for your membership rule. Let’s take another look at the membership rule from the SystemCenter management pack for grouping management servers that are not the root management server:

    <MembershipRule>

        <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ManagedComputer"]$</MonitoringClass>

        <RelationshipClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.ComputerGroupContainsComputer"]$</RelationshipClass>

        <Expression>

            <Contains maxDepth="1">

                <MonitoringClass>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.CollectionManagementServer"]$</MonitoringClass>

                <Expression>

                    <SimpleExpression>

                        <ValueExpression>

                            <Property>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.HealthService"]/IsRHS$</Property>

                        </ValueExpression>

                        <Operator>Equal</Operator>

                        <ValueExpression>

                            <Value>False</Value>

                        </ValueExpression>

                    </SimpleExpression>

                </Expression>

            </Contains>

        </Expression>

    </MembershipRule>

    The group (not shown above) is the singleton type Microsoft.SystemCenter.CollectionManagementServerComputersGroup, which derives from Microsoft.SystemCenter.ComputerGroup, an abstract type. The type we want to group is Microsoft.SystemCenter.ManagedComputer, which derives from Microsoft.Windows.Computer. Now, we specified the relationship type Microsoft.SystemCenter.ComputerGroupContainsComputer in the RelationshipClass element, which means GroupCalc creates instances of that type of relationship to group members. What we need to be sure of is that this is a valid relationship type to use. Here’s the definition of ComputerGroupContainsComputer:

    <RelationshipType ID="Microsoft.SystemCenter.ComputerGroupContainsComputer Abstract="false" Base="System!System.Containment">

        <Source>Microsoft.SystemCenter.ComputerGroup</Source>

        <Target>System!System.Computer</Target>

    </RelationshipType>

    Look at the Source and Target types. What these tell us is that the group has to be of a type derived from the abstract type Microsoft.SystemCenter.ComputerGroup, and the instance we’re grouping has to derive from System.Computer, another abstract type. It looks like our group matches -- CollectionManagementServerComputersGroup derives from ComputerGroup. And since Windows.Computer derives from System.Computer, and ManagedComputer derives from Windows.Computer, our grouped instances can be part of this relationship too – all is good.

    What happens if we pick the wrong the relationship type? GroupCalc will reject the configuration for the rule, and you’ll see an error in the event log from the runtime. An example of an incorrect relationship type would be the one used in the first example we discussed, Microsoft.SystemCenter.InstanceGroupContainsEntities. Here’s its definition:

    <RelationshipType ID="Microsoft.SystemCenter.InstanceGroupContainsEntities" Base="System!System.Containment" Abstract="false"

      <Source>Microsoft.SystemCenter.InstanceGroup</Source>

      <Target>System!System.Entity</Target>

    </RelationshipType>

    The Source is Microsoft.SystemCenter.InstanceGroup which is a singleton group type that derives directly from the abstract type System.Group – this doesn’t look good. The Target type is the base type of everything, so we’re definitely okay there. So it’s the Source type that makes InstanceGroupContainsEntities invalid with the management server membership rule, because CollectionManagementServerComputersGroup doesn’t derive from InstanceGroup.

    The reason you have to be careful is that our verification process for importing management packs will not catch this. We don’t have that level of knowledge, or semantic checks, available in our verification process to help us here. Only GroupCalc has this check in its verification process.

    Referenced properties

    I have one more note on the membership rule for Microsoft.SystemCenter.CollectionManagementServerComputersGroup. Notice that we are grouping CollectionManagementServer instances, as specified in the MonitoringClass element. In the expression, we reference a property on HealthService, which is a base type of CollectionManagementServer. We do this because you need to specify the type that defines the property. So you cannot do this:

    <ValueExpression>

         <Property>$MPElement[Name="SCLibrary!Microsoft.SystemCenter.CollectionManagementServer"]/IsRHS$</Property> // WRONG!!!

    </ValueExpression>

    This will produce an error from our management pack verification process since there is no such property, which is good – we’ll know right away, before we can even import, that this is incorrect.

    What we won’t know right away, and what you need to be careful of, are property references that can pass management pack verification but still can be rejected by GroupCalc. For example, if we were to put in a Windows.Computer property there, it would pass management pack verification – the property is valid after all. But our GroupCalc schema implies that the property referenced must be from the type you’re grouping, or from any of its base types. Again, only GroupCalc has this knowledge, and you’ll get a runtime error logged after GroupCalc rejects the configuration.

    Conclusion

    GroupCalc can help you in most of your scenarios that involve grouping instances. I hope you get a chance to investigate GroupCalc more thoroughly. If you do and have questions, please don’t hesitate to post these questions to the newsgroup – we’ll be sure to help.

    Thanks - Joel

  • Jakub@Work

    Paternity Leave

    • 3 Comments

    I am starting my 1 month paternity leave today and as such won't be posting much here, although I will still try to reply to questions as they come in.

  • Jakub@Work

    SDK Documentation Posted to MSDN

    • 0 Comments

    There are still some issues that need to be addressed, but the docs are up:

    http://msdn2.microsoft.com/en-us/library/bb437575.aspx

  • Jakub@Work

    Working with Management Pack Templates

    • 9 Comments

    Management pack templates provide a powerful way to create single or a collection of management pack objects. Essentially, you author the fragment of a management pack you want created, with some missing values that become configuration to your template, and upon execution of the template the missing values are provided and the finished management pack object(s) are materialized and imported.

    Templates show up in the UI under the Authoring pane. In order for your own custom template to show up there, you need to create a folder for it and place the template in the folder as a folder item. It will show up without the folder, but won't work quite right. Enabling executing the template via the UI is outside the scope of this post, but should be eventually available as a tutorial on www.authormps.com.

    I've attached a sample management pack that essentially recreates the instance group template we use for creating groups.

    If you take a look at the template defined there, you will see that the configuration section is very similar to other management pack elements. The configuration schema here specifies what values must be provided to the template processor.

    The second section is the references for the actual template. The references refer to the references section of the management pack the template is in, by alias. There is one special alias defined, 'Self', which refers to the management pack the template is in.

    Finally, under the implementation section is the fragment of the management pack you want your template to create. You can have the configuration values replace any part of the implementation by referring to the values via the $TemplateConfig/<configuration variable name>$ format.

    In the templates management pack you will also notice that I put the sample templates in a newly created folder. This will ensure the UI behaves properly with any template output I produce. What happens is that the template output can be placed in a folder, and the UI will treat these folders as individual instances of execution of the template, such that they can be managed as homogenous units, even though they may have created a wide variety of management pack objects.

    The code below runs my template using the SDK.

    First you will notice that I need to get the management pack the template is in and the template itself. I need the management pack for two reasons. First, I need a management pack to run the template against; all the objects produced by the template will be placed in this management pack. Second, I need this particular management pack because it is not sealed and thus any templates defined in it, must be run against it. If you seal the management pack that contains the template, you can run it against any non-sealed management pack.

    Next, I have to build the configuration for my template. This is just XML that matches the schema of my template. You will also notice that within my configuration I have to referenced some management packs. This will be reflected by adding additional references as a parameter to processing the template. Note that if I want to use references that already exist in the management pack the template output will be put in, these aliases must match the already existing aliases for the same management packs.

    Finally, when I process my template, I provide additional information that will be used to name the folder the template output is put into. This is optional, but required if you want the output to show up in the UI and want to be able to delete it easily (by deleting everything in this folder). The method actually returns the folder the output was put in.

    using System; using System.Collections.ObjectModel; using System.Text; using System.Xml; using Microsoft.EnterpriseManagement; using Microsoft.EnterpriseManagement.Configuration; namespace Jakub_WorkSamples { partial class Program { static void ProcessTemplate() { // Connect to the local management group ManagementGroup localManagementGroup = new ManagementGroup("localhost"); // Get template management pack. This is where we will store out template output since // the sample template management pack is not sealed and the output needs to be // in the same management pack as the template in this case. ManagementPack templateManagementPack = localManagementGroup.GetManagementPacks( "Template.Sample")[0]; // Get the template you want to process MonitoringTemplate sampleTemplate = localManagementGroup.GetMonitoringTemplates( new MonitoringTemplateCriteria("Name = 'Sample.Template'"))[0]; // Populate the configuration for the template string formula = @"<MembershipRule> <MonitoringClass>$MPElement[Name=""Windows!Microsoft.Windows.Computer""]$</MonitoringClass> <RelationshipClass>$MPElement[Name=""InstanceGroup!Microsoft.SystemCenter.InstanceGroupContainsEntities""]$</RelationshipClass> </MembershipRule>"; StringBuilder stringBuilder = new StringBuilder(); XmlWriter configurationWriter = XmlWriter.Create(stringBuilder); configurationWriter.WriteStartElement("Configuration"); configurationWriter.WriteElementString("Namespace", "Sample.Namespace"); configurationWriter.WriteElementString("TypeName", "MyClass"); configurationWriter.WriteElementString("LocaleId", "ENU"); configurationWriter.WriteElementString("GroupDisplayName", "My Class"); configurationWriter.WriteElementString("GroupDescription", "My Class Description"); configurationWriter.WriteStartElement("MembershipRules"); configurationWriter.WriteRaw(formula); configurationWriter.WriteEndElement(); configurationWriter.WriteEndElement(); configurationWriter.Flush(); // Get the management packs for references ManagementPack windowsManagementPack = localManagementGroup. GetManagementPack(SystemManagementPack.Windows); ManagementPack instanceGroupManagementPack = localManagementGroup. GetManagementPack(SystemManagementPack.Group); ManagementPackReferenceCollection newReferences = new ManagementPackReferenceCollection(); newReferences.Add("Windows", windowsManagementPack); newReferences.Add("InstanceGroup", instanceGroupManagementPack); templateManagementPack.ProcessMonitoringTemplate(sampleTemplate, stringBuilder.ToString(), newReferences, "MyTemplateRunFolder", "My Template Run", "This is the folder for my sample template output"); } } }
  • Jakub@Work

    Security Auditing

    • 1 Comments

    The SDK service supports success and failure auditing for every method invoked on the service. For the most part, the event only includes the SDK operation (e.g. UserRole__Get), the user we validated against that operations, the actual SDK service method requesting the access check and the session id of the SDK client that this access check was performned for. Enabling auditing is straight forward as it leverages the object level auditing mechanism built in Windows 2003 Server:

    1. Open Local Security Policy, found under Administrative Tools
    2. Expand Local Policies and select Audit Policy
    3. Select "Audit object access"
    4. Right-click and Properties
    5. Enable "Success" and/or "Failure", depending on what you want to audit

    Shortly, all SDK methods should be audited to the security event long.

    Aside from the information mentioned above, we do audit more information for some operations.

    For tasks, we audit: JobId, TaskId, TargetObjectId, whether the task requires encryption, every override and value applied (if any) and username and domain if an alternate account was provided for execution.

    For ManagementPack change failures, including imports, updates and deletes, for security related failures we audit which management failed to be changedand what element triggered the failure. This can occur for users in an Author or Advanced Operator profile that try to perform management pack operations outside their class scope.

    Finally, we audit additional information for user role related changes.

  • Jakub@Work

    Exporting Management Packs

    • 8 Comments

    Customers have asked if you can see what is inside sealed management packs. The UI actually prevents this, but it is possible to do via the command shell. In both cases, you need to find the management pack you want to export through whatever method you prefer and then export it as below:

    Command Shell Example:

    Get-ManagementPack -Name "Microsoft.SystemCenter.2007" | Export-ManagementPack -Path "C:\"

    SDK Example:

    using System; using System.Collections.ObjectModel; using Microsoft.EnterpriseManagement; using Microsoft.EnterpriseManagement.Configuration; using Microsoft.EnterpriseManagement.Configuration.IO; namespace Jakub_WorkSamples { partial class Program { static void ExportManagementPack() { // Connect to the local management group ManagementGroup mg = new ManagementGroup("localhost"); // Get any management pack you want ManagementPack managementPack = mg.GetManagementPack("Microsoft.SystemCenter.2007", "31bf3856ad364e35", new Version("6.0.5000.0")); // Provide the directory you want the file created in ManagementPackXmlWriter xmlWriter = new ManagementPackXmlWriter(@"C:\"); xmlWriter.WriteManagementPack(managementPack); } } }

  • Jakub@Work

    MCF from non-Windows Clients

    • 7 Comments

    Note: This will only work in RTM

    In order to make our MCF web-service work from non-windows clients, the first step is to actually change the binding the MCF web-service uses. In theory, what we ship with (wsHttpBinding) should work cross-platform, but at the moment there are no non-Windows products that generate a proper proxy and thus make it difficult to use (See Update below). If at all possible, please use a windows based client to talk to MCF or a non-Windows based proxy that fully supports wsHttpBinding as the functionality is much richer, especially around errors. If you choose to proceed down this route, note that exceptions are not properly propagated (they all show up as generic fault exceptions) and it will be impossible to tell from the client what server-side errors occurred. If you have no choice, keep reading...

    If we switch the proxy to use basicHttpBinding, the service will act like an asmx web-service and everything should work cross-platform with existing web-service products. In order to actually use basicHttpBinding, however, the service will require some additional configuration. Ultimately, we need the caller to be presented as a windows account. For cross platform, since you can't use windows authentication, you are forced to use client certificates and map them accordingly. In order to use client certificates, however, you need to setup SSL (you can also use Message level security but I only setup and tested Transport level). Here are the steps:

     

    1. Create a server certificate to use for your MCF endpoint to enable SSL (this certificate will need to be trusted by your clients)

    2. Import this certificate into the Local Machine store on the Root Management Server

    3. Setup the MCF endpoint to use SSL

    Since we are self-hosted, this cannot be done in IIS. You will need to find HttpCfg.exe (for Win2K3 this is found under SupportTools on the CD) and run the following command:

    HttpCfg.exe set ssl -i 0.0.0.0:6000 -h 82e8471434ab1d57d4ecf5fbed0f1ceeba975d8d -n LOCAL_MACHINE -c MY -f 2
     
    The 6000 is the port you are using in the configuration file. The "82e8471434ab1d57d4ecf5fbed0f1ceeba975d8d " is the thumbprint of the certificate you want to use (this can be found under the Details tab of the certificate snap-in after viewing the certificate). The -f 2 enables the server to accept client certificates.

    4. Update Microsoft.Mom.Sdk.ServiceHost.exe.config to look like the attached file

    You can pick whatever port you want to use, although I was never able to get 80 to work in testing.

    Also note that when I tried this out generating the proxy using wsdl.exe from a machine other than the server itself, it failed when my endpoint was defined as localhost. I had to specify the server name in the endpoint definition for the tool to work.

    5. Restart the omsdk (OpsMgr Sdk Service) service

    6. Generate a client certificate

    There seem to be two ways to do this. The first, and the one I tried successfully, is to generate a client certificate that has, in the Subject Alternate Name field the principal name of the user you want to map to. This will work if the CA that issues the certificate is an Enterprise CA on the domain your OpsMgr Sdk Service is running on. In the details under the Subject Alternate Name field this looks something like this:

    Other Name:
    Principal Name=youruser@yourdomain

    Alternatively, AD allows for the configuration of certificate mapping directly on the respective user object. I did not try this method as I do not have domain admin access on the domain I was testing on, but this should work as well.

    7. Use the client certificate in the request

     

    I tested this out using a proxy generated by wsdl.exe from the 2.0 .Net Framework and everything seemed to work ok. Things didn't work well with the 1.1 Framework wsdl.exe as some of the normally non-nullable fields (such as DateTime fields) are not nullable in 1.1, but can be null from MCF. Thus, whatever tool you use to generate proxies, it needs to be able to handle null values for value types.

    Update: I did some digging around, and although I did not test any of these, it seems there are some java projects for WCF interop that should allow you to communicate directly with our wsHttpBinding. There is the JAX-WS project and Project Tango. There are probably more out there, but I was reading about this in particular and people having success using them to interop specifically with wsHttpBinding.

  • Jakub@Work

    SCOM 2007 Labs from MMS

    • 0 Comments

    MomResources.org, a great site run by one of our MVPs, has posted all the lab documents from MMS. These are a GREAT resource for everything that is SCOM 2007. Download them here.

  • Jakub@Work

    MMS Demo - Discovery and operational data insertion via SDK

    • 12 Comments

    I put together and presented a short demostration on the SDK at MMS on Monday. I think it went over well and was a short glimspe on some of the power of the SDK. I wanted to make my demo available so here it is. Let me know if there are any issues with it.

  • Jakub@Work

    Almost Done and MMS

    • 1 Comments

    We are almost done here, and a lot of folks are heading to MMS next week for the official launch of System Center Operations Manager 2007. I will be there next week so I probably won't post until I get back. I hope to see some of you there!

  • Jakub@Work

    Monitoring Objects

    • 11 Comments

    Monitoring objects are the instances that are discovered in SCOM. They are identified by unique values for key properties as defined by the class(es) that they are as well as the key properties of their host, if applicable. Their identity is solidified by the first non-abstract class in the objects class hierarchy, even though there may still exist a hierarchy of non-abstract classes that derive from this class. For instance, a computer is defined by its System.Computer class, even though it is also a System.Windows.Computer and potentially a MyCompany.SpecialComputer, both of which derive from System.Computer. It is important to note that these additional non-abstract classes do not in any way change the identity of the object itself and can be thought of as roles.

    Since a monitoring object is a representation of an instantiated class, it really represents a meta-object in a way. Early on, we were trying to figure out if we want strongly typed monitoring objects, such as Computer or Server, but for the most part decided against it, with a few exceptions to SCOM specific classes like ManagementServer. Given that, the MonitoringObject, in it's most basic form, is a property bag of values for all the class(es) properties of the object as well as the objects host(s).

    When we store monitoring objects in the database, they are stored in two places. We have a general store where common attributes of all monitoring objects are maintained, such as Name, Id and class list (what classes the object is) and a dynamically generated view per identifying class that gets populated with class values. As such, all SDK methods that return MonitoringObject must make at least two queries to construct the object. If the response is heterogeneous in object class, there are even more queries performed. This fact and it's performance implications led to the SDK exposing a full MonitoringObject that represents the property bag as mentioned above as well as a stripped PartialMonitoringClass that includes all the same functionality as MonitoringObject, but does not include any property values or property value related methods. Any method returning PartialMonitoringObject will always perform a single database query and thus is the recommended approach when working with these objects, unless the properties are necessary. The only additional method on MonitoringObject that is not on PartialMonitoringObject is:

    public object GetMonitoringPropertyValue(MonitoringClassProperty property)

    Please note that this method can be used properties of the monitoring object itself, or any key properties of any host class.

    Before diving into all the various ways to query for monitoring objects, I wanted to go through some of features of the object (both MonitoringObject and PartialMonitoringObject) itself. First, there is a public event available on the object:

    public event EventHandler<MonitoringObjectMembershipChangedEventArgs> OnRelatedEntitiesChanged

    This event will get signaled any time any object in the containment hierarchy of the current object is changed. The event includes the Id of the object whose contained members changed, but does NOT include what objects actually changed; a query would need to be performed to get contained objects and compare accordingly.

    The object also contains the following properties for quick reference without needing a full Monitoring Object

    public string Name public string Path public string DisplayName public string FullName public bool IsManaged public DateTime LastModified public HealthState HealthState public DateTime? StateLastModified public bool IsAvailable public DateTime? AvailabilityLastModified public bool InMaintenanceMode public DateTime? MaintenanceModeLastModified public ReadOnlyCollection<Guid> MonitoringClassIds public Guid LeastDerivedNonAbstractMonitoringClassId

    Name is the key value(s) of the monitoring object, while DisplayName is the same as name unless a friendly display name was specified by the discovery source via the DisplayName property defined on the System.Entity class. Path is the concatenated names of the hosts, while FullName is the uniquely identifying full name of the current instance, including name, path and class name. The HealthState of the System.EntityHealth monitor for the object is populated, as is the currently IsAvailable property (which is a function of the health service that is currently monitoring this object being available) and whether or not the object is in maintenance mode. IsManaged is always true and is not used. The MonitoringClassIds are all the non-abstract classes this object is, and the LeastDerivedNonAbstractMonitoringClassId is what it says it is, namely the class that brings in the identity of the object, just with a really long name :-).

    There are a ton of methods on this object, that I will leave that for you to browse through. I will go through some as the topics come up with future posts.

    Now, how do you get this objects? Well, first, let's go over MonitoringObjectGenericCriteria and MonitoringObjectCriteria. MonitoringObjectGenericCriteria allows you to query by generic properties shared across all monitoring objects. This is the list of properties you can use:

    Id
    Name
    Path
    FullName
    DisplayName
    LastModified
    HealthState
    StateLastModified
    IsAvailable
    AvailabilityLastModified
    InMaintenanceMode
    MaintenanceModeLastModified

    MonitoringObjectCriteria uses the same properties and allows you to query for monitoring objects by specific property values, as defined on the class(es) of the objects (such as PrincipalName on Computer), however, you can NOT use non-abstract classes. Note that when querying by MonitoringObjectCriteria, MonitoringObject instances are always returned as we have to make a query on both tables in the database anyway.

    What follows is an overview of the methods that are available for retrieving MonitoringObject(s) and PartialMonitoringObject(s). I won't go over all of them as hopefully the docs/Intellisense can help you find what you need.

    These are methods that accept criteria as accessible on the ManagementGroup object:

    public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(MonitoringObjectGenericCriteria criteria) public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(MonitoringObjectCriteria criteria) public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(ICollection<MonitoringObjectCriteria> criteriaCollection) public ReadOnlyCollection<PartialMonitoringObject> GetPartialMonitoringObjects(MonitoringObjectGenericCriteria criteria)

    These are methods that accept criteria as accessible on the monitoring object instance itself. TraversalDepth can be OneLevel or Recursive and determines whether we return immediately contained instances, or all contained instances:

    public ReadOnlyCollection<MonitoringObject> GetRelatedMonitoringObjects(MonitoringObjectGenericCriteria criteria, TraversalDepth traversalDepth) public ReadOnlyCollection<PartialMonitoringObject> GetRelatedPartialMonitoringObjects(MonitoringObjectGenericCriteria criteria, TraversalDepth traversalDepth)

    These methods allow you to specify a class as your criteria, including abstract classes, and are found on ManagementGroup (similar methods are available on MonitoringObject with TraversalDepth as a parameter):

    public ReadOnlyCollection<MonitoringObject> GetMonitoringObjects(MonitoringClass monitoringClass) public ReadOnlyCollection<PartialMonitoringObject> GetPartialMonitoringObjects(MonitoringClass monitoringClass)

    For getting related objects for multiple instances (the UI State View uses these methods), ManagementGroup allows you to get related object to a collection of objects by class, relationship type or MonitoringObjectCriteria (MonitoringObject also has similar methods for a single object):

    public Dictionary<T, ReadOnlyCollection<MonitoringObject>> GetRelatedMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringClass monitoringClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject public Dictionary<T, ReadOnlyCollection<MonitoringObject>> GetRelatedMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringObjectCriteria criteria, TraversalDepth traversalDepth) where T : PartialMonitoringObject public Dictionary<T, ReadOnlyCollection<MonitoringObject>> GetRelatedMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringRelationshipClass relationshipClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject public Dictionary<T, ReadOnlyCollection<PartialMonitoringObject>> GetRelatedPartialMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringClass monitoringClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject public Dictionary<T, ReadOnlyCollection<PartialMonitoringObject>> GetRelatedPartialMonitoringObjects<T>(ICollection<T> monitoringObjects, MonitoringRelationshipClass relationshipClass, TraversalDepth traversalDepth) where T : PartialMonitoringObject

    This list isn't exhaustive, but it's pretty close. If you have any particular scenarios that you are confused about with regard to these methods and how best to accomplish a particular query, shoot me an email or post a comment. Also note that MonitoringRelationship is another class that has a source MonitoringObject and a target MonitoringObject included in the class itself and provides another convenient way to retrieve monitoring objects, but that is for another post.

  • Jakub@Work

    Authoring Management Packs Resource

    • 8 Comments

    I added a link to http://www.authormps.com. This is a site maintained by a co-worker geared towards management pack authors. It is in its early stages, but should prove to be an excellent resource for management pack authoring.

  • Jakub@Work

    WCF and SDK and Threads, Oh My!

    • 0 Comments

    Since the SDK uses WCF under the covers for communicating with the SDK service, users of the SDK must play nice with it when it comes to thread pool utilization. WCF works a bit differently than it would appear from programmatically using it; namely all the calls are asynchronous and the reply is actually processed on a different thread than the method invocation thread. This poses an interesting problem: what if there are not threads to service the reply? A coworker of mine ran into this issue and needless to say, it stalemated his application. He enqueued a bunch of task execution methods onto the threadpool and all of them woke up and submitted their tasks. The replies could not be processed (and in this particular case, there is actually a separate client-callback method that needs to be processed), so they all appeared to just hang, even though the server showed no signs of the calls being blocked. Well, they weren't; they were just done, but unable to retrieve the reply. Longer story short, don't use all your threads to do remote SDK calls.

  • Jakub@Work

    Tasks not working?

    • 2 Comments

    If you are experiencing any issue where notifications aren't working, which can manifest itself as the UI not having management pack related information updated or tasks never completing (like discovery), one thing to look at is broker settings in SQL. If you run the following command (replacing the database name with whatever you named the Operations Manager database):

    SELECT is_broker_enabled FROM sys.databases WHERE name='OperationsManager'

    The result should be 1. If it is not, you need to run the following to enable it:

    ALTER DATABASE OperationsManager SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE

    Warning: WITH ROLLBACK IMMEDIATE will terminate any currently running transactions, but the command needs exclusive access to the db, and thus is required.

    Update: Dustin Hannifin also put up a post regarding this issue and running SQL Server with an account that does not have proper permissions on the SQL Brroker queue. This usually is caused by running SQL as a local user.

  • Jakub@Work

    Running Tasks

    • 4 Comments

    I've had some questions regarding how to run tasks via the SDK so I thought I would post an example here. The code below shows the various ways of executing tasks in the SDK. The underlying implementation relies on query notifications from SQL 2005 to notify us whenever the status of a particular batch changes; we actually register a new notification for every batch, but since the format of the notification is the same and only parameters change, the performance is not overly affected.

    The code is pretty self-explanatory. I don't recommend using the synchronous version of the API because if something unexpected happens to the task your application will be stuck on the call until it times out (30 minutes) OR if you actually expect your task to take longer than 30 minutes, this version will timeout prior to your task completing. Also, if you don't care about getting notified of the results immediately, you can use the SubmitTask(s) API without providing a callback and simply query for results at a later time using the batch id you get from the call and the GetMonitoringTaskResults method available on both ManagementGroup and MonitoringObject.

    using System;

    using System.Collections.ObjectModel;

    using Microsoft.EnterpriseManagement;

    using Microsoft.EnterpriseManagement.Configuration;

    using Microsoft.EnterpriseManagement.Monitoring;

     

    namespace Jakub_WorkSamples

    {

        partial class Program

        {

            static void RunningTasks()

            {

                // Connect to the local management group

                ManagementGroup mg = new ManagementGroup("localhost");

     

                // First lets get the task we are going to execute

                // Note: Indexing the result like this is not necessarily the best

                // practice here, since there could be more than one task with this

                // name.

                MonitoringTaskCriteria taskCriteria = new MonitoringTaskCriteria(

                    "Name = 'Microsoft.SystemCenter.GetAllFailedWorkflows'");

                MonitoringTask failedWorkflowTask =

                    mg.GetMonitoringTasks(taskCriteria)[0];

     

                // Configuration allows you to specify credentials to use different

                // from the default. It also allows you to specify overrides, if

                // any are available

                MonitoringTaskConfiguration failedWorkflowTaskConfiguration =

                    new MonitoringTaskConfiguration();

     

                // Now we need to get an instance of a health service

                // First get the class

                MonitoringClass healthServiceClass =

                    mg.GetMonitoringClass(SystemMonitoringClass.HealthService);

               

                // Next get the object (we'll just pick the first one)

                MonitoringObject healthServiceObject =

                    mg.GetMonitoringObjects(healthServiceClass)[0];

     

                // Object centric task execution

               

                // Synchronous

                ReadOnlyCollection<MonitoringTaskResult> results =

                    healthServiceObject.ExecuteMonitoringTask(failedWorkflowTask,

                    failedWorkflowTaskConfiguration);

     

                // Asynchronous (standard .Net implementation)

                IAsyncResult asyncTaskResult =

                    healthServiceObject.BeginExecuteMonitoringTask(

                    failedWorkflowTask,

                    failedWorkflowTaskConfiguration,

                    null, // You can specify a callback

                    null); // And additional context

     

                results = healthServiceObject.EndExecuteMonitoringTask(asyncTaskResult);

     

                // Asynchronous (more advanced SCOM specific implementation)

                Guid batchId = healthServiceObject.SubmitMonitoringTask(failedWorkflowTask,

                    failedWorkflowTaskConfiguration,

                    MyTaskCallback);

     

                // Task execution off ManagementGroup (for multiple targets)

                PartialMonitoringObject[] targets =

                    new PartialMonitoringObject[] { healthServiceObject };

     

                // Synchronous

                results = mg.ExecuteMonitoringTask(targets, failedWorkflowTask,

                    failedWorkflowTaskConfiguration);

     

                // Asynchronous (standard .Net implementation)

                asyncTaskResult =

                    mg.BeginExecuteMonitoringTask(

                    targets,

                    failedWorkflowTask,

                    failedWorkflowTaskConfiguration,

                    null, // You can specify a callback

                    null); // And additional context

     

                results = mg.EndExecuteMonitoringTask(asyncTaskResult);

     

                // Asynchronous (more advanced SCOM specific implementation)

                batchId = mg.SubmitMonitoringTask(targets,

                    failedWorkflowTask,

                    failedWorkflowTaskConfiguration,

                    MyTaskCallback);

     

                // Wait for the task to complete

                System.Threading.Thread.Sleep(5000);

            }

     

            private static void MyTaskCallback(Guid batchId,

               ReadOnlyCollection<MonitoringTaskResult> results,

               bool completed)

            {

                if (completed)

                {

                    Console.WriteLine("Batch Id {0} is done!", batchId);

                }

            }

        }

    }

     

  • Jakub@Work

    Is My Rule/Monitor Running?

    • 1 Comments

    Well, I haven't had a lot of inspiration lately in terms of questions to blog about. If nothing comes up in the next week or so, I might just start writing about various components in the SDK as it is a rather larger API, that is not particularily well documented at this point. Before I go down that route, however, I was going to give a quick tutorial on how to check if a rule/monitor is running.

    1. Open the UI :-)
    2. Go to the Monitoring Node
    3. Go to the Discovered Inventory View
    4. By default this view is scoped to Computer, you will want to change this by looking at the right Actions pane under State Actions. Hit "Change target type..."
    5. In the :Look For" text box type "Health Service" and select it from the list below. The view will now refresh.
    6. Select the health service you want to see if the rule/monitor is running for. The right pane will now change and Health Service Tasks should appear under State Actions.
    7. Select "Show Running Rules and Monitors for this Health Service" and run it.
    8. You can also run "Show Failed Rules and Monitors for this Health Service"

    The output will look something like this (exact formating is not persisted, but the data should be about right): 

       Show Running Rules and Monitors for this Health Service Task Description
    Status: Succeeded
    Scheduled Time: 2/5/2007 11:52:25 AM
    Start Time: 2/5/2007 11:52:28 AM
    Submitted By:
    Run As:
    Run Location:
    Target:
    Target Type: Health Service
    Category: Maintenance
    Task Output:
    < DataItem type =" WorkflowsReport " time =" 2007-02-05T11:52:29.2045781-08:00 " sourceHealthServiceId =" 00000000-0000-0000-0000-000000000000 " >
      < Status > Running </ Status >
      < Count > 151 </ Count >
    < Details >
    < Instance Id =" {0914A006-3C72-0E16-1B17-C542E1C6953E} " >
      < Workflow > Microsoft.Windows.Server.InstanceGroup.Discovery </ Workflow >
      </ Instance >
    < Instance Id =" {099BFE0F-FDDB-8D5A-67AF-A79317EB6E91} " >
      < Workflow > Microsoft.SystemCenter.PopulateManagementServerComputerGroup </ Workflow >
      < Workflow > Microsoft.SystemCenter.ComputerGroup.AvailabilityRollup </ Workflow >
      < Workflow > Microsoft.SystemCenter.ComputerGroup.PerformanceRollup </ Workflow >
      < Workflow > Microsoft.SystemCenter.ComputerGroup.ConfigurationRollup </ Workflow >
      < Workflow > Microsoft.SystemCenter.ComputerGroup.SecurityRollup </ Workflow >
      </ Instance >
      </ Details >
      </ DataItem >

    The Instance Id in the xml is the Id of the monitoring object that the rule/monitor is running for. The name in the Workflow node is the name of the rule/monitor as defined in the management pack. This is not the same as the display name show in the UI.

  • Jakub@Work

    Notification Subscriptions

    • 45 Comments

    There have been a few requests recently for more granular notification subscriptions. While this is fully supported, our UI is limited in what it exposes for users to tweak. Here is a look at the SDK and how to use it to create a subscription (and related objects).

    First of all, to use subscriptions, you need to setup some auxiliary objects. In my example below, the first thing I create is an endpoint. We support Sip, Sms and Smtp. The endpoint is stored as a ModuleType in the database, although this is abstracted away by the API. The fact that it's a module type, however, means that the name has to follow the naming restrictions for management pack elements. After the endpoint, I create an action (also a ModuleType). The action combines an endpoint with configuration that allows the format of the endpoint to be specified. In the Smtp case, this allows you to specify email properties. Finally, I create a recipient that the subscription will be targeted to.

    The actual subscription involves combining all the aforementioned components and specifying the criteria by which to filter notifications. In SCOM 2007, notifications are based on alerts. You configure which alerts you want to trigger notifications by using the AlertNotChangedSubscriptionConfiguration and the AlertChangedSubscriptionConfiguration classes. These are also used for connector framework subscriptions to mark alerts for forwarding. These two classes represent criteria by which to match alerts. The first matches alert that have not changed that match the criteria while the latter matches alerts that have changed that match the criteria. Both work off a polling interval. If you look at the classes in the SDK, you will notice that you can specify groups and classes to filter by, but what I really wanted to outline here was the criteria property as this is what is really not exposed fully by the UI. The criteria has to match the schema as defined in the Microsoft.SystemCenter.Library called AlertCriteriaType. Note, you don't need the Expression tag, that is filled in for you. In terms of which columns you can query for in the criteria, it is anything that is defined on the Alert table in the db.

    EDIT: Stefan Koell has put together a great powershell example to accomplish the same task.

    Here's the code:

    (Note - If the criteria is not all on one line, it doesn't work correctly, that's why the formatting is a bit weird below. If you select the code, the full criteria should select)

    using System;

    using Microsoft.EnterpriseManagement;

    using Microsoft.EnterpriseManagement.Administration;

    using Microsoft.EnterpriseManagement.Configuration;

    using Microsoft.EnterpriseManagement.ConnectorFramework;

    using Microsoft.EnterpriseManagement.Monitoring;

     

    namespace Jakub_WorkSamples

    {

        partial class Program

        {

            static void InsertSubscription()

            {

                // Connect to the sdk service on the local machine

                ManagementGroup localManagementGroup = new ManagementGroup("localhost);

     

                // Setup an Smtp Endpoint

                SmtpServer smtpServer = new SmtpServer("localhost");

                smtpServer.PortNumber = 42;

                smtpServer.AuthenticationType =

                    SmtpNotificationAuthenticationProtocol.Anonymous;

     

                // The guid is here for a unique name so this can be rerun

                SmtpNotificationEndpoint smtpEndpoint =

                    new SmtpNotificationEndpoint(

                    "SmtpEndpoint_" + Guid.NewGuid().ToString().Replace('-', '_'),

                    "smtp",

                    smtpServer);

                smtpEndpoint.DisplayName = "My Smtp Endpoint";

                smtpEndpoint.Description = "This is my Smtp Endpoint";

                smtpEndpoint.MaxPrimaryRecipientsPerMail = 10;

                smtpEndpoint.PrimaryServerSwitchBackIntervalSeconds = 15;

     

                // This commits the endpoint into the system

                localManagementGroup.InsertNotificationEndpoint(smtpEndpoint);

     

                // Setup the Smtp Action (this includes email format)

                SmtpNotificationAction smtpNotificationAction =

                    new SmtpNotificationAction(

                    "SmtpAction" + Guid.NewGuid().ToString().Replace('-', '_'));

     

                smtpNotificationAction.Body = "Body";

                smtpNotificationAction.Description = "Description";

                smtpNotificationAction.DisplayName = "DisplayName";

                smtpNotificationAction.Endpoint = smtpEndpoint;

                smtpNotificationAction.From = "Test@Test.com";

                smtpNotificationAction.Headers.Add(

                    new SmtpNotificationActionHeader("Name", "Value"));

                smtpNotificationAction.IsBodyHtml = false;

                smtpNotificationAction.ReplyTo = "replyto@test.com";

                smtpNotificationAction.Subject = "my subject";

     

                // This commits the action into the system

                localManagementGroup.InsertNotificationAction(smtpNotificationAction);

     

                // Setup a recipient

                NotificationRecipientScheduleEntry scheduleEntry =

                    new NotificationRecipientScheduleEntry();

                scheduleEntry.ScheduleEntryType =

                    NotificationRecipientScheduleEntryType.Inclusion;

                scheduleEntry.ScheduledDays =

                    NotificationRecipientScheduleEntryDaysOfWeek.Weekdays;

                scheduleEntry.DailyStartTime =

                    new NotificationRecipientScheduleEntryTime(8, 0);

                scheduleEntry.DailyEndTime =

                    new NotificationRecipientScheduleEntryTime(17, 0);

     

                NotificationRecipientDevice recipientDevice =

                    new NotificationRecipientDevice("smtp", "test@test.com");

                recipientDevice.Name = "TestDevice";

                recipientDevice.ScheduleEntries.Add(scheduleEntry);

     

                NotificationRecipient recipient =

                    new NotificationRecipient("RecipientName" + DateTime.Now.ToString());

                recipient.Devices.Add(recipientDevice);

                recipient.ScheduleEntries.Add(scheduleEntry);

     

                // Commits the recipient

                localManagementGroup.InsertNotificationRecipient(recipient);

     

                // Alert configuration

                AlertNotChangedSubscriptionConfiguration config =

                    new AlertNotChangedSubscriptionConfiguration(

                    AlertSubscriptionConfigurationType.Any);

                config.Criteria = "<SimpleExpression><ValueExpression><Property>ResolutionState</Property></ValueExpression><Operator>Equal</Operator><ValueExpression><Value>255</Value></ValueExpression></SimpleExpression>";

                config.ExpirationStartTime = DateTime.Now;

                config.PollingIntervalMinutes = 1;

     

                // Subscription

                AlertNotificationSubscription alertChangeSubscription =

                    new AlertNotificationSubscription(

                    "MyNewAlertChangeSubscription" + Guid.NewGuid().ToString().Replace('-', '_'),

                    config);

                alertChangeSubscription.DisplayName = "My Subscription";

                alertChangeSubscription.Description = "My Subscription Description";

                alertChangeSubscription.ToRecipients.Add(recipient);

                alertChangeSubscription.Actions.Add(smtpNotificationAction);

     

                // Commits the subscription

                localManagementGroup.InsertNotificationSubscription(alertChangeSubscription);

            }

        }

    }

     

  • Jakub@Work

    Management Pack Authoring in SCOM SDK - Quick Start Guide

    • 88 Comments

    Well, it has been quite a holiday! My wife delivered our son, Jamison Jakub, on December 26th at 8:14 AM. Mom and baby are doing great. I've posted some photos on our families personal site: www.oleksyfamily.com. I am more or less back at work for now and will be taking my paternity leave later, most likely after we ship SCOM 2007.

    I wanted to talk a little about management pack authoring and editing in the SDK. This is the largest part of the SDK that I did not write, so I am not an expert at the internal workings of a lot of it, but I do know how it works.

    First, there is a rather large object hierarchy when it comes to management pack related objects. At the most derived level, you will almost always find a Monitoring* object derived from a ManagementPack* object (e.g. MonitoringRule and ManagementPackRule). Monitoring* objects are returned by the online, connected SDK from the database. These objects are not "newable" and contain slightly more context, and sometimes more functionality, then their respective base class. (For instance they always have a pointer to ManagementGroup, which ManagementPack* object do not). Whenever you want to create a new management pack object, you have to use the ManagementPack* version of it to create it.

    The context of a management pack object is always a management pack. When you create an object, you need to specify which management pack it goes into. This is the management pack that the ultimate commit call (AcceptChanges()) will need to be made.

    If you want to change an existing object, simply change whatever you want about it (i.e. any settable properties) and then set its Status property to PendingUpdate. Once you do this, internally it gets placed into a pending changes collection for that management pack. But which instance you might ask? If you are working in connected SDK and you retrieve a management pack object from the database that object belongs to a particular management pack. Internally, this management pack is cached (unless you are running on CacheMode.None in which you should not be doing management pack authoring) and we always maintain the same instance of that management pack. No matter how many times you retrieve that object, that management pack instance will be the same. In fact no matter how you retrieve that management pack (with the ONE caveat that if you do criteria based searching for a management pack, the instance will actually be different as it doesn't use the cache to perform the query) it will be the same instance.

    This poses an interesting problem in that if multiple users in the same application try to update the same management pack, they will be writing into the same management pack at the same time. To alleviate this problem, the ManagementPack class exposes a LockObject property that will lock the management pack for editing. This lock should be taken prior to updating the management pack and released after committing the changes.

    In order to commit changes, simply retrieve the management pack you are changing (either by calling GetManagementPack() on the object(s) you are changing, or some other independent SDK method that is not criteria based) and call AcceptChanges(). Once this method returns, the changes are committed.

    I am sure users will have questions in this area, but it is such a large topic to cover, I just wanted to put together a small quick start guide to get everyone started. As always, please ask any questions you might have or if you want an expanded post on anything in particular regarding this area.

  • Jakub@Work

    Merry Christmas and Happy New Year!

    • 1 Comments
    I'm off for the next few weeks spending time with family and enjoying the holiday. I will still be online to reply to questions, but I won't be putting together any posts during this time. Also, we are expecting our first child in early January, so depending on when that happens, it may be even longer =).
  • Jakub@Work

    Overrides in SCOM 2007

    • 23 Comments

    In SCOM 2007 we have implemented a powerful, but somewhat complicated override architecture.

    Basic overrides for rules, monitors and other workflows fall into two categories: configuration and property overrides. Configuration overrides allow you to override particular configuration elements that are unique to a workflow, while property overrides allow you to override the properties (such as the Enabled property) that are common among all workflows or a particular type (i.e. rules, monitors, etc).

    Configuration overrides actually begin with the the management pack author declaring what portions of configuration are overrideable. For instance, if I declare the following DataSourceModuleType in a management pack:

    <DataSourceModuleType ID="MyDSModuleType" Accessibility="Public">

     <Configuration>

      <xsd:element name="Name" type="xsd:string"></xsd:element>

     </Configuration>

     <OverrideableParameters>

      <OverrideableParameter ID="Name" Selector="$Config/Name$" ParameterType="string"/>

     </OverrideableParameters>

     <ModuleImplementation>

      <Managed>

       <Assembly>MyAssembly</Assembly>

       <Type>MyType</Type>

      </Managed>

     </ModuleImplementation>

     <OutputType>MyDataType</OutputType>

    </DataSourceModuleType>

    the configuration element Name is declared as being overrideable via the OverrideableParameters element. This means that any workflow that uses this data source type, will be able to leverage this as an overrideable parameter. If a portion of configuration is not marked as overrideable, it can't be overridden. Also, we only support simple types of overrideable parameters, so if you have something complex that needs to be overridden, it needs to be declared as a string and your module would need to do the parsing on its own.

    Nothing special needs to be declared for property based overrides. All workflows support overriding the "Enabled" property (an implicit property indicating whether the workflow is enabled or not) and monitors have additional property overrides defined that allow all the various alert related parameters to be overridden.

    When actually defining an override, there are several things that need/can be specified. First off, you need to specify which workflow you want the override to apply to. You also have to specify which configuration element, by referencing the OverrideableParameter ID, or which property you want to override as well as the new value you want. Lastly, you need to define where this override applies. We provide two ways to specify this: Context and ContextInstance. The Context attribute specifies which class you want the override to apply to while the ContextInstance (which is the Guid Id of a monitoring object you want to target) specifies a particular instance. I will go over exactly how these are resolved in a bit, when I talk about the algorithm we use for applying overrides. The other thing you can specify for these overrides is whether or not they are "Enforced". Enforced overrides always take precedence over non-Enforced and can only exists in non-sealed management packs to better allow administrators to manage their overrides.

    Orthogonal to the aforementioned overrides, we also have the concept of category overrides. Category overrides apply to workflows whose Enabled property as defined as  onAdvancedMonitoring, onStandardMonitoring or onEssentialMonitoring. These overrides follow the same targeting concepts as the others with the addition of which Category of workflow to apply to (EventCollection, StateCollection, etc), but act in broad strokes by enabling and disabling many workflows with a single override. If I have a rule that is defined as onAdvancedMonitoring, it will only run if there is an override indicating that onAdvancedMonitoring is enabled. If I have a rule that is marked as onEssentialMonitoring, it will run with an override applied of onAdvancedMonitoring, onStandardMonitoring or onEssentialMonitoring. Essentially, each level is a superset of the level before it, and is enabled as such. SCOM will ship out of the box with overrides enabling onStandardMonitoring while SCE will ship with onEssentialMonitoring.

    Now, how do all these overrides actually get applied? To take the most complicated case, we'll work with an instance. We want to know what overrides apply to a particular instance of IIS for a particular rule targeted toward IIS. The first thing the algorithm does, conceptually, is gather all the types and instances that are in this instances hierarchy. So, this instance would include the IIS class and any classes that it derives from all the way to System.Entity and the computer instance that hosts this IIS instance and the computer class of this computer as well as all it's base classes. Next, the algorithm collects any overrides that may apply to this particular rule and overlays them on the hierarchy. So, if you have an enabled override disabling this rule with a context of System.Entity, it will exist in this objects hierarchy. With this conceptual hierarchy in mind, the algorithm starts at the top and traverses down applying the following criteria, in priority order:

      1. Non-Category enabled overrides always win over category overrides
      2. An enforced override always wins over a non-enforced override
      3. Instance targeted overrides always win over class targeted overrides
      4. Overrides in non-sealed management packs always win over sealed overrides
      5. A child in the hierarchy of overrides always wins over a direct parent override of the same type (instance vs class)
      6. Class overrides from contained instances always win over class overrides of the instance.
      7. Instance overrides with the higher relative depth win over those with a lower depth. Depth is calculated from the root node(s) of the containment hierarchy of which the instance in question is a part
      8. Randomly chosen

    There are methods in the SDK that will give you resultant set. One MonitoringObject and MonitoringClass there are several overloads for GetResultantOverrides. The UI also exposes resultant overrides via the overrides UI for workflows and eventually there will be a summary view of overrides available as well.

  • Jakub@Work

    Differences between MCF in MOM 2005 and SCOM 2007

    • 78 Comments

    I wanted to go through and outline some of the changes we made for MCF since our last release. The things I really wanted to accomplish in redesigning MCF was to make it more integrated with SCOM 2007 than it was in MOM 2005, to make necessary performance improvements and maintain at least the same level of functionality as we had before, while keeping with the model-based approach of SCOM 2007.

    Integration with SCOM 2007

    With the significant changes that took place between these releases, we had an opportunity to integrate MCF more closely with the product than it was before. The first step here, was getting rid of resolution state as the mechanism to mark alerts for forwarding. Instead, the alert object has a special nullable field (ConnectorId) that represents the identifier of the connector that currently "owns" this alert.

    There are a few ways to mark alerts for forwarding. In the UI, you can right-click on an alert and there will be a fly-out menu with available connectors so you can explicitly mark an alert for forwarding. Programmatically, the ConnectorId field on a MonitoringAlert object is settable and once set can be applied by calling the Update() method on the alert. Lastly, we have introduced a concept of connector subscriptions that are exposed via the UI and programmatically (MonitoringConnectorSubscription) that allow users to "subscribe" to a particular set of alerts by using criteria that should be marked for a particular connector.

    Another aspect of more tight-knit integration is the fact that 100% of MCF functionality is available via the managed SDK. While we still ship with a web-service (using WCF) and a proxy to communicate with it, the recommendation is to use the SDK to do MCF functionality unless you are not running on windows; this provides a richer object model and a better programming experience. The namespace in the SDK that servers MCF functionality is Microsoft.EnterpriseManagement.ConnectorFramework and the main object the provides the root of much of the functionality is ConnectorFrameworkAdministration which can be retrieved off a ManagementGroup object via GetConnectorFrameworkAdministration().

    The last aspect of integration is the fact that a connector is modeled in SCOM 2007. While we aren't planning on shipping anything that provides health information for a connector out of the box (time constraints), there is an ability for customers and partners who write connectors to develop a health model for their connector. In a nutshell, creating a connector creates a new instance of Microsoft.SystemCenter.Connector. In order to develop a health model around a new connector, a user should extend this class with their own and develop a health model targeted at the new class. Once this is complete, the built-in setup process will need to be augmented by the connector author to discover their connector as an instance of their new class. Assuming the model is developed and working correctly, the connector object will now have state like anything else SCOM is managing.

    Performance Improvements

    In 2005 we have many performance issues at scale due to the fact that a lot of data is copied from place to place. In the MOM to MOM Connector scenario in particular, we physically moved alerts from bottom tiers to the top tier, replicating the data and keeping it in sync.

    The first change for performance was with respect to tiering. We no longer copy alerts from bottom tiers to the top tier, but instead make available alerts from multiple tiers on the top tier. A SCOM administrator can setup tiers in the UI and make them available to connectors. Once available, MCF provides special "tiered" versions of various methods that aggregate data across these tiers the administrator setup. For instance, calling GetMonitoringAlertsForTiers() will loop through all available tiers and aggregate the results, returning alerts from each. The recommended approach for tiering, however, is to manage the calls to each tier from within the connector code itself as this allows more robustness in terms of error handling and allows for parallel processing for multiple tiers. There is much more to talk about with respect to tiering and how to accomplish this, but it is not in the scope of this article. If there is interest, I will put together a more detailed post on tiering.

    The next improvement for performance has to do with how we process alerts and alert changes. In MOM 2005, we distinguished between "New" and "Updated" alerts and actually kept a cached copy of each change to an alert in a pending list for each connector. If an alert changed 3 times, we would have three independently acknowledgeable changes and three copies of the alert in the cache. This caused performance problems when dealing with large scale deployments. In order to remedy this problem, in SCOM we manage alerts for connectors in place. The alerts in the alert table have a timestamp associated with non-connector related changes (so you don't get an update to an alert you changed as a connector) and that timestamp is checked against the current bookmark of a connector when retrieving alerts. Acknowledging data is now a function of moving a connectors bookmark. In other words, when a connector retrieves 10 alerts, when they are done processing these alerts, they should acknowledge receipt and processing by calling AcknowledgeMonitoringAlerts() with the maximum time of the alerts in the batch that was retrieved. This will ensure no data is lost.

    Important Note: - Since SQL datetime is not particularly granular, it is possible that two alerts with the same datetime timestamp get split between two SELECT calls. Even worse, when acknowledging across tiers, the system times on the machines will not be the same which might cause data loss by acknowledging with a time from one alert on one tier to another tier that has a time slightly in the past. In order to remedy this we actually have a built-in "delay" when retrieving alerts so that we don't get the newest alert but instead get alerts older than 30 seconds. This actually causes some "weird" behavior in that if you mark an alert for forwarding and immediately call GetMonitoringAlerts() you won't get anything, even though the alert is marked. This value is configurable in the database. This query should do it, pending any changes to the property name at RTM: (The SettingValue is the number of seconds of "buffer" you want in GetMonitoringAlerts calls. It needs to be at least 1)

    UPDATE GlobalSettings SET SettingValue = 1 WHERE ManagedTypePropertyId in
    (SELECT ManagedTypePropertyId FROM ManagedTypeProperty WHERE ManagedTypePropertyName = 'TierTimeDifferenceThreshold')

    Other Random Things

    1. We no longer support discovery data insertion via the web-service. This can be done as described in my earlier post.
    2. We support batching (and in fact enforce it with a maximum size of 1000), however the batch size is an "approximation" as ties in the alert timestamp might require more than the requested number of alerts to be returned. Also, batch size for tiered methods is the total across all tiers, not per tier.
    3. The MonitoringConnector object in the SDK has an Initialized property and a Bookmark property available for reading. The web-service has GetConnectorState() and GetConnectorBookmark() for the same purpose.
    4. Alert insertion is not supported. Please see this post and this one.
    5. Alert history retrieval is not done as part of the alert retrieval. Via the SDK there is a GetMonitoringAlertHistory() method on the alert. Via the web-service we have GetMonitoringAlertHistoryByAlertIds() for the same purpose.
    6. We added a way to generically get alerts via the web-service: GetMonitoringAlertsByIds(). This is available in the SDK in a variety of methods.
    7. When updating alerts via the web-service, the connector is implicit. When updating alerts via the SDK on behalf of a connector, it is important to use the overload that accepts a MonitoringConnector object to indicate it is the connector that is changing the alert.
Page 2 of 3 (62 items) 123