Welcome to MSDN Blogs Sign in | Join | Help

Jakub@Work

Programming with System Center Service Manager and Operations Manager
Service Manager 2010 Beta 2 Released

We released our public Beta 2 on Friday and are busy making progress toward our RTM early next year. This is a fully public, feature complete Beta available on the Microsoft Connect site. The email sent from the Connect site to subscribers:

The Service Manager team is very pleased to announce that Beta-2 for Service Manager 2010 is now available for download from connect. Details and links are below.

Download

To download, simply go to the Service Manager site on Connect www.connect.microsoft.com. Once you have successfully signed in, click the Downloads link, click Service Manager Beta-2 and then select all of the following files on the download details page:

· SMCDImage_amd64.exe

· SMCDImage_x86.exe (32 bit console)

· InstallOMMPs.exe

· AuthoringTool.exe (authoring console)

· SM_B2_Public.zip

Click download to begin the download process. Once the files have successfully downloaded, please be sure to read the release notes before installing the product as they contain some important information about the release

You can also use the public forums for feedback and support, link below
http://forums.microsoft.com/TechNet/ShowForum.aspx?ForumID=2066&SiteID=17

Please read the “Preparing for Service Manager Deployment” section of the Installation and Configuration Guide for step-by-step guidance on how to install and configure the product.

Object Templates

In Service Manager, we have a variety of scenarios where we needed to capture partial object state and apply that state at a later time. The state captured is usually all or part of some IT process. For example, when creating a Change Request from the UI, the user is prompted with a set of templates to choose from, each representing a different kind of change request. These templates are stored as Object Templates in MPs and pre-define a set of values to populate the change request form with.

Object templates can be used to store data about classes as well as projections. They are neither classes, nor objects, but rather blueprints (stored in management packs) for creating or modifying objects. In the attached MP you will find three different object templates.

  1.     <ObjectTemplate ID="NFL.NFCDivision.Template" TypeID="NFL.Division" >
  2.       <Property Path="$Target/Property[Type='NFL.Conference']/Name$">NFC</Property>
  3.     </ObjectTemplate>

The first two set properties of a class. The Path notation is the same as used in projections, with the exception that you specify the particular property you want to set as well. You can specify as many or as few properties as you want, these objects don’t need to be complete, but we do verification to ensure property values make sense.

Projections can also be defined:

  1.     <ObjectTemplate ID="NFL.New.Template" TypeID="NFL.Conference.All" >
  2.       <Property Path="$Target/Property[Type='NFL.Conference']/Name$">New Conference</Property>
  3.       <Object Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$">
  4.         <Property Path="$Target/Property[Type='NFL.Conference']/Name$">New Conference</Property>
  5.         <Property Path="$Target/Property[Type='NFL.Division']/Name$">New Division 1</Property>
  6.         <Object Path="$Target/Path[Relationship='NFL.DivisionContainsTeam' TypeConstraint='NFC.Team']$">
  7.           <Property Path="$Target/Property[Type='NFC.Team']/Name$">New Team</Property>
  8.         </Object>
  9.       </Object>
  10.       <Object Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$">
  11.         <Property Path="$Target/Property[Type='NFL.Conference']/Name$">New Conference</Property>
  12.         <Property Path="$Target/Property[Type='NFL.Division']/Name$">New Division 2</Property>
  13.       </Object>
  14.     </ObjectTemplate>


These are similar, but allow you to define a hierarchy of objects that conform to the projection definition the object template references. Whenever you want to define a new object, you need to ensure the endpoint of the relationship is concrete, or you need to specify the TypeConstraint to indicate what class you want to create, as in the example above.

In code, you can use these templates to create new objects or update existing objects:

  1.             // Connect to the management group
  2.             EnterpriseManagementGroup managementGroup =
  3.                 new EnterpriseManagementGroup("localhost");
  4.             // Get the management pack
  5.             ManagementPack nflManagementPack =
  6.                 managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));
  7.             // Get the object templates
  8.             ManagementPackObjectTemplate nfcDivisionTemplate =
  9.                 nflManagementPack.GetObjectTemplate("NFL.NFCDivision.Template");
  10.             ManagementPackObjectTemplate afcDivisionTemplate =
  11.                 nflManagementPack.GetObjectTemplate("NFL.AFCDivision.Template");
  12.             ManagementPackObjectTemplate nflTemplate =
  13.                 nflManagementPack.GetObjectTemplate("NFL.New.Template");
  14.             // Create a new NFC division using the template
  15.             CreatableEnterpriseManagementObject newNfcDivision =
  16.                 new CreatableEnterpriseManagementObject(managementGroup, nfcDivisionTemplate);
  17.             // Change it to an AFC division
  18.             newNfcDivision.ApplyTemplate(afcDivisionTemplate);
  19.             // Create a new projection from a template
  20.             EnterpriseManagementObjectProjection newNfl =
  21.                 new EnterpriseManagementObjectProjection(managementGroup, nflTemplate);
  22.             // You can also apply a template to an existing projection
  23.             newNfl.ApplyTemplate(nflTemplate);
Categorizing Management Pack Elements

If you are familiar with Management Packs and their elements in Operations Manager, you'll know that there were a few elements that had a "Category" attribute whose value was fixed from an enumeration defined in the schema. While this worked, it was quite limiting. For one, the enumeration itself did not make a lot of sense globally. There were values in the enumeration that only applied to some of the elements that used it. Next, it only allowed for some elements to be categorized and only once. Finally, the enumeration was not extensible.

In Service Manager we've added the ability to categorize any Management Pack element via an extensible mechanism that leverages the new concept of enumerations as defined in management packs. Enumerations are essentially hierarchal extensible lists that are defined in management packs. They can be used both as values for class properties as well as values for categorization of elements. In this article, I'll be discussing the latter.

I've updated the NFL management pack I have been using and introduced a new enumeration and some categories. First, you'll find the following enumeration defined:

<EnumerationTypes>
        <EnumerationValue ID="NFL.VisibleToCallers" Accessibility="Public" />
</EnumerationTypes>

I can define enumerations to mean just about anything I want as the consumer of these enumerations and subsequent categories defines their behavior. In this case, I am defining an enumeration that categorizes elements as visible to callers via some API that I will be providing.

Once I have defined my enumeration, the next step is to categorize my elements as "in" the value. For this, I need to define category elements:

<Categories>
    <Category ID="NFL.Visibility1" Target="NFL.Conference" Value="NFL.VisibleToCallers" />
    <Category ID="NFL.Visibility2" Target="NFL.Division" Value="NFL.VisibleToCallers" />
    <Category ID="NFL.Visibility3" Target="NFL.Team" Value="NFL.VisibleToCallers" />
