Don’t pay the ransom I escaped

I have been a little under the radar lately lets see

Now with no other responsibilities I  can get back to this blog and my new company Savviety Consulting.

Currently I am at MMS 2012 and Vegas. Stay tuned for a new release of SCSMFacade and the new Team Foundation Server (TFS) connector.

    Calculating priority in the System Center Service Manager portal

    There seems to be an issue when you create an incident in the System Center Service Manager (SCSM) portal that the priority is calculated after the incident is saved. This results in the update incident notification workflow been triggered. So I had to figure out how to calculate priority when an incident is saved in the portal.

    I’ll show you two ways to do it the first way is with the SDK and the second with the SCSM façade api.

    var emg = new EnterpriseManagementGroup("Your SCSM Server");
    
    // ManagementPackReferences is a class in Microsoft.Mom.BuildConstants.dll
    var settingsId = new Guid(ManagementPackReferences.SYSTEM_WORKITEM_INCIDENT_GENERALSETTING_REFERENCE);
    var mp = emg.ManagementPacks.GetManagementPack(settingsId);
    var @class = mp.GetClass("System.WorkItem.Incident.GeneralSetting");
    
    // Why "DisplayName IS NOT NULL"? I borrowed it from somewhere
    
    var criteria = new EnterpriseManagementObjectCriteria("DisplayName IS NOT NULL", @class);
    var settings = emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(criteria, ObjectQueryOptions.Default).FirstOrDefault();
    if (settings == null)
    {
        throw new InvalidOperationException("Could not retrieve Default Incident Settings");
    }
    var priorityMatrix = settings[null, "PriorityMatrix"].Value.ToString();
    
    Guid urgency, inpact; // set these to the input valuew
    
    var el = XElement.Parse(priorityMatrix);
    
    // linq abuse , but I wanted to see if I can do it in one line
    var priority = el.Elements().Where(x => x.Attribute("Id").Value == urgency.ToString()).First().Elements().Where(x => x.Attribute("Id").Value == impact.ToString()).First();
    
    var priorityValue = int.Parse(priority.Value);
    

    I know I am showing off but her is how I do it

    var priorityValue = SCSMServer.IncidentSettings.GetPriority(new Guid(urgencyId), new Guid(impactId));
    

    you can set the priority in the portal code web part CreatRequest.cs in the SetValues method

     dataItem[Microsoft.EnterpriseManagement.ServiceManager.Portal.Common.Constants.Priority] =
                    PortalHelper.GetPriority(urgency["Id"].ToString(), impact["Id"].ToString());
    

    PortalHelper is a helper class I use, but I am sure you can figure it out.

    The workflow always runs twice…

    This particular problem drove me absolutely crazy. At seemingly random times, the create incident e-mail notification workflow would run twice. This was a deal-breaker to releasing System Center Service Manager (SCSM). After a couple days of trial and error I discovered that when I entered a value in an extended property the workflow ran twice. If I just entered values in the default incident class the workflow ran once. After consulting with Microsoft support we arrived at the following solution.

    Export your management pack that contains the create incident e-mail workflow. Locate the ID of your incident extension class with the following SQL query and copy it to the clipboard.

    SELECT  [ManagedTypeId], [TypeName] FROM [ManagedType] WHERE IsExtensionType = 1 Order by [TimeAdded] DESC
    

    Open it up in an editor and look for the following section.

    Your guid should be “a604b942-4c7b-2fb2-28dc-61dc6f465c68″ which is the incident ID. Replace the incident id with the extension class id.

    Now your create incident notification workflow will run once.

    SCSM MonitoringHost.exe was killing me – Fixed

    I was up all night working on this and I can’t sleep so what better way to make me sleepy them write blog posts. I’ll let you know if it works.

    Like many of you on the forums and blogs you may have experienced the System Center Service Manager (SCSM) monitoringhost.exe consuming all available CPU resources for a long period of time. I opened the case with Microsoft and we proceeded to work a decision tree into well in the night with no solution. We did determined the issue was related to the sending of e-mails whether they be notification e-mails or subscription e-mails.

    This morning while waiting for the Microsoft SCSM engineer to call back and continue our diagnosis I decided to create an SMTP server locally on the SCSM server and pointed the e-mail channel to the local SMTP server instead of Exchange. After having done this the problem went away. I created about 30 change requests and incidents all with notifications and subscriptions and monitoringhost.exe did not consume the CPU as it had done before.

    Soon after the engineer called back, I was able to walk him through this solution we changed the SMTP channel back to exchange and reproduced the problem. We changed it back to the SMTP server and did not have a problem. The engineer is going to work this solution in the lab at Microsoft. While this is a work around there still is an issue for those of you who must use Exchange for the SMTP channel.

    The case is still open and I will post any resolution on this blog.

    Change Request and Incident Data passed to Activities

    I don’t know about you guys but my people were giving me hell over the fact that you could not get parent data into activity email notifications. This was a huge problem for our implementation. Below is how I solved it.

    In a nutshell I created a custom workflow activity and then bound it to whenever a change request or incident is created. The workflow activity then copies fields into an extension class for review and manual activities. Let’s start with the management pack that extends review and manual activities

    <ClassTypes>
            <ClassType ID="Interclick.ManualActivity.ExtensionClass" Accessibility="Public" Abstract="false" Base="CoreActivity!System.WorkItem.Activity.ManualActivity" Hosted="false" Singleton="false" Extension="true">
              <Property ID="ParentTitle" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentDescription" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentArea" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentImpact" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentPriority" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentRisk" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentScheduledStart" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentScheduledEnd" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
            </ClassType>
    
            <ClassType ID="Interclick.ReviewActivity.ExtensionClass" Accessibility="Public" Abstract="false" Base="CoreActivity!System.WorkItem.Activity.ReviewActivity" Hosted="false" Singleton="false" Extension="true">
              <Property ID="ParentTitle" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentDescription" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentArea" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentImpact" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentPriority" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentRisk" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentScheduledStart" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
              <Property ID="ParentScheduledEnd" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false"  />
            </ClassType>
          </ClassTypes>
    

    You can modify this to support the fields you need for your use cases. I created as many as I could so wouldn’t have to keep changing it.

    Now create a Windows workflow activity library application in Visual Studio. Reference the following assemblies

    • System.Drawing – if you don’t know where that is you need another job.
    • Microsoft.EnterpriseManagement.Core – this can be found in the “SDK Binaries” directory on the SCSM server
    • Microsoft.ServiceManager.WorkflowAuthoring.ActivityLibrary – this can be found in the SCSM authoring “PackagesToLoad” directory
    • System.ServiceManager.Facade – you can get this on CodePlex by downloading the current source code and compiling

    Below is the code that I used in my workflow activity, I was under a very tight deadline so no comments from the peanut gallery. I will clean it up later.

     protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                SCSMServer.SetCurrentManagementGroup("localhost");
                var changeRequestProjection = SCSMChangeRequest.GetChangeRequest(WorkItemId);
    
                foreach (var activityProjection in changeRequestProjection)
                {
                    var activity = activityProjection.Value.Object;
                    if (activity[null, "Id"] == null) break;
    
                    activity[null, "ParentId"].Value = (changeRequestProjection.Object[null, "Id"].Value != null)
                                                             ? changeRequestProjection.Object[null, "Id"].Value.ToString
                                                                   ()
                                                             : null;
    
                    activity[null, "ParentTitle"].Value = (changeRequestProjection.Object[null, "Title"].Value != null)
                                                               ? changeRequestProjection.Object[null, "Title"].Value.ToString
                                                                     ()
                                                               : null;
    
                    activity[null, "ParentDescription"].Value =
                        (changeRequestProjection.Object[null, "Description"].Value != null)
                            ? changeRequestProjection.Object[null, "Description"].Value.ToString()
                            : null;
    
                    activity[null, "ParentArea"].Value = (changeRequestProjection.Object[null, "Area"].Value != null) ? ((ManagementPackEnumeration)changeRequestProjection.Object[null, "Area"].Value).DisplayName : null;
    
                    activity[null, "ParentImpact"].Value = (changeRequestProjection.Object[null, "Impact"].Value != null) ? ((ManagementPackEnumeration)changeRequestProjection.Object[null, "Impact"].Value).DisplayName : null;
    
                    activity[null, "ParentPriority"].Value = (changeRequestProjection.Object[null, "Priority"].Value != null) ? ((ManagementPackEnumeration)changeRequestProjection.Object[null, "Priority"].Value).DisplayName : null;
    
                    activity[null, "ParentRisk"].Value = (changeRequestProjection.Object[null, "Risk"].Value != null) ? ((ManagementPackEnumeration)changeRequestProjection.Object[null, "Risk"].Value).DisplayName : null;
    
                    if ((changeRequestProjection.Object[null, "ScheduledStartDate"].Value != null))
                    {
                        var value = changeRequestProjection.Object[null, "ScheduledStartDate"].Value.ToString();
                        var date = DateTime.Parse(value);
                        activity[null, "ParentScheduledStart"].Value = date.ToString("g");
                    }
    
                    if ((changeRequestProjection.Object[null, "ScheduledEndDate"].Value != null))
                    {
                        var value = changeRequestProjection.Object[null, "ScheduledEndDate"].Value.ToString();
                        var date = DateTime.Parse(value);
                        activity[null, "ParentScheduledEnd"].Value = date.ToString("g");
                    }
                    changeRequestProjection.Overwrite();               
    
                }
    
              return ActivityExecutionStatus.Closed;
    
            }
    

    Please note “if (activity[null, "Id"] == null) break;” for some reason at least on my box the foreach loop does not stop on the last activity and so without this statement it throws null reference exceptions.

    To get all that in a workflow see this link. On a side note I would use the management pack that you used to extend the activity types.

    In the System Center Service Manager (SCSM) authoring tool you will need to create two workflows using your custom activity. One workflow is for when incident is created and the other for when a change request is created assuming you want to do this on both objects.

    So import your management pack and create a change request or incident save it and when you go back to the activities, open one of them and you should see the following at least this is what I see

    You can then use these fields in manual and review activity notifications. I am working on a solution to the problem of adding activities once a change request or incident has been created. Since the parent ID is stored in a sibling activity I should be able to figure it out. Same Bat Time Same Bat Channel.

    As Travis said “Sweet Hotness”.

    Fruits of a lazy programmer

    I am lazy. I hate writing repetitive code. I hate it when my programmers write repetitive code. I hate reading repetitive code. So when I started to write custom XAML forms for my Software Release management pack for System Center Service Manager (SCSM), I saw the repetitive lines in the management pack. Crap. So I wrote the little utility below to produce the string entities for a management pack. Enjoy

    <StringsFromXAML FormId="interclick.ServiceManager.SoftwareChangeRequest.Form">
      <FormStrings>
        <FormString ID="Label_Software_Change_Request_Information">$MPElement[Name="Microsoft.Demo.ServiceRequest.Form.Label_Software_Change_Request_Information"]$</FormString>
        <FormString ID="Label_Title">$MPElement[Name="Microsoft.Demo.ServiceRequest.Form.Label_Title"]$</FormString>
    <!-- omitted for brevity-->
      </FormStrings>
      <StringResources>
        <StringResource ID="interclick.ServiceManager.SoftwareChangeRequest.Form.Label_Software_Change_Request_Information" />
        <StringResourceID="interclick.ServiceManager.SoftwareChangeRequest.Form.Label_Title" />
    <!-- omitted for brevity-->
       </StringResources>
      <DisplayStrings>
        <DisplayString ElementID="interclick.ServiceManager.SoftwareChangeRequest.Form.Label_Software_Change_Request_Information">
          <Name />Software_ Change_ Request_ Information</DisplayString>
        <DisplayString ElementID="interclick.ServiceManager.SoftwareChangeRequest.Form.Label_Title">
          <Name />Title</DisplayString>
    <!-- omitted for brevity-->
       </DisplayStrings>
    
    </StringsFromXAML>
    
    class Program
        {
            /*
             *
             * Control Examples
             <Label Grid.Column="1"  Content="{Binding Strings[Label_Software_Change_Request_Information].Value, RelativeSource={RelativeSource FindAncestor, AncestorType=FormsInfra:FormView, AncestorLevel=1}, FallbackValue='[Software Change Request Information]', Converter={StaticResource FormatterConverter}, ConverterParameter=\{0\}:}" />
             <GridViewColumn DisplayMemberBinding="{Binding Title}" Width="Auto" Header="{Binding Strings[Header_Title].Value, RelativeSource={RelativeSource FindAncestor, AncestorType=FormsInfra:FormView, AncestorLevel=1}, FallbackValue='[Title]' }" />
    
             */
    
            const string formIdentifier = "interclick.ServiceManager.SoftwareChangeRequest.Form";
    
            static Regex regex = new Regex(@"Strings\[(.*?)\]\.Value");
            static Regex regex1 = new Regex(@"(?<!^)(?=[A-Z])");
            static XElement formStrings = new XElement("FormStrings");
            static XElement stringResources = new XElement("StringResources");
            static XElement displayStrings = new XElement("DisplayStrings");
            static void Main(string[] args)
            {
    
                var root = new XElement("StringsFromXAML");
                root.Add(new XAttribute("FormId", formIdentifier));
                using (var reader = new XmlTextReader(new System.IO.FileStream(@"C:\Research\interclick.ServiceManager.SoftwareRelease\interclick.ServiceManager.SoftwareChangeRequest\SoftwareChangeRequest.xaml", FileMode.Open)))
                {
                    while (reader.Read())
                    {
                        if (reader.IsStartElement() && reader.Name.Equals("Label"))
                        {
                            //TODO GridViewColumn.Header 
    
                            reader.MoveToAttribute("Content");
                            if (regex.IsMatch(reader.Value))
                            {
                                var val = regex.Match(reader.Value).Groups[1].Value;
                                AddFormStrings(val);
                                AddStringResources(val);
                                AddDisplayStrings(val, "Label_");
                            }
    
                        }
                        //    <GridViewColumn DisplayMemberBinding="{Binding SequenceID}" Width="Auto" Header="{Binding Strings[Header_SequenceID].Value,RelativeSource={RelativeSource FindAncestor, AncestorType=FormsInfra:FormView, AncestorLevel=1}, FallbackValue='[Software Change Request Information]' }" />
    
                        else if (reader.IsStartElement() && reader.Name.Equals("GridViewColumn"))
                        {
                            reader.MoveToAttribute("Header");
                            if (regex.IsMatch(reader.Value))
                            {
                                var val = regex.Match(reader.Value).Groups[1].Value;
                                AddFormStrings(val );
                                AddStringResources(val);
                                AddDisplayStrings(val,"Header_");
                            }
                        }
    
                    }
                }
    
                root.Add(formStrings);
                root.Add(stringResources);
                root.Add(displayStrings);
    
                root.Save("..\\..\\StringsFromXAML.xml");
    
                Console.ReadLine();
    
            }
    
            private static void AddDisplayStrings(string val,string prefix)
            {
                var element = new XElement("DisplayString");
                var attr = new XAttribute("ElementID", string.Format("{0}.{1}", formIdentifier, val));
                element.Add(attr);
                // ugly yuck
                var text = string.Join(" ", regex1.Split(val.Replace(prefix, "")));
                element.Add(new XElement("Name"),text );
                displayStrings.Add(element);
            }
    
            private static void AddStringResources(string val)
            {
                var element = new XElement("StringResource");
                var attr = new XAttribute("ID", string.Format("{0}.{1}",formIdentifier, val));
                element.Add(attr);
                stringResources.Add(element);
    
            }
    
            private static void AddFormStrings(string val)
            {
                var element = new XElement("FormString");
                var attr = new XAttribute("ID", val);
                element.Add(attr);
                element.SetValue(string.Format("$MPElement[Name=\"Microsoft.Demo.ServiceRequest.Form.{0}\"]$",val));
                formStrings.Add(element);
            }
    

    What No Global Variables?

    I like having a dev environment for System Center Service Manager (SCSM). What I don’t like is having to edit all the e-mail templates when I export my management packs with the development server name and then import them into the production server. So I came up with the following solution. I would take the four items I use the most in e-mail templates and extend the class to include a default server name variable. You can see the way I extended the types below. I can now use this variable like this.

    http://$Context/Property[Type='f099c58d-f181-db4a-cbf1-3c75c2181599']/013949da-886b-b387-9724-674e70b5838f$/Images/interclick.png

      <ClassTypes>
            <ClassType ID="Interclick.Global.Incident.ExtensionClass" Accessibility="Public" Abstract="false" Base="IncidentLibrary!System.WorkItem.Incident" Hosted="false" Singleton="false" Extension="true">
              <Property ID="DefaultServerName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" DefaultValue="BOCA-SCSM-2" />
            </ClassType>
    
            <ClassType ID="Interclick.Global.ChangeRequest.ExtensionClass" Accessibility="Public" Abstract="false" Base="ChangeRequestLibrary!System.WorkItem.ChangeRequest" Hosted="false" Singleton="false" Extension="true">
              <Property ID="DefaultServerName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" DefaultValue="BOCA-SCSM-2" />
            </ClassType>
    
            <ClassType ID="Interclick.Global.ManualActivity.ExtensionClass" Accessibility="Public" Abstract="false" Base="ActivityLibrary!System.WorkItem.Activity.ManualActivity" Hosted="false" Singleton="false" Extension="true">
              <Property ID="DefaultServerName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" DefaultValue="BOCA-SCSM-2" />
            </ClassType>
    
            <ClassType ID="Interclick.Global.ReviewActivity.ExtensionClass" Accessibility="Public" Abstract="false" Base="ActivityLibrary!System.WorkItem.Activity.ReviewActivity" Hosted="false" Singleton="false" Extension="true">
              <Property ID="DefaultServerName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" DefaultValue="BOCA-SCSM-2" />
            </ClassType>
          </ClassTypes>
    

    This has worked flawlessly and save me alot of time.

    Purpose and Philosophy in the System.ServiceManager.Facade API

    Let me tell you about my philosophy in creating this project for System Center Service Manager (SCSM). I did not want to create a complicated object model because I felt that would interfere with each implementation of SCSM. For example my implementation of a SCSM Incident is not your implementation of Incident. In stead I used T4 templates in Visual Studio to dynamically generate code so each implementation could have an custom tailored API.

    So what I created was a series of static helper objects that masked some of the underlying complexity.

    I am open to suggestions as to where to go next but here are some of the things that are on my list.

    • Adding the rest of the overrides in the existing objects. For example all of the overrides for GetObject etc.
    • Adding OR to the criteria builder.
    • SCSMWorkflow A workflow helper object.
    • SCSMTemplate A template helper object.
    • SCSMConsoleTask A console task helper object
    • SCSMActiveDirectory An active directory helper object that will facilitate various active directory tasks such is new user and add user to group.
    • SCSMType and SCSMExtendedType Type helper object

    I am currently working on a connector framework for System Center Service Manager (SCSM) to make it easier to write connectors and I hope to have it finished within the next couple of days. This approach will be more of a traditional object model approach with the help of some of the SCSM helper objects. I will be building a Team Foundation Server(TFS) connector as the first connector with the framework.

    Also I have a couple of real-world use cases that I’ve been asked to do my own company interclick.

    A console task on Change Management that when you create another Change Management request it automatically relates it to the parent Change Management item.

    A list of what they call interested parties that don’t fit the normal roles that can receive e-mail notifications. For example if we release a feature for a department in our company being able to notify the employees in that Department.

    Any other ideas would be appreciated.

    Complicated Searches In System Center Service Manager (SCSM)

    One of the things I had to do in coding System Center Service Manager (SCSM) was search for file attachments. You don’t even want to see the ugly code you have to write to do this. With the System.ServiceManager.Facade API it looks like this

     public EnterpriseManagementObject GetFileAttachment(incidentID, name)
    {
     var builder = new CriteriaBuilder();
     builder.AddReferences("ServiceManager.IncidentManagement.Library", "CoreIncident");
     builder.AddReferences("System.WorkItem.Library", "WorkItem");
     builder.AddReferences("System.SupportingItem.Library", "SupportingItem");
     var simpleExpressions = new List<SimpleExpression>
               {
                   new SimpleExpression(CriteriaBuilder.NameSpace,
                    "$Target/Property[Type='CoreIncident!System.WorkItem.Incident']/Id$",
                    incidentId,
                    ExpressionOperator.Equal),
                    new SimpleExpression(CriteriaBuilder.NameSpace,
                                                    "$Target/Path[Relationship='WorkItem!System.WorkItemHasFileAttachment' SeedRole='Source'                                   TypeConstraint='SupportingItem!System.FileAttachment']/Property[Type='SupportingItem!System.FileAttachment']/DisplayName$",
                                                                     displayName, ExpressionOperator.Equal)
                                            };
    
     var andExpression = new AndExpression(CriteriaBuilder.NameSpace, simpleExpressions);
    
     var criteria builder.CreateCriteria(SCSMServer.CommonTypeProjections.IncidentProjection, andExpression);
    
     fileName = SecurityElement.Escape(name);
     try  {
            ObjectProjectionCriteria criteria = SearchAttachmentCriteria(incidentId, fileName);
            return SCSMServer.GetObjectProjectionReader(criteria, ObjectQueryOptions.Default).FirstOrDefault();
            }
            catch (InvalidCriteriaException exception)
            {
              Exception innerException = exception.InnerException;
              LogMessage(innerException.Message + "/r/n" + innerException.StackTrace);
              throw;
             }
    }
    

    Next week I am working on adding workflows to the API, it may take a while.

    System Center Service Manager Facade API

    Back in late December my boss asked me to take a look at System Center Service Manager (SCSM) and start customization and implementation until we could onboard a dedicated engineer.  I spent most of December writing an exchange connector which Microsoft released about a day before my own was complete. That’s the way goes sometimes.

    SCSM has a great technical blog that you can read here. Travis and his team have done an excellent job with examples of customize and extend the system.

    You’ll also want to check out Power Workflow for Service Manager here. This software is a great and the customer service is excellent.

    In reviewing how customizations are made in System Center Service Manager (SCSM), I soon realized that this would be a very different style of coding than I have been used to in the past. As with most of these types of systems even the extensibility is extensible. So normal composition or aggregation object model patterns of coding just don’t work effectively.

    SCSM revolves around five major areas

    • Users
    • Incidents
    • Change Management Requests
    • Activities
    • Problems

    The whole system uses Windows Workflow Foundation workflows. There is an authoring tool that will allow you to  extend classes and create custom workflows. In my case we needed more so I cranked up vs 2010 and started coding.

    The first coding concept you have to grasp is that of a Type Projection. Everything you think is an object is actually a Type Projection. A weak analogy would be that of a SQL view. A SQL view can contain data one table or many related tables.

    So let me show you some code. This is how you retrieve an incident. This is just the basic information contained in an incident.

    EnterpriseManagementGroup emg = new EnterpriseManagementGroup(Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\System Center\2010\Service Manager\Console\User Settings",
    "SDKServiceMachine", "localhost").ToString());
    
    // what guid is it?
    ManagementPackTypeProjection typeProjection = emg.EntityTypes.GetTypeProjection(new Guid("285CB0A2-F276-BCCB-563E-BB721DF7CDEC"));
    
    // try remembering all this
    var criteriaXml = "<Criteria xmlns=\"http://Microsoft.EnterpriseManagement.Core.Criteria/\"> <Reference Id=\"System.WorkItem.Incident.Library\" Version=\"7.0.6555.0\" PublicKeyToken=\"31bf3856ad364e35\" Alias=\"IncidentMP\" /><Expression><SimpleExpression><ValueExpressionLeft><Property>$Target/Property[Type='IncidentMP!System.WorkItem.Incident']/Idlt;/Property></ValueExpressionLeft><Operator>Equal</Operator><ValueExpressionRight><Value>IR201</Value></ValueExpressionRight></SimpleExpression></Expression></Criteria>";
    
    ObjectProjectionCriteria criteria = new ObjectProjectionCriteria(criteriaXml, typeProjection, emg);
    
    var incidentProjection = emg.EntityObjects.GetObjectProjectionReader<EnterpriseManagementObject>(criteria, ObjectQueryOptions.Default);
    

    Not to be trite, but “Houston we have a problem”. There is no way I’m going to be able to be productive having to code this verbosely. So I’ve spent the last three weeks not only doing my own customizations but writing a facade API to make my life a lot easier. I also figured to give back to the community as well by sharing it. So I have my first project on CodePlex http://http://scsmfacade.codeplex.com/. You can download the bits of there. Here is an example of the same use case.

     var incidentProjection = SCSMIncident.GetIncident("IR201");

    Now that is what I like to see.

    I’ll be posting more examples for coding in System Center Service Manager (SCSM) and adding to the API as I come across more more use cases, in the meantime enjoy.