</Categories>

As you can see, I've placed three classes into my enumeration, thus categorizing them. But what does this mean from an API perspective? Really, it doesn't mean anything unless the consumer honors the categorization. As an example, the Service Manager UI uses various enumerations as defined in our management packs to categorize elements, such as console tasks or views, to control their visibility behavior in the UI.

Next, I'll demonstrate how a caller of the API can use this categorization. In the following sample, after importing the attached management pack, I retrieve the defined enumeration and generate criteria from it to filter out the classes I want to retrieve based on the categorization:

            // Connect to the management group
            EnterpriseManagementGroup managementGroup =
                new EnterpriseManagementGroup("localhost");

            // Get the management pack
            ManagementPack nflManagementPack = 
                managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));

            // Get the type enumeration value
            ManagementPackEnumeration showToCallersEnumeration =
                nflManagementPack.GetEnumeration("NFL.VisibleToCallers");

            // Create an in statement using the category property
            string classCriteria = string.Format(@"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
                                        <Expression>
                                            <In>
                                              <GenericProperty>Category</GenericProperty>
                                              <Values>
                                                <Value>{0}</Value>
                                              </Values>
                                            </In>
                                          </Expression>
                                        </Criteria>", showToCallersEnumeration.Id);

            // Create the criteria object
            ManagementPackClassCriteria classesVisibleToCallersCriteria = new ManagementPackClassCriteria(classCriteria);
            
            // Get classes using criteria
            IList<ManagementPackClass> classesVisibleToCallers = managementGroup.EntityTypes.GetClasses(classesVisibleToCallersCriteria);

Important Note: The criteria behaves in the following way with respect to inclusion and exclusion of categories. If you generate a query as shown earlier, it will return any class that is in this category, regardless if it is in other categories. If you change the query to ask for classes where category is not of a particular id, it will exclude the classes that are ONLY in that particular category; so if a class is in category A and B and you ask for classes not in B, you will still get the class back because A is not in B. If a class is only in category B and you ask for classes not in B, you will not get it back because that class is exclusively in B and thus NOT IN B is false. Even if this didn't make sense to you (I had to read it back several times myself :-) ), essentially what this means is that NOT EQUAL to a particular category queries just don't work the way you would expect so please don't use them or understand how they work before you do. To mitigate this problem, we are planning on caching categories client-side so users can quickly do filtering post-query for "NOT" semantics.

New Criteria

Well, we're back from MMS which was pretty successful for Service Manager in general. In a lot of ways, it was a coming out party of sorts as we were able to demonstrate a ton of functionality about the product and really show how well it is coming along. My session with Travis went great and I really feel we did a solid job in speaking to and demonstrating the power of the Service Manager platform. The team is heads down in Beta 2 right now as we push for an early Fall release. Beta 2 is very exciting for us because at that point the product is basically complete from a feature perspective and customers will be able to play with as close to the final bits as they've ever had. I really hope we get a lot more people interested in customization and extension at that time and I'll be excited to blog about a lot of the new features we're introducing in Beta 2 from an SDK perspective.

In this post, I wanted to talk about our new criteria a little bit and introduce you to the new format and some of the new concepts. If you are familiar with OM, the criteria there was very similar to a WHERE clause in SQL. While this was very friendly from an end-user perspective, it actually was quite burdensome from a developer perspective, both in generating complicated criteria and for us in parsing it and ensuring its uniqueness. For Service Manager 2010 we decided to prioritize power and ease of programmatic manipulation over readability and the ability to quickly script the criteria. Criteria is used in for querying for a variety of objects in the SDK. Here's I'll use examples mostly around the instance and projection space.

Simple Expression
A simple expression just compares a property to a value. In this example we are comparing PropertyName on ClassName to the value 3.

               <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Property[Type='ClassName']/PropertyName$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
              </SimpleExpression>

For projections, a simple expression would look like this, where the property is actually a path:

              <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Path[Relationship='RelationshipName']/Property[Type='ClassName']/PropertyName$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
              </SimpleExpression>

Also, rather than using names, you can use Ids in the criteria:

              <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Property[Type='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/b982d3ec-96e0-4901-b1d9-a06196acf0bc$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>3</Value>
                </ValueExpressionRight>
              </SimpleExpression>

or:

            <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Path[Relationship='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/
Property[Type='b982d3ec-96e0-4901-b1d9-a06196acf0bc']/b982d3ec-96e0-4901-b1d9-a06196acf0bc$</Property> </ValueExpressionLeft> <Operator>Equal</Operator> <ValueExpressionRight> <Value>3</Value> </ValueExpressionRight> </SimpleExpression>

Unary/Regex Expression
These are the same as the simple expressions, but instead Unary and RegEx operators are supported. RegExExpression supports ContainsSubstring, MatchesWildcard, MatchesRegularExpression, DoesNotContainSubstring, DoesNotMatchWildcard and DoesNotMatchRegularExpression. UnaryExpression supports IsNull and IsNotNull.

Contains/NotContains/ContainedBy/NotContainedBy Expression
These expressions allow a traversal to be made either down or up containment to either include or exclude results based the existence of a relationship to other instances. The concept here is similar to an EXISTS query in SQL.

          <Contains>
            <!-- A Contains query from a particular node in the projection; this can also just drop path and the implicit path is the seed -->
            <Path>$Target/Path[Relationship='RelationshipName']$</Path>
            <Class>$MPElement[Name='System!System.Computer']$</Class>
            <Expression>
              <SimpleExpression>
                <ValueExpressionLeft>
                  <Property>$Target/Property[Type='System!System.Computer']/FQDN$</Property>
                </ValueExpressionLeft>
                <Operator>Equal</Operator>
                <ValueExpressionRight>
                  <Value>ComputerName</Value>
                </ValueExpressionRight>
              </SimpleExpression>
            </Expression>
          </Contains>

The Path element indicates where within the context of the criteria you want to perform the containment based query. If the path is excluded, it means that you want the containment query to be done on the target of the criteria, namely the projection seed class or the class in the case of regular instance queries.

As a more concrete example, given an Incident projection with System.Incident as the seed and a single related owner System.User, one can do containment based queries on either the incident or the user. If a query like “Give me all incidents that are contained by the ‘Tier 1 Incident Group’”, the path element would be excluded, the Class element would point to the Incident Group type and the expression would be empty. If there were some criteria on that group that needed to be matched, that expression would be used for that. If the query was like “Give me incidents that have a owner that is contained by “Tier 1 Users Group”, the path element would need to be populated pointing to System.User.

It is important to note that the path element indicates where in the context of the criteria you want the containment query to apply, while the class and criteria indicate what you are searching for in that containment space.

And/Or Expression
These expressions allow you to combine other expressions.

In Expression
And finally we support a basic IN clause for use with Guid fields only. The other thing to note here is that I am using a GenericProperty instead of Property element. This allows you to refer to generic properties of instances that aren't modeled. This property also has a Path attribute that allows you to point to a node in a projection for which you want the generic property to apply.

    <Expression>
      <!-- Simple IN Clause (GUID support only) -->
      <In>
        <GenericProperty>Id</GenericProperty>
        <Values>
          <Value>b982d3ec-96e0-4901-b1d9-a06196acf0bc</Value>
          <Value>b982d3ec-96e0-4901-b1d9-a06197acf0bc</Value>
          <Value>b982d3ec-96e0-4901-b1d9-a06198acf0bc</Value>
        </Values>
      </In>
    </Expression>
MMS 2009

I apologize for not posting lately, we've been really heads down on Beta 2 and working on our sessions for MMS. I plan to post more regularly again when things die down a bit, but in the meantime just wanted to let you know that I'll be at MMS doing the session below. If anyone is also going to be there, come check out our talk.

 SO23 Customizing and Extending Service Manager
Speaker(s): Jakub Oleksy, Travis Wright
Track(s): Operations Management
Session Type(s): Breakout Session
Products(s): Operations Manager 2007, Service Manager

A technical drill-down on the customization capabilities of Service Manager: This session will articulate how to extend and deliver Service Manager through the delivery of management packs. We will demonstrate the authoring tools to learn how to create workflows, customize templates and extend the configuration management database

Getting and Working With Type Projections - Basic

In my last post in gave you a more in-depth look at creating type projections in Service Manager. In this post, I'd like to provide you with some basic examples on how you can retrieve and work with instances of type projections. I'll be using the same Management Pack and data set from the previous post.

On the Instances interface you will find a few methods for retrieving instances of EnterpriseManagementObjectProjection. Post Beta 1, we've actually cleaned up this interface significantly so I don't want to concentrate too much on the actual method call, but rather the parameters to it and working with the result.

In Beta 1 the method you would use looks like this:

IList<EnterpriseManagementObjectProjection> GetObjectProjections<T>(ManagementPackTypeProjection managementPackTypeProjection, 
ObjectProjectionCriteria criteria, ObjectQueryOptions queryOptions) where T : EnterpriseManagementObject;

Post Beta 1, this method is replaced by:

IObjectProjectionReader<T> GetObjectProjectionReader<T>(ObjectProjectionCriteria criteria, ObjectQueryOptions queryOptions)
            where T : EnterpriseManagementObject;

The ManagementPackTypeProjection from the first simply gets rolled into the ObjectProjectionCriteria object and the return type gets changed to support buffered reads to help with performance, however, IObjectProjectionReader can actually be used like an IList (although it doesn't implement IList); I'll discuss the reader more once we ship Beta 2 later this year. My examples below will use the latest code since that will be around longer; hopefully it is easily transferable for you to the Beta 1 API.

The first thing you need in order to get a type projection instance, is the type projection definition. This narrows down the types of instances you will get back to those that fit the structure of the type projection:

      // Connect to the management group
      EnterpriseManagementGroup managementGroup =
            new EnterpriseManagementGroup("localhost");

      // Get the management pack
      ManagementPack nflManagementPack = 
            managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));

      // Get the type projection
      ManagementPackTypeProjection nflProjection =
            nflManagementPack.GetTypeProjection("NFL.Conference.All");

Very simply you can retrieve projections by specifying the type:

IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
      managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
      new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);

This will retrieve all projections in the system of that structure. A projection exists if the root object of the projection exists, regardless of whether any of the components are found. If you want to limit your result somehow, you will need to specify criteria to limit your result set. One example of simple criteria you could add is matching on one of the generic properties of the root object:

            string displayNameCriteria = @"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
                  <Expression>
                    <SimpleExpression>
                      <ValueExpressionLeft>
                        <GenericProperty>DisplayName</GenericProperty>
                      </ValueExpressionLeft>
                      <Operator>Equal</Operator>
                      <ValueExpressionRight>
                        <Value>National Football Conference</Value>
                      </ValueExpressionRight>
                    </SimpleExpression>
                  </Expression>
                </Criteria>";

            IObjectProjectionReader<EnterpriseManagementObject> objectProjectionsByDisplayName =
                 managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                     new ObjectProjectionCriteria(displayNameCriteria, nflProjection, managementGroup), ObjectQueryOptions.Default);

This would limit the results to any projections whose root object had a display name matching "National Football Conference." The list of available generic properties for Service Manager is Id, Name, DisplayName, LastModified and new post Beta 1 CreatedDate and LastModifiedBy.

It is also possible to query on type specific properties of the root object:

            string propertyCriteria = @"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
                  <Expression>
                    <SimpleExpression>
                      <ValueExpressionLeft>
                        <Property>$Target/Property[Type='NFL.Conference']/Name$</Property>
                      </ValueExpressionLeft>
                      <Operator>Equal</Operator>
                      <ValueExpressionRight>
                        <Value>NFC</Value>
                      </ValueExpressionRight>
                    </SimpleExpression>
                  </Expression>
                </Criteria>";

            IObjectProjectionReader<EnterpriseManagementObject> objectProjectionsByProperty =
                 managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                     new ObjectProjectionCriteria(propertyCriteria, nflProjection, nflManagementPack, managementGroup), ObjectQueryOptions.Default);

Criteria can get more complicated than the above examples, including specifying criteria on the individual components of the projection, but an in-depth discussion will happen in a later post.

Once you get an EnterpriseManagementObjectProjection, you essentially have a collection of instances organized in a hierarchy as defined by the type projection. If you run the following code:

            foreach (EnterpriseManagementObjectProjection projection in objectProjections)
            {
                Console.WriteLine("Conference: {0}", projection.Object.DisplayName);
                foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                {
                    Console.WriteLine("\tDivision: {0}", division.Object.DisplayName);
                    foreach (IComposableProjection team in division[divisionContainTeam.Target])
                    {
                        Console.WriteLine("\t\tTeam: {0}", team.Object.DisplayName);
                        foreach (IComposableProjection coach in team[coachCoachesTeam.Source])
                        {
                            Console.WriteLine("\t\t\tCoach: {0}", coach.Object.DisplayName);
                        }
                    }
                }
            }

You'll get a result that shows you the structure of the projection in memory:

Conference: American Football Conference
        Division: AFC West
                Team: Oakland Raiders
                Team: Kansas City Chiefs
                Team: San Diego Chargers
                Team: Denver Broncos
        Division: AFC East
                Team: Buffalo Bills
                Team: New England Patriots
                Team: New York Jets
                Team: Miami Dolphins
        Division: AFC South
                Team: Houston Texans
                Team: Jacksonville Jaguars
                Team: Tennessee Titans
                Team: Indianapolis Colts
        Division: AFC North
                Team: Cleveland Browns
                Team: Pittsburgh Steelers
                Team: Baltimore Ravens
                Team: Cincinnati Bengals
Conference: National Football Conference
        Division: NFC North
                Team: Green Bay Packers
                Team: Chicago Bears
                        Coach: Lovie Smith
                Team: Detroit Lions
                Team: Minnesota Vikings
        Division: NFC South
                Team: Carolina Panthers
                Team: Atlanta Falcons
                Team: Tampa Bay Buccaneers
                Team: New Orleans Saints
        Division: NFC East
                Team: New York Giants
                Team: Dallas Cowboys
                Team: Philadelphia Eagles
                Team: Washington Redskins
        Division: NFC West
                Team: St. Louis Rams
                Team: Seattle Seahawks
                Team: Arizona Cardinals
                Team: San Francisco 49ers

If the line is indented, it indicates a jump across a relationship. The hierarchy is organized by the relationship types that bring in each component; put another way, as you traverse from parent to child you are traversing a relationship of a particular relationship type in one direction. So, as you go from American Football Conference to AFC West, you are moving from "Conference" to "Division" on the NFL.ConferenceHostsDivision relationship type.

Each node in an EnterpriseManagementObjectProjection implements IComposableProjection which offers various ways at traversing through the hierarchy. The object is also IEnumerable<KeyValuePair<ManagementPackRelationshipEndpoint, IComposableProjection>>, which shows that the hierarchy is organized by the relationship endpoint that brings each node in. You also get a pointer to the actual object at the node:

        /// <summary>
        /// The current object in the projection.
        /// </summary>
        EnterpriseManagementObject Object
        {
            get;
        }

The role that brought the current object into the current projection:

        /// <summary>
        /// Gets the role of this projection, relative to its parent.
        /// </summary>
        /// <value></value>
        ManagementPackRelationshipEndpoint ObjectRole
        {
            get;
        }

The object that brought the current object into the current projection:

        /// <summary>
        /// The parent object, if any, for the current object in the projection.
        /// </summary>
        IComposableProjection ParentObject
        {
            get;
        }

And a few indexers to aid in traversal, one of which we used in the sample above:

        /// <summary>
        /// Access all IComposableProjection elements of this IComposableProjection, optionally recursively.
        /// </summary>
        IList<IComposableProjection> this[TraversalDepth traversalDepth]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by role name.
        /// </summary>
        IList<IComposableProjection> this[string roleName]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by role name.
        /// </summary>
        IList<IComposableProjection> this[string roleName, ManagementPackClass classConstraint]
        {
            get;
        }

        /// <summary>
        /// Gets the IComposableProjection child of the projection by id of the contained object
        /// </summary>
        IComposableProjection this[string roleName, Guid id]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by relationship role.
        /// </summary>
        IList<IComposableProjection> this[ManagementPackRelationshipEndpoint role]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by relationship role and a class constraint.
        /// </summary>
        IList<IComposableProjection> this[ManagementPackRelationshipEndpoint role, ManagementPackClass classConstraint]
        {
            get;
        }

        /// <summary>
        /// Gets the IComposableProjection child of the projection by id of the contained object
        /// </summary>
        IComposableProjection this[ManagementPackRelationshipEndpoint role, Guid id]
        {
            get;
        }

In future posts I'll discuss more advanced concepts around working with projections instances (they're IXPathNavigable!), a deeper look at criteria, creating and editing projections and a lot more. I hope that by the time Beta 2 ships later this year to have a comprehensive list of topics that help introduce you to new API concepts.

More with Type Projections

In my last post I introduced you to a new concept in the Service Manager infrastructure called Type Projections. Here, I want to expand on my previous example and discuss type projection structure in more depth. I've attached the management pack that I will be discussing (NFL.xml) and there is reference code at the bottom of this post for populating sample data.

Type projection definitions allow users to create virtual object collections through any relationships found in the system. In the previous post I showed how a projection could bring hosted and host objects together in one go. The following projection expands on that example by introducing System.Containment and System.Reference relationships, as well as introducing the concepts of type constraints and relationship direction:

      <TypeProjections>
        <TypeProjection ID="NFL.Conference.All" Accessibility="Public" Type="NFL.Conference">
          <Component Alias="Division" Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$">
            <Component Alias="AFCTeam" Path="$Target/Path[Relationship='NFL.DivisionContainsTeam' SeedRole='Source' TypeConstraint='AFC.Team']$"/>
            <Component Alias="NFCTeam" Path="$Target/Path[Relationship='NFL.DivisionContainsTeam' SeedRole='Source' TypeConstraint='NFC.Team']$">
              <Component Alias="Coach" Path="$Target/Path[Relationship='NFL.CoachCoachesTeam' SeedRole='Target']$" />
            </Component>
          </Component>
        </TypeProjection>
      </TypeProjections>

Added to the Division component from the previous post, I've now introduced two components that bring in all the teams in the NFL. I've defined my NFL.Team class as abstract and created concrete classes, AFC.Team and NFC.Team. I've also created a relationship derived from System.Containment associating an NFL.Division to an NFL.Team. Of note is that this relationship ends with an abstract class as the target endpoint. While this is supported by type projection definitions, the projection I have defined actually demonstrates the ability to constraint a component to a particular class. The two components, AFCTeam and NFCTeam each share a relationship, however, they each add a different type constraint. Given something can't be an NFCTeam and AFCTeam at the same time, these components will introduce unique sets of data.

If I were to change the AFCTeam component to remove the type constraint, that component would return all NFL.Team instances while the NFCTeam component would return only those NFL.Team objects that are NFC.Teams as well.

Next, I added a System.Reference relationship between NFL.Coach and NFL.Team. Notice that the coach is the source of that relationship. If I want to include all coaches for NFC.Teams, I need to indicate in the type projection definition that I want to traverse the relationship from target to source. This is done via the SeedRole attribute. The "Seed" refers to the parent component of the current component; the attribute describes which role the seed should play relative to the relationship used in the current component. In the sample, the relationship is NFL.CoachCoachesTeam and the seed (NFCTeam) is the target of that relationship and hence SeedRole='Target'.

Traversing this structure is similar to the example in my previous post:

            // Connect to the management group
            EnterpriseManagementGroup managementGroup =
                new EnterpriseManagementGroup("localhost");

            // Get the management pack
            ManagementPack nflManagementPack = 
                managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));

            // Get the type projection
            ManagementPackTypeProjection nflProjection =
                nflManagementPack.GetTypeProjection("NFL.Conference.All");

            // Get the relationship types
            ManagementPackRelationship nflConferenceContainsDivision =
                nflManagementPack.GetRelationship("NFL.ConferenceHostsDivision");

            ManagementPackRelationship divisionContainTeam =
                nflManagementPack.GetRelationship("NFL.DivisionContainsTeam");

            ManagementPackRelationship coachCoachesTeam =
                nflManagementPack.GetRelationship("NFL.CoachCoachesTeam");

            IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
                managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                    new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);

            foreach (EnterpriseManagementObjectProjection projection in objectProjections)
            {
                Console.WriteLine("Conference: {0}", projection.Object.DisplayName);
                foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                {
                    Console.WriteLine("\tDivision: {0}", division.Object.DisplayName);
                    foreach (IComposableProjection team in division[divisionContainTeam.Target])
                    {
                        Console.WriteLine("\t\tTeam: {0}", team.Object.DisplayName);
                        foreach (IComposableProjection coach in team[coachCoachesTeam.Source])
                        {
                            Console.WriteLine("\t\t\tCoach: {0}", coach.Object.DisplayName);
                        }
                    }
                }
            }

When we traverse from team to coach, we use the .Source property of the relationship which corresponds to the SeedRole description in the type projection definition. As part of traversing the projection, think of the role as where you are traversing to; in this example you are traversing from the team (the .Target property) to the coach (the .Source property).

When traversing from divisions to teams, I am using the abstract .Target property of the NFL.DivisionContainsTeam relationship. Type constraints are not expressed in the traversal mechanism for these objects; the key from going to one component to another is the relationship endpoint, save any type constraints.

Reference Code

           // Connect to the management group
            EnterpriseManagementGroup managementGroup =
                new EnterpriseManagementGroup("localhost");

            // Import the management pack
            ManagementPack managementPack = new ManagementPack("NFL.xml");
            managementGroup.ManagementPacks.ImportManagementPack(managementPack);
            managementPack = managementGroup.ManagementPacks.GetManagementPack(managementPack.Id);

            // Populate Data
            IncrementalDiscoveryData dataTransaction = new IncrementalDiscoveryData();

            // Get System.Entity class
            ManagementPackClass systemEntity = managementGroup.EntityTypes.GetClass(SystemClass.Entity);

            // Conferences
            ManagementPackClass conference = managementPack.GetClass("NFL.Conference");
            CreatableEnterpriseManagementObject nfc = new CreatableEnterpriseManagementObject(managementGroup, conference);
            nfc[conference, "Name"].Value = "NFC";
            nfc[systemEntity, "DisplayName"].Value = "National Football Conference";
            dataTransaction.Add(nfc);

            CreatableEnterpriseManagementObject afc = new CreatableEnterpriseManagementObject(managementGroup, conference);
            afc[conference, "Name"].Value = "AFC";
            afc[systemEntity, "DisplayName"].Value = "American Football Conference";
            dataTransaction.Add(afc);

            // Divisions
            Dictionary<string, List<string>> divisions = new Dictionary<string,List<string>>();
            divisions.Add("AFC North", new List<string>());
            divisions["AFC North"].Add("Baltimore Ravens");
            divisions["AFC North"].Add("Cincinnati Bengals");
            divisions["AFC North"].Add("Cleveland Browns");
            divisions["AFC North"].Add("Pittsburgh Steelers");
            divisions.Add("NFC North", new List<string>());
            divisions["NFC North"].Add("Chicago Bears");
            divisions["NFC North"].Add("Detroit Lions");
            divisions["NFC North"].Add("Green Bay Packers");
            divisions["NFC North"].Add("Minnesota Vikings");
            
            divisions.Add("AFC South", new List<string>());
            divisions["AFC South"].Add("Houston Texans");
            divisions["AFC South"].Add("Indianapolis Colts");
            divisions["AFC South"].Add("Jacksonville Jaguars");
            divisions["AFC South"].Add("Tennessee Titans");
            divisions.Add("NFC South", new List<string>());
            divisions["NFC South"].Add("Atlanta Falcons");
            divisions["NFC South"].Add("Carolina Panthers");
            divisions["NFC South"].Add("New Orleans Saints");
            divisions["NFC South"].Add("Tampa Bay Buccaneers");

            divisions.Add("AFC East", new List<string>());
            divisions["AFC East"].Add("Buffalo Bills");
            divisions["AFC East"].Add("Miami Dolphins");
            divisions["AFC East"].Add("New England Patriots");
            divisions["AFC East"].Add("New York Jets");
            divisions.Add("NFC East", new List<string>());
            divisions["NFC East"].Add("Dallas Cowboys");
            divisions["NFC East"].Add("New York Giants");
            divisions["NFC East"].Add("Philadelphia Eagles");
            divisions["NFC East"].Add("Washington Redskins");

            divisions.Add("AFC West", new List<string>());
            divisions["AFC West"].Add("Denver Broncos");
            divisions["AFC West"].Add("Kansas City Chiefs");
            divisions["AFC West"].Add("Oakland Raiders");
            divisions["AFC West"].Add("San Diego Chargers");
            divisions.Add("NFC West", new List<string>());
            divisions["NFC West"].Add("Arizona Cardinals");
            divisions["NFC West"].Add("San Francisco 49ers");
            divisions["NFC West"].Add("Seattle Seahawks");
            divisions["NFC West"].Add("St. Louis Rams");

            Dictionary<string, string> teamToCoach = new Dictionary<string, string>();
            teamToCoach.Add("Chicago Bears", "Lovie Smith");

            ManagementPackClass divisionClass = managementPack.GetClass("NFL.Division");
            ManagementPackClass nfcTeamClass = managementPack.GetClass("NFC.Team");
            ManagementPackClass afcTeamClass = managementPack.GetClass("AFC.Team");
            ManagementPackClass coachClass = managementPack.GetClass("NFL.Coach");

            ManagementPackRelationship divisionContainsTeamClass = managementPack.GetRelationship("NFL.DivisionContainsTeam");
            ManagementPackRelationship coachCoachesTeamClass = managementPack.GetRelationship("NFL.CoachCoachesTeam");
            foreach (string divisionName in divisions.Keys)
            {
                CreatableEnterpriseManagementObject division = new CreatableEnterpriseManagementObject(managementGroup, divisionClass);

                string conferenceName;
                ManagementPackClass teamClass;

                if (divisionName.Contains("AFC") == true)
                {
                    conferenceName = "AFC";
                    teamClass = afcTeamClass;
                }
                else
                {
                    conferenceName = "NFC";
                    teamClass = nfcTeamClass;
                }

                // Need to make sure to set key values of the host
                division[conference, "Name"].Value = conferenceName;
                division[divisionClass, "Name"].Value = divisionName;
                division[systemEntity, "DisplayName"].Value = divisionName;
                dataTransaction.Add(division);
                foreach (string teamName in divisions[divisionName])
                {
                    CreatableEnterpriseManagementObject team = new CreatableEnterpriseManagementObject(managementGroup, teamClass);
                    team[teamClass, "Name"].Value = teamName;
                    team[systemEntity, "DisplayName"].Value = teamName;
                    dataTransaction.Add(team);

                    CreatableEnterpriseManagementRelationshipObject divisionContainsTeam = 
new CreatableEnterpriseManagementRelationshipObject(managementGroup, divisionContainsTeamClass); divisionContainsTeam.SetSource(division); divisionContainsTeam.SetTarget(team); dataTransaction.Add(divisionContainsTeam); if (teamToCoach.ContainsKey(teamName) == true) { CreatableEnterpriseManagementObject coach = new CreatableEnterpriseManagementObject(managementGroup, coachClass); coach[coachClass, "Name"].Value = teamToCoach[teamName]; coach[systemEntity, "DisplayName"].Value = teamToCoach[teamName]; dataTransaction.Add(coach); CreatableEnterpriseManagementRelationshipObject coachCoachesTeam =
new CreatableEnterpriseManagementRelationshipObject(managementGroup, coachCoachesTeamClass); coachCoachesTeam.SetSource(coach); coachCoachesTeam.SetTarget(team); dataTransaction.Add(coachCoachesTeam); } } } // Use Overwrite instead of Commit here because I want to bypass optimistic concurrency checks which will not allow the // insertion of an existing instances and I want this method to be re-runnable dataTransaction.Overwrite(managementGroup);
Getting Started with Type Projections

A very powerful new concept in the SDK is the notion of Type Projections. I always describe type projections as a view over our type system, much like a SQL view over tables, with the added capability that our type projections are hierarchal. What projections allow you to do is query over and retrieve collections of objects related somehow in our model in one go. In this post I want to provide a very simply example of how to define and retrieve a type projection, and in future posts I will dive much deeper into working with them.

For this post, I've attached a management pack (NFL.xml) that defines the classes and type projection used in the sample code. Also, I have the code I used to populate my objects in the Reference Code section below.

In the management pack you will notice two classes defined, NFL.Conference and NFL.Division, and a hosting relationship type, NFL.ConferenceHostsDivision, between them. My scenario is that I want to display all the conferences and divisions in the UI, so I define a type projection that puts the two classes together:

<TypeProjection ID="NFL.Conference.All" Accessibility="Public" Type="NFL.Conference">
     <Component Alias="Division" Path="$Target/Path[Relationship='NFL.ConferenceHostsDivision']$" />
</TypeProjection>

The first thing you should notice in the above definition is the Type property on the root element; this defines the type of instance that should exist at the root or "seed" of the projection. Next, you'll notice the projection has one component, Division, that relates to the root. The Path attribute is what defines how to find the desired component, relative to the seed. I will describe the path notation in more depth in future posts, but here Target refers to the seed element of the projection (an instance of NFL.Conference), followed by a Path construct that tells the system to follow the NFL.ConferenceHostsDivision relationship type.  This will result in a structure that looks like this:

* NFL.Conference

* NFL.Division

Now, in order to work with this, the SDK has introduced a new object type, EnterpriseManagementObjectProjection, along with an interface, IComposableProjection, to allow for retrieval of and then navigation of the returned structure. The following code shows a basic example of retrieving and iterating through the objects:

            // Connect to the management group
            EnterpriseManagementGroup managementGroup =
                new EnterpriseManagementGroup("localhost");

            // Get the management pack
            ManagementPack nflManagementPack = 
                managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));

            // Get the type projection
            ManagementPackTypeProjection nflProjection = 
                nflManagementPack.GetTypeProjection("NFL.Conference.All");

            // Get the relationship type
            ManagementPackRelationship nflConferenceContainsDivision = 
                nflManagementPack.GetRelationship("NFL.ConferenceHostsDivision");

            IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
                managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                    new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);

            foreach (EnterpriseManagementObjectProjection projection in objectProjections)
            {
                Console.WriteLine("Conference {0}", projection.Object.DisplayName);
                foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                {
                    Console.WriteLine("Division {0}", division.Object.DisplayName);
                }
            }

The sample first connects to the server and retrieves the type projection definition which is necessary for interacting with the SDK and the relationship type definition which is necessary for navigating the resulting instance. Next, we get an IObjectProjectionReader<EnterpriseManagementObject> that will contain all instances of our type projection. Finally, the example shows how to iterate through the object and get data.

Reference Code

            // Connect to the management group
            EnterpriseManagementGroup managementGroup =
                new EnterpriseManagementGroup("localhost");

            // Import the management pack
            ManagementPack managementPack = new ManagementPack("NFL.xml");
            managementGroup.ManagementPacks.ImportManagementPack(managementPack);
            managementPack = managementGroup.ManagementPacks.GetManagementPack(managementPack.Id);

            // Populate Data
            IncrementalDiscoveryData dataTransaction = new IncrementalDiscoveryData();

            // Get System.Entity class
            ManagementPackClass systemEntity = managementGroup.EntityTypes.GetClass(SystemClass.Entity);

            // Conferences
            ManagementPackClass conference = managementPack.GetClass("NFL.Conference");
            CreatableEnterpriseManagementObject nfc = new CreatableEnterpriseManagementObject(managementGroup, conference);
            nfc[conference, "Name"].Value = "NFC";
            nfc[systemEntity, "DisplayName"].Value = "National Football Conference";
            dataTransaction.Add(nfc);

            CreatableEnterpriseManagementObject afc = new CreatableEnterpriseManagementObject(managementGroup, conference);
            afc[conference, "Name"].Value = "AFC";
            afc[systemEntity, "DisplayName"].Value = "American Football Conference";
            dataTransaction.Add(afc);

            // Divisions
            ManagementPackClass division = managementPack.GetClass("NFL.Division");
            string[] nfcDivisionNames = new string[] {
                "NFC East",
                "NFC North",
                "NFC South",
                "NFC West"
            };
            
            foreach (string nfcDivisionName in nfcDivisionNames)
            {
                CreatableEnterpriseManagementObject nfcDivision = new CreatableEnterpriseManagementObject(managementGroup, division);
                
                // Need to make sure to set key values of the host
                nfcDivision[conference, "Name"].Value = "NFC";
                
                nfcDivision[division, "Name"].Value = nfcDivisionName;
                nfcDivision[systemEntity, "DisplayName"].Value = nfcDivisionName;
                dataTransaction.Add(nfcDivision);
            }

            string[] afcDivisionNames = new string[] {
                "AFC East",
                "AFC North",
                "AFC South",
                "AFC West"
            };

            foreach (string afcDivisionName in afcDivisionNames)
            {
                CreatableEnterpriseManagementObject afcDivision = new CreatableEnterpriseManagementObject(managementGroup, division);

                // Need to make sure to set key values of the host
                afcDivision[conference, "Name"].Value = "AFC";

                afcDivision[division, "Name"].Value = afcDivisionName;
                afcDivision[systemEntity, "DisplayName"].Value = afcDivisionName;
                dataTransaction.Add(afcDivision);
            }

            // Use Overwrite instead of Commit here because I want to bypass optimistic concurrency checks which will not allow the 
            // insertion of an existing instances and I want this method to be re-runnable
            dataTransaction.Overwrite(managementGroup);
Improvements in SDK from Operations Manager 2007

I wanted to provide a high level overview of feature additions to the SDK since Operations Manager 2007 shipped. The items are in no particular order and will be discussed in more depth in subsequent posts.

Default Values for Properties

Types can define default values on properties. The default values need to be literals and are implemented using the default value column construct in SQL.

Required Properties

Types can now defined non-key required properties. Values for these properties will always be required during insertion unless the property has a default value, in which case that value will be used.

Auto-Increment Properties

Integer and string properties can be defined as auto-increment in a management pack. When instances of types that contain auto-increment properties are created in the SDK client, the client retrieves the next available value from the database and removes it from future use (whether or not the instance is committed). This value is then embedded in the property using a standard .Net format string with {0} replaced by the value from the database. As an example, this can be used to create unique, readable ticket numbers: “SR_{0}”.

Binary Properties

Binary properties have been added and are written and returned as streams. The stream is retrieved on demand and will not be brought back as part of normal instance retrieval.

Enumeration Properties

Management packs can define enumerations that can be used as property values in the instance space. These enumerations are hierarchal and extensible via other management packs.

Categories in Management Packs

Categories allow the association of an enumeration (as defined above) and a management pack element. An element can have an unlimited number of associated enumerations that act to categorize the element for various uses.

Type Extensions

Type extensions are an MP schema change to allow a derived type to be defined as an extension of its base type. The design is such that a user can add a property to a type, for all instances of that type. Thus, when someone imports a type extension, the system adds this type to all instances of the base type and all new instances will automatically be an instance of the type extension class, regardless of the data the discovery packet that discovered them contains. Type extensions can be explicitly added or updated on an instance, but cannot be removed without removing the entire instance.

Relationship Endpoints

Relationships now have sub-elements that represent the source and target endpoints. These endpoints can define cardinality value of 0, 1 and MaxInt. If max and min cardinality are both 1, it is enforced by the infrastructure, otherwise it is used as a hint to the UI and other consumers.

Instance History

The CMDB stores property and relationship changes for instances. They can be retrieved per instance from the SDK and are used by other components internally that rely on instance space changes.

Instance Change Subscriptions

Similar to the alert change subscriptions in OM, we now have an instance space change subscription data source that allows you to trigger other actions because on an instance space change. We use this to trigger WWF workflows.

Projections

A new concept in the type space, type projections allow you to define a hierarchal view over the type space that gets materialized on retrieval by the SDK client. This allows retrieval of logical instances that are composed of many instances and the relationships that join them together. The mechanism is used as the backing instance space definition for all forms and views in Service Manager; that is a view or form is bound to a particular projection.

SDK also have a lot of functionality built in to support this concept, including advanced query support over these definitions. For instance, you can define a projection that is an incident with its assigned to user and then query for all instances of this projection where to assigned to user is some particular user.

Object Templates

Object templates are essentially saved instances and projections defined in a management pack that can be used to create new instances or be applied to existing instances and set their values to those found in the template.

Type indexes

You can now define indexes on types in a management pack to help improve performance.

Criteria Improvements

Criteria has changed (we still support the old format as well) to be xml-based to remove ambiguity problems we had with the old model and to consolidate it with group calculation and subscription criteria. We’ve also enhanced criteria to support projection queries, containment queries and added an IN clause predicate. The new features are only available with the new xml format.

Full-Text Queries

Full-Text is supported for string and binary properties, with significant limitations in how the full-text query syntax can be leveraged within other criteria elements. Currently this is only being used for knowledge search within some limited scenarios.

Partial Instances

Users of the SDK can now specify which properties they want to get back when they retrieve instances. This functionality applies to all mechanisms of retrieving instances and is recommended to help with performance issues when dealing with large data sets.

Paging and Sorting

The SDK supports retrieving instances in a paged mode that returns some buffer worth of data on each remote call. This functionality should be combined with sorting that will allow for UI design that yields better performance.

Abstract Query Support

Abstract types are now supported within type-based queries, both for types and projections.

Security Enhancements

We’ve expanded the security model to support property level write granularity, group + type scoping on read and implied permissions based on the instance itself (i.e. the owner of an incident as some set of implied permissions on it).

Resource Support in Management Packs

Management packs can now have embedded resources (the file is msi-based) that separate large resources (reports, form binaries, etc) from the xml manifest of the management pack.

XPath Navigable Instance Space

With the introduction of projections it became important to have a friendlier way to traverse a large hierarchal instance on the client. To server that need projection instances and instances are both XPathNavigable.

Optimistic Concurrency on Instances

We support full-optimistic concurrency support, per instance.

Unification of CustomMonitoringObject and MonitoringObject

We’ve merged the two objects for a more consistent experience when working with the instance space in the SDK.

Getting Started with the Service Manager SDK

The Service Manager public API is partitioned into several assemblies all of which can by found either in the GAC after install or in the Program Files folder in "SDK Binaries". The primary assembly is Microsoft.EnterpriseManagement.Core (Core) which replaces Microsoft.EnterpriseManagement.Common from Operations Manager 2007. Core also contains a lot of the functionality from the Microsoft.EnterpriseManagement.OperationsManager (OM) assembly that shipped with Operations Manager 2007. The OM assembly still exists, but contains only Operations Manager specific functionality. Service Manager also has a couple other assemblies with Service Manager and Data Warehouse functionality. The following table describes the relationships between Service Manager and Operations Manager assemblies.

Service Manager Operations Manager 2007
Microsoft.EnterpriseManagement.Core Microsoft.EnterpriseManagement.Common and Microsoft.EnterpriseManagement.OperationsManager
Microsoft.EnterpriseManagement.ServiceManager (SM) None
Microsoft.EnterpriseManagement.OperationsManager Microsoft.EnterpriseManagement.OperationsManager
Microsoft.EnterpriseManagement.DataWarehouse (DW) None

The refractoring of the Operations Manager assemblies was done to enable a better story for sharing functionality across the products. Core is delivered by the Service Manager team and provides the fundamental services that System Center products built on top of the OM CMDB share. Core contains the majority of the instance space functionality, all of the management pack infrastructure and other common features across the products. SM contains functionality on the Service Manager product needs while OM is for Operations Manager only. In Beta 1 Service Manager still uses the OM assembly as we haven't completely refractored everything as necessary. The DW assembly contains new data warehouse specific functionality; this is functionality specific to the new Service Manager based data warehouse and not the old Operations Manager data warehouse functionality, which can still be found in the OM assembly.

In order to get started, you need to link at the very least against the Core assembly. Once you do that, the following code will establish a connection to the locally installed SDK service:

using System;
using Microsoft.EnterpriseManagement;

namespace Jakub_WorkSamples
{
    partial class Program
    {
        static void Connect()
        {
            // Connect to the local management group
            EnterpriseManagementGroup localManagementGroup = new EnterpriseManagementGroup("localhost");
        }
    }
}

You'll notice this part is almost identical to the Operations Manager. Once the connection is established, you can begin playing around with the various services provided by EnterpriseManagementGroup. Previously most functionality was actually directly on EnterpriseManagementGroup and while it is all still there (on ManagementGroup, derived from EnterpriseManagementGroup) , it is marked as obsolete as we are using a new paradigm for providing access to various features of the API. The new mechanism for accessing various parts of the API is centered around services (namely interfaces) exposed via properties on the EnterpriseManagementGroup object. You'll notice, for example, a property called Instances. This provides access to a variety of methods that deal with instance space access. A quick sample follows:

using System;
using Microsoft.EnterpriseManagement;
using Microsoft.EnterpriseManagement.Common;

namespace Jakub_WorkSamples
{
    partial class Program
    {
        static void GetInstance()
        {
            // Connect to the local management group
            EnterpriseManagementGroup localManagementGroup = new EnterpriseManagementGroup("localhost");

            // Get an instance by Id (need to replace with valid Id of an instance)
            EnterpriseManagementObject instance = 
localManagementGroup.Instances.GetObject<EnterpriseManagementObject>(Guid.NewGuid(), ObjectQueryOptions.Default); } } }

Over time I'll be talking about the various interfaces and the specific functionality they provide and be talking much more about the Instances interface which introduces significant new functionality from the equivalent OM objects (MonitoringObject).

This model of partitioning is used in all the assemblies and allows us to better group and share functionality across products. We're definitely still open to feedback on how to better partition our services; our goal is to group common functionality together such that most tasks would be done by using one or two services.

Also, a couple things to note. First, this is Beta 1 and there will be changes moving forward. The Instances API in particular will undergo a significant facelift as we are added buffered read support for instances. We are also cleaning up the interfaces and trying to remove unnecessary overloads as much as possible.

Service Manager Beta Released

You can find more details here.

Also, community forums are setup for discussion and can be found here. These are the preferred medium for questions with the beta.

I'm out for Thanksgiving this week, but plan to get started next week with a high-level post on fundamental changes in the SDK architecture to better accommodate Service and Operations Manager with a single, shared API.

New Look, Evolving Content

Well, it has been a while since I had any posts with new content (and it will actually be just a bit longer), but I wanted to write a quick post on what has been going on with SDK development for Operations Manager and talk a bit about Service Manager.

First off, Operations Manager is going well and tracking toward R2. I've been been involved in that development, but there is not a lot of new content that went into the SDK for that release; mostly important bug fixes were put in to address customer issues and other issues that were found. There were a few new features that went in to help with performance (such as a lightweight alert and event objects to help with views), but development on the SDK has been slow going on that front.

After SP1 (although we've been keeping changes in sync since then) of Operations Manager was ready, we took the SDK code and have been evolving it to meet the needs of Service Manager. Service Manager has much deeper and more complicated requirements of the SDK and it makes sense for that product to push and own the charter of the SDK. The plan is to have the next major release of Operations Manager be built on top of the new SDK so that Service Manager and Operations Manager truly share a large part of the infrastructure. This will help enable a lot of "better together" scenarios for both products.

We've been busy putting a ton of new features into the SDK and enabling all sorts of new scenarios to be possible over the infrastructure and we're tracking well to our Beta 1 that will be released shortly. For those that are familiar with Service Manager, formerly Service Desk, this is the first public release after the previously announced reset.

I plan to evolve this blog into discussions around this new SDK, its new features, differences with the Operations Manager 2007 and general help for the Service Manager product. I will continue to field your questions either through email or through comments here on Operations Manager, but as is already self-evident if you look at my recent blog posts, I will not be writing new posts on SCOM 2007 SDK. When the next major release of SCOM has its first public milestone, all of the content here will be pertinent to that as well.

Good Article on Management Pack Authoring

A co-worker of mine recently pointed this out to me, so I thought I would share here.

http://www.developer.com/design/article.php/3740486

System Center Virtual User Group

The System Center Virtual User Group recently had their first meeting where they presented on several topics about Operations Manager 2007. They recorded the live meeting and posted the content online. The topics were  Cross-platform monitoring in Operations Manager 2007 SP2, Powershell in Operations Manager 2007, and Audit Collection Services (ACS) Planning, Configuration and Tuning Tips. You can find all the videos linked here as well.

 

System Center Service Manager

Well, I have been pretty quiet lately with posts and I can finally share why. Over the past few months I have been very busy working on System Center Service Manager and making the Operations Manager infrastructure work for that product as well. More information about the changes in that product can be found here:

http://blogs.technet.com/systemcenter/

Given these changes, I am actually no longer on the Operations Manager team, but actually exclusively on the Service Manager core development team. What does that mean for Operations Manager and the SDK? The infrastructure I am working on builds on the existing SDK of Operations Manager API and makes it work seamlessly with Service Manager and Operations Manager. The infrastructure will be structured as a System Center programmatic infrastructure with a lot of functionality being common across products (currently Service Manager and Operations Manager), with product specific functionality built on top of it.

All this being said, the best way to use the API today is to make sure a proper level of abstraction is used in larger projects; this will minimize impact of any changes in the future.

More Posts Next page »
Page view tracker