Archive for Service Manager Customization

Oct
25

SendEmail for Service Requests

Posted by: | Comments (2)

From time to time I see questions regarding the use of “SendEmail” with Service Requests. For those not familiar with SendEmail it’s a console task for Service Manager that can be used by an analyst to send emails to other users regarding an incident. A common usage scenario is to request additional information from the affected user of an incident. In combination with the Exchange Connector this results in a really nice solution that documents the communication between the two parties in the action log of the incident.

Since the original version of SendEmail (part of the Exchange Connector download that can be found here) didn’t support usage with Service Requests I’ve now created a dialect of the solution that’s made for usage with Service Requests.

image

image

You can find the solution at the TechNet Gallery here: TechNet Gallery – SendEmail for Service Request Fulfillment

If you like the solution please rate it at the Gallery!

Oct
06

“Quick Create Incident”

Posted by: | Comments (5)

A couple of weeks ago one of my customers were asking if it was possible to get a console task that was bound to a specific incident template. They wanted to avoid the step where they have to go search/browse for the template in the “Template Picker” when creating a new incident. I came to think about the “Incident Quick Templates” solution which gives you a “fly-out-menu” of templates when you need to apply a frequently used template on an existing incident, but that solution can’t be used to create new incidents. So I went ahead and created a custom console task that solves the problem. Here it is, my “Quick Create Incident” console task.

image

The solution allows you to specify any number of templates you want to be able to pick from a “fly-out-menu” when creating a new incident.

Download: TechNet Gallery: Quick Create Incident

Setting up the solution

To be able to use your preferred templates in the “fly-out-menu” you need to know the internal Id of the templates you want to use.

Locate a Template ID

To locate the ID of a template, follow these steps:

  1. Start the “Service Manager Shell” (within the Service Manager Console go to AdministrationStart PowerShell Session)
  2. Use the following command to locate the ID of a template (replace “incident” with any word that is part of the display name of the template you’re looking for, *=wildcard)

Get-SCSMObjectTemplate -DisplayName *Incident*|ft DisplayName,Id

  1. Make a note of the Id  for the templates you want to have access to in the “fly-out-menu”
Wiring up your templates to the “fly-out-menu”

To add menu items (which are actually separate console tasks) wired to your templates, download the solution from here: TechNet Gallery: Quick Create Incident . Extract the zip archive to “C:\Temp\QuickCreateIncident”. Now, to define which templates you want to have access to in the “fly-out-menu”, open “C:\Temp\QuickCreateIncident\Litware.IncidentManagement.QuickCreate.Library.xml” in your favorite XML editor.

In the management pack I’ve wired-up two templates as examples to the “fly-out-menu”, the “Hardware Incident Template” and the “Network Incident Template”. Each of these menu items are defined as a <ConsoleTask> in the MP, see example below.

<ConsoleTask ID="Litware.Incident.QuickCreate.Network.Task" Accessibility="Public" Enabled="true" Target="Litware.Incident.QuickCreate.GroupTask" RequireOutput="false">
        <Assembly>EnterpriseManagement!SdkDataAccessAssembly</Assembly>
        <Handler>Microsoft.EnterpriseManagement.UI.SdkDataAccess.ConsoleTaskHandler</Handler>
        <Parameters>
          <Argument Name="Assembly">Litware.SCSM.QuickCreate</Argument>
          <Argument Name="Type">Litware.SCSM.QuickCreate.TaskHandlers.QuickCreateIncidentCommand</Argument>
          <Argument Name="TemplateId">4cf10508-c474-94fd-55df-28cb7a6f3fb3</Argument>
        </Parameters>
      </ConsoleTask>

Each menu item is actually defined as separate Console Tasks that has the same Target, which is a parent Console Task. There are three important things to notice in the example above:

  • The Console Task ID needs to be unique
  • The Target needs to be Litware.Incident.QuickCreate.GroupTask
  • The TemplateID is the reference used to define which template that will be used to create the new incident
To add your own menu items
  1. Copy the XML for one of the example <ConsoleTasks> (“Network” or “Hardware”)
  2. Give the new Console Task a new Console Task ID (which is uniqe)
  3. Change the TemplateId value to the Id the template you want to use (that you located using the Get-SCSMObjectTemplate command earlier)
Setting the display name of a menu item

To add a display name of a console task, follow these steps.

  1. Locate the Console Task ID for which Console Task you want to set the display name
  2. Make sure there is a <DisplayString> defined, as below, where the Console Task ID matches the ElementID

<LanguagePack ID="ENU" IsDefault="true">
      <DisplayStrings>
        …
        <DisplayString ElementID="Litware.Incident.QuickCreate.Network.Task">
            <Name>Network Incident</Name>
            <Description />
        </DisplayString>
        …
      </DisplayStrings>
</LanguagePack>

  1. The value of the <Name> tag will be the name shown in the Service Manager console

Note: In the example above we’ve added/updated the English display string. To add display strings for other languages. See the following blog post: Localizing Management Pack Content. Also, if you delete a console task, make sure you delete the <DisplayString> as well since you will not be able to import the MP if it contains display strings that refers to deleted elements.

Setting the icon of a menu item

To specify an icon for a menu item, in the <ImageReferences> section, add a line as the example below but make sure the ElementID matches the Console Task ID of your Console Task (menu item) that you want to set the icon for.

<ImageReference ElementID="Litware.Incident.QuickCreate.Network.Task" ImageID="SMIncident!IncidentMgmt_CreateIncidentFromTemplate_16" />

Finalizing

To be able to use the custom Console Task you need to bundle the provided assembly file with Management Pack. Before doing this though, you should seal the Management Pack (otherwise an annoying confirmation dialog will show up each time you click one of the menu items).

  1. Start the “Service Manager Shell”, within the Service Manager Console go to AdministrationStart PowerShell Session)
  2. Execute the following command:

cd C:\Temp\QuickCreateIncident

  1. Execute the following command to Seal the Management Pack:

Protect-SCManagementPack  -ManagementPackFile .\Litware.IncidentManagement.QuickCreate.Library.xml -KeyFilePath .\QuickCreateKey.snk -CompanyName Litware -OutputDirectory ./

  1. Execute the following command to bundle the sealed Management Pack with the assembly containing the custom Console Task:

New-SCManagementPackBundle -ManagementPack .\Litware.IncidentManagement.QuickCreate.Library.mp -Name Litware.IncidentManagement.QuickCreate.Library.mpb -Resource .\Litware.SCSM.QuickCreate.dll

  1. Finally, import the Management Pack Bundle using the console or using the following command:

Import-SCSMManagementPack .\Litware.IncidentManagement.QuickCreate.Library.mpb

You’re done! If you like the solution rate it at the gallery: TechNet Gallery: Quick Create Incident

Note: The example menu items “Hardware Incident” and the “Network Incident” are bound to out-of-the-box templates. If you’ve deleted the templates these examples are refering to since installing Service Manager these examples will not work and should be deleted. If you decide to delete them (the ConsoleTask elements), remember to delete all elements refering to them as well (display strings and image references).

In Service Manager 2012 the console tasks used to change the status of an Incident are grouped in a parent task called "Change Incident Status". Some people I’ve talked to actually prefer the way the console tasks were presented in Service Manager 2010 where they were presented as separate tasks instead of a group, the reason mainly being that it saves one mouse click.

I’ve addressed the wish of having things the way they were by creating a management pack that hides the group and re-wires each of the child tasks to make them appear as separate tasks outside of the group.

Before importing the management pack:
image

After importing the management pack:
image

Download the management pack at TechNet Gallery: http://gallery.technet.microsoft.com/Service-Manager-2012-b558f3ac

Enjoy!

PS: After writing this post I found another post with the same topic, check out this great blog post by Dieter Gasser: “Moving the Resolve task out of the “Change Incident Status” group”

Comments (2)

In Part 1: Custom Form Templates for Service Manager 2012 I showed you how to make use of the custom form templates, this time I’ll show you how the templates were created. This blog post will not only be useful to those that wants to create templates that can be used with the Service Manager Authoring Tool, but this is also a good start for those who want to build their complete form within Visual Studio.

In the previous post I shared two templates that re-used Service Manager controls to equipe custom forms with the Related Items Tab and the History Tab. This is how they were created.

Setting up the development environment

This guide was developed on a machine that had the following components installed:

  • Visual Studio 2012
  • Service Manager 2012 Console
  • Service Manager 2012 Authoring Tool
  • Service Manager 2012 Update Rollup 2

Note: You should without any problems be able to use any version of Visual Studio 2010/2012 when following this guide.

Setting up the Visual Studio Project

  1. Fire up Visual Studio 2010/2012 and create a new “WPF User Control Library”
    Important: You need to make use of .Net Framework 3.5
    SNAGHTML2bfe415
  2. Add the following references to the project
?View Code CSHARP
Microsoft.EnterpriseManagement.UI.Control.dll 
Microsoft.EnterpriseManagement.UI.SMControls.dll 
Microsoft.EnterpriseManagement.ServiceManager.Application.Common.dll 
Microsoft.EnterpriseManagement.ServiceManager.SharedResources.dll

All but the “SharedResources” assembly can be found in the installation directory of the Service Manager console. The “SharedResources” assembly can be found in a sub directory of the “Authoring Tool installation directory” called PackagesToLoad.

Creating the Form Templates

This section will guide you in how to build a custom form template which will contain two special Service Manager controls, the RelatedItemsPane control (Related Items tab) and the History control (History tab).

  1. Delete UserControl1 and add a new user control called MyTemplate (for Part 1 of this blog post I created two templates, CITemplate and WITemplate)
  2. In the XAML code for the user control, in the UserControl tag, set the Height and Width to something like 600 to make it easier to work with the control in the Authoring Tool.
  3. In the XAML code for the user control, add the following reference
?View Code CSHARP
xmlns:smcontrols="clr-namespace:Microsoft.EnterpriseManagement.UI.WpfControls;assembly=Microsoft.EnterpriseManagement.UI.SMControls"
  1. Drag a TabControl onto the design surface.
  2. Name the existing tab item “tabItemGeneral
  3. Add two tab items – tabItemRelItems for “Related items” and tabItemHistory for “History”.
  4. Add a Grid control inside the “General Tab”

Step 4-7 should give you a TabControl XAML representation as below:

<TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <TabItem Header="General" x:Name="tabItemGeneral">
        <Grid />
    </TabItem>
    <TabItem Header="Related Items" x:Name="tabItemRelItems">
    </TabItem>
    <TabItem Header="History" x:Name="tabItemHistory">
        <smcontrols:HistoryTab></smcontrols:HistoryTab>
    </TabItem>
</TabControl>

Note: The Authoring Tool doesn’t support adding controls directly inside a TabItem, that’s the reason for adding a Grid control in step 7.

Adding the “Related Items Tab”

To re-use the “Related Items” control in your custom form template you need to make use of the RelatedItemsPane control. To add the “Related Items” control simply declare the control in your code behind:

?View Code CSHARP
private RelatedItemsPane _relatedItemsPane;

Depending on if you’re going to use the form template to create forms for Work Item classes or Configuration Item classes there’s a difference:

Alt.1 – Related Items Tab in Configuration Item Forms

Add the following two lines to the constructor (“MyTemplate()”) method of your user control:

?View Code CSHARP
_relatedItemsPane = new RelatedItemsPane(new ConfigItemRelatedItemsConfiguration());
tabItemRelItems.Content = _relatedItemsPane;

Alt.2 – Related Items Tab in Work Item Forms

Add the following lines to the constructor (“MyTemplate()”) method of your user control:

?View Code CSHARP
var paneConfig = new WorkItemRelatedItemsConfiguration("RelatedWorkItems", "RelatedWorkItemSource","RelatedConfigItems","RelatedKnowledgeArticles","FileAttachments");
_relatedItemsPane = new RelatedItemsPane(paneConfig);
tabItemRelItems.Content = _relatedItemsPane;

Note: When using the “Related Items” control you need to provide the form with a binding source that contains the required relationships (components) used by the “Related Items” control. If the form isn’t bound to a type projection that includes the relationships used by the “Related Items” control the “Related Items” control will be “disabled”. In the section called “Wiring up a working binding source” in Part 1 of this blog post there’s examples of two type projections that contains the required components, one for work item forms and one for configuration item forms.

Adding the “History Tab”

To add the “History” control to a tab item you simply add the control in the XAML code for the tab item as below:

?View Code CSHARP
<TabItem Header="History" x:Name="tabItemHistory">
	<smcontrols:HistoryTab></smcontrols:HistoryTab>
</TabItem>

Wrapping it up!

Compile the project in (Release Mode) and use the output assembly (e.g. Litware.SCSM.CustomFormTemplates.dll) as described in Part 1: Custom Form Templates for Service Manager 2012.

In this example I did’t go all the way and add localization support, for more information on localization support in custom forms see: Localizing Forms – Service Request Example

For reference, here’s the XAML code and code beind of the CITemplate and WITemplate I created in Part 1 of the blog post:

CITemplate.xaml

<UserControl x:Class="Litware.SCSM.CustomFormTemplates.CITemplate"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:smcontrols="clr-namespace:Microsoft.EnterpriseManagement.UI.WpfControls;assembly=Microsoft.EnterpriseManagement.UI.SMControls"
             mc:Ignorable="d" 
             Height="600" Width="600">
    <Grid>
        <TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
            <TabItem Header="General" x:Name="tabItemGeneral">
                <Grid/>
            </TabItem>
            <TabItem Header="Related Items" x:Name="tabItemRelItems">
            </TabItem>
            <TabItem Header="History" x:Name="tabItemHistory">
                <smcontrols:HistoryTab></smcontrols:HistoryTab>
            </TabItem>
        </TabControl>
    </Grid>
</UserControl>

CITemplate.xaml.cs

?View Code CSHARP
using Microsoft.EnterpriseManagement.ServiceManager.Application.Common;
 
namespace Litware.SCSM.CustomFormTemplates
{
    /// <summary>
    /// Interaction logic for CITemplate.xaml
    /// </summary>
    public partial class CITemplate
    {
        private readonly RelatedItemsPane _relatedItemsPane;
 
        public CITemplate()
        {
            InitializeComponent();
 
            _relatedItemsPane = new RelatedItemsPane(new ConfigItemRelatedItemsConfiguration());
            tabItemRelItems.Content = _relatedItemsPane;
        }
    }
}

WITemplate.xaml

<UserControl x:Class="Litware.SCSM.CustomFormTemplates.WITemplate"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:smcontrols="clr-namespace:Microsoft.EnterpriseManagement.UI.WpfControls;assembly=Microsoft.EnterpriseManagement.UI.SMControls"
             mc:Ignorable="d" 
             Height="600" Width="600">
    <Grid>
        <TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
            <TabItem Header="General" x:Name="tabItemGeneral">
                <Grid/>
            </TabItem>
            <TabItem Header="Related Items" x:Name="tabItemRelItems">
            </TabItem>
            <TabItem Header="History" x:Name="tabItemHistory">
                <smcontrols:HistoryTab></smcontrols:HistoryTab>
            </TabItem>
        </TabControl>
    </Grid>
</UserControl>

WITemplate.xaml.cs

?View Code CSHARP
using Microsoft.EnterpriseManagement.ServiceManager.Application.Common;
 
namespace Litware.SCSM.CustomFormTemplates
{
    /// <summary>
    /// Interaction logic for CITemplate.xaml
    /// </summary>
    public partial class WITemplate
    {
        private readonly RelatedItemsPane _relatedItemsPane;
 
        public WITemplate()
        {
            InitializeComponent();
 
            var paneConfig = new WorkItemRelatedItemsConfiguration("RelatedWorkItems", "RelatedWorkItemSource",
                                                               "RelatedConfigItems", "RelatedKnowledgeArticles",
                                                               "FileAttachments");
            _relatedItemsPane = new RelatedItemsPane(paneConfig);
            tabItemRelItems.Content = _relatedItemsPane;
        }
    }
}
Comments (7)

A while back I created some “custom form templates” to be used with the Service Manager Authoring Tool (2010). The purpose was to enable “authors” to re-use some of the existing controls in Service Manager like the “Related Items Tab” and the “History Tab” when creating new forms in the Authoring Tool. By request I’ve updated these to support Service Manager 2012. So far I’ve just added two templates that contain the “Related Items Tab” and the “History Tab”. One template is for Work Items and one is for Configuration Items.

SNAGHTMLc125cb

If you just want to try the templates out you can go ahead and download this example bundle: Litware.SCSM.FormsExample.mpb. After importing it you can use any of the following two views to try out the forms using the Create MyWorkItem or Create MyConfigItem task:

  • Work Items > My Work Items > All My Work Items
  • Configuration Items > My Config Items > All My Config Items

To learn how to use the custom templates in the Service Manager Authoring Tool let’s create two simple classes and a form for each of them (based on the templates).

Creating the Example Manangement Pack

  1. Create a folder called C:\Temp\MyCustomForms
  2. Copy Litware.SCSM.CustomFormTemplates.dll to C:\Temp\MyCustomForms
  3. Start the Service Manager Authoring Tool
  4. Create a new Management Pack called “Litware.SCSM.FormsExample.xml” in “C:\Temp\MyCustomForms

Creating a custom Configuration Item class

  1. Right click Classes and select “Create Configuration Item Class…
    image
  2. In the Create Class dialog, name the class “MyConfigItem” and click Create.
  3. Add a new property by clicking “Create Property…”, name the property “PrincipalName”.
  4. While having the new property selected, mark it as Key in the Details pane.
    image
  5. Delete the property that was automatically added (called something like Property_…) when you created the class.
    image
  6. In the confirmation dialog, click yes to delete the property.

Creating a custom Work Item class

  1. Right click Classes and select “Create Work Item Class…
  2. In the Create Class dialog, name the class “MyWorkItem” and click Create.
  3. Delete the property that was automatically added (called something like Property_…) when you created the class.

We don’t have to add a new key property to the custom Work Item class since we’re already inheriting a key property.

Using the Configuration Item Form Template

  1. Right click Forms in the Management Pack explorer, select Add Custom
    image
  2. In the Base Class dialog, select your custom class called MyConfigItem and click OK
  3. In the Add custom fom dialog, name your form MyConfigItemForm, select the following assembly C:\Temp\MyCustomForms\Litware.SCSM.CustomFormTemplates.dll
  4. Still in the Add custom form dialog, in the type selection drop down select CITemplate (which contains both a related items and the history tab) and click Create
    SNAGHTML74e66a
  5. In the the “Form Designer”, add a Label and a TextBox to General tab.
  6. Change the Content of the Label to “Principal Name”
  7. Change the Binding Path of the TextBox to PrincipalName
    image

Using the Work Item Form Template

  1. Right click Forms in the Management Pack explorer, select Add Custom
  2. In the Base Class dialog, select your custom class called MyWorkItem and click OK
  3. In the Add custom fom dialog, name your form MyWorkItemForm, select the following assembly C:\Temp\MyCustomForms\Litware.SCSM.CustomFormTemplates.dll
  4. Still in the Add custom form dialog, in the type selection drop down select WITemplate (which contains both a related items and the history tab) and click Create
  5. In the the “Form Designer”, add a Label and a TextBox to General tab.
  6. Change the Content of the Label to “Title”
  7. Change the Binding Path of the TextBox to Title
  8. Save all changes and exit the Service Manager Authoring Tool

Wiring up a working binding source

Since the control used for the Related Items tab requires a type projection with specific components to work, let’s go through the steps required to make sure we provide that.

  1. Open up C:\Temp\MyCustomForms\Litware.SCSM.FormsExample.xml in an XML editor
  2. In the <References> section, make sure you have the following references:

    <Reference Alias="WorkItem">
            <ID>System.WorkItem.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> 
    </Reference> 
    <Reference Alias="SupportingItem">
            <ID>System.SupportingItem.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> 
    </Reference> 
    <Reference Alias="CoreKnowledge">
            <ID>System.Knowledge.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> 
    </Reference> 
    <Reference Alias="ConfigurationManagement">
            <ID>ServiceManager.ConfigurationManagement.Library</ID>
            <Version>7.5.1561.0</Version>
            <PublicKeyToken>31bf3856ad364e35</PublicKeyToken> 
    </Reference>

    Note: If you already have references to any of these Management Packs, make sure the alias of the existing reference matches the alias in the XML above.

  3. In the <EntityTypes> section (below the <ClassTypes> section) add the following two type projections encapsulated in the <TypeProjections> tag
  4. <TypeProjections>
            <TypeProjection ID="MyConfigItem.Projection" Accessibility="Public" Type="MyConfigItem">
              <Component Path="$Context/Path[Relationship='WorkItem!System.WorkItemRelatesToConfigItem' SeedRole='Target']$" Alias="ImpactedWorkItem" />
              <Component Path="$Context/Path[Relationship='WorkItem!System.WorkItemAboutConfigItem' SeedRole='Target']$" Alias="RelatedWorkItem" />
              <Component Path="$Context/Path[Relationship='SupportingItem!System.ConfigItemHasFileAttachment']$" Alias="FileAttachment" />
              <Component Path="$Context/Path[Relationship='System!System.ConfigItemRelatesToConfigItem']$" Alias="RelatedConfigItem" />
              <Component Path="$Context/Path[Relationship='System!System.ConfigItemRelatesToConfigItem' SeedRole='Target']$" Alias="RelatedConfigItemSource" />
              <Component Path="$Context/Path[Relationship='CoreKnowledge!System.EntityLinksToKnowledgeDocument']$" Alias="RelatedKnowledgeArticles" />
    </TypeProjection>

    <TypeProjection ID="MyWorkItem.Projection" Accessibility="Public" Type="MyWorkItem">
              <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemHasFileAttachment']$" Alias="FileAttachments">
                <Component Path="$Target/Path[Relationship='SupportingItem!System.FileAttachmentAddedByUser']$" Alias="FileAttachmentAddedBy" />
              </Component>
              <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemRelatesToWorkItem']$" Alias="RelatedWorkItems">
                <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAffectedUser']$" Alias="RWIAffectedUser" />
                <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAssignedToUser']$" Alias="RWIAssignedUser" />
              </Component>
              <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemRelatesToWorkItem' SeedRole='Target']$" Alias="RelatedWorkItemSource">
                <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAssignedToUser']$" Alias="RelatedWorkItemAssignedTo" />
              </Component>
              <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAboutConfigItem']$" Alias="RelatedConfigItems" />
              <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAboutConfigItem' TypeConstraint='CoreKnowledge!System.Knowledge.Article']$" Alias="RelatedKnowledgeArticles" />
            </TypeProjection>
    </TypeProjections>

  5. Now let’s wire up the forms to each of the type projections. For the Config Item form, find the <Form> with ID="MyConfigItemForm" and replace Target="MyConfigItem" with Target="MyConfigItem.Projection"
  6. For the Work Item form, find the <Form> with ID="MyWorkItemForm" and replace Target="MyWorkItem" with Target="MyWorkItem.Projection"
  7. Save the file and exit the XML editor.

Creating the Management Pack Bundle

Before we’re ready to go we need to create a Management Pack Bundle which contains the Management Pack and the dll that contains the form templates.

  1. Open up a PowerShell command prompt
  2. Execute the following commands:
    import-module smlets
    cd C:\Temp\MyCustomForms
    New-MPBFile.ps1 .\Litware.SCSM.FormsExample.xml .\Litware.SCSM.FormsExample –computername SCSMComputerName

Note: I just loaded up smlets to get access to New-MPBFile.ps1, nothing else. Smlets can be found at http://smlets.codeplex.com

Taking the custom forms for a spin

  1. Import the Management Pack Bundle: Litware.SCSM.FormsExample.mpb
  2. Restart the console
  3. Create views for the custom classes (in the example files available as download in the top of the blog post I’ve already added the views)
  4. Use the console tasks Create MyConfigItem or Create MyWorkItem to try out the forms.

In Part 2 I’ll show you how the templates were created!

Important: Throughout this blog post I’m storing date model resources like managment pack classes in unseald management packs. You should always avoid this in production scenarios but since this is just to demo the templates I didn’t go through the extra steps required. Normally I would have created a separate managment pack with all data model resources within and selad that before starting up the creation of forms.

When creating views in System Center Service Manager, displaying a property of a related object can be accomplished by targeting a combination class, a.k.a. type projection. The wizard that is used to build views in the SM console let you choose which of the related objects, e.g. the affected user of an incident, that you want to bind a column to in a view. This works for all relationships that are constrained to only allow the “source object” (e.g. an incident) to relate one “target object” (e.g. the affected user). The limitations are described in detail in this blog post: http://blogs.technet.com/b/servicemanager/archive/2011/04/06/faq-why-can-t-i-add-some-columns-that-i-want-to-views.aspx

As described in the blog post the reason why we’re not able to select objects that are related to the “source class” based on a one-to-many relationship is based on the complexity around providing features like sorting etc.

Now let’s say you’re ok with not being able to sort on a column like this and would find some kind of value in showing the display name of one of your affected configuration items in a view. Maybe the reason is that you want to know if the incidents listed in the view has defined affected configuration items or not? If so, then you could make use of a XAML (which is the language that WPF and Silverlight is based on) feature that lets you bind to one element in a collection using an index. The abstracts below shows how you force a view to show one of the related objects from a “one-to-many” relationship by data binding to an index of a collection of related objects.

The first section shows a type projection defined for the purpose of having a view showing the affected user and affected configuration items of an incident. The reason for creating this type projection instead of using e.g. the “Incident (Advanced)” type projection is only for performance reasons; always try to minimize the number of components used in a type projection used by your views. Since I only need the two I created a type projection that only contains the two.

Type Projection

<TypeProjection ID="Gridpro.Incident.TypeProjetion.AffectedItems" Accessibility="Public" Type="CoreIncident!System.WorkItem.Incident">
    <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAffectedUser']$" Alias="AffectedUser" />
    <Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemAboutConfigItem']$" Alias="AffectedConfigItems" />
</TypeProjection>

Next abstract comes from the input parameter of my view that tells Service Manager that I want to makes use of the type projection described above.

View – Item Source Query Parameter

<AdvancedListSupportClass.Parameters>
	<QueryParameter Parameter="TypeProjectionId" Value="$MPElement[Name='Gridpro.Incident.TypeProjetion.AffectedItems']$" />
</AdvancedListSupportClass.Parameters>

The final abstract shows how to make use of the index when binding to the affected configuration items. Notice the “[0]” written behind the type projection component alias AffectedConfigItems.

View – Column definition

<mux:Column Name="AffectedConfigItems" DisplayMemberBinding="{Binding Path=AffectedConfigItems[0].DisplayName}" Width="100" DisplayName="Header_AboutCI" Property="AffectedConfigItems[0].DisplayName" DataType="s:String" />

In the picture below you can see the example view provided in the following management pack: Gridpro.Examples.Views.zip

Indexed binding
Jul
01

Competing workflows

Posted by: | Comments (0)

When using an AddRelationship or RemoveRelationship trigger to fire of workflows in Service Manager you might end up with an unwanted scenario of competing workflows.

Let’s say that you implement a rule that subscribes to relationships being added between a work item and configuration items (such as “configuration items to change” in a change request). The normal behavior after implementing this would be that multiple workflows where fired if multiple configuration items where related to the change request at the same time.

The behavior above is good if you want to do something specific with each configuration item being added to a change request. In certain scenarios though you might only be interested in firing of one workflow when relationships are added (or removed), no matter the amount of relationship changes being committed to the Service Manager database. If this is the case, you could end up with multiple workflows competing to finish of the same task.

Say you want to do something to a change request as soon as a (any) configuration item is added to the change request through the “configuration items to change” relationship. In this case, you only need one workflow to fire of, no matter how many configuration items were added in the transaction (commit). In scenarios like these the “top secret” BatchSize value of subscriptions comes into play. After some digging around I found that the value of BatchSize parameter actually limits the amount of write actions being executed based on a subscription (per poll). This effectively means that setting the BatchSize value to 1 will make sure that only one workflow (write action) is executed even though the condition of the subscription rule was fulfilled by multiple objects.

The MP xml below comes from a management pack where I’ve changed the BatchSize to 1 to fire of a single workflow when a configuration item(s) is added to a change request. The workflow in this specific case was used to add a special activity to all change requests which has one or more “configuration items to change”. The workflow would start with checking of the special activity had already been added, if not it would add the activity. This way, if a previous workflow has already added the activity, the following workflows (triggered by new configuration items being added) would abort at a very early stage, perfect! But if the BatchSize hadn’t been tuned to 1, adding multiple configuration items to the change (where no “configuration items to change” had previously been added) at the same time, would cause multiple workflows to fire of. All of them would think that they were first and they would end up competing for adding the special activity.

<DataSource ID="DS" TypeID="SystemCenter!Microsoft.SystemCenter.CmdbInstanceSubscription.DataSourceModule">
 <Subscription>
  <RelationshipSubscription RelType="$MPElement[Name='WorkItem!System.WorkItemAboutConfigItem']$" SourceType="$MPElement[Name='WorkItem!System.WorkItem']$" TargetType="$MPElement[Name='System!System.ConfigItem']$">
   <AddRelationship />
  </RelationshipSubscription>
  <PollingIntervalInSeconds>30</PollingIntervalInSeconds>
  <BatchSize>1</BatchSize>
 </Subscription>
</DataSource>
Dec
19

Color Coded Views

Posted by: | Comments (24)

The question has been asked a number of times: “Can the views in Service Manager be color coded?". For a while now I’ve been telling people that they can be. In this blog post I’ll show one way of doing exactly that.

Before I go into the details of how to do this I would like to take the opportunity to introduce Gridpro AB. Gridpro is the name of a new System Center (SC) focused company founded by me and my friend and co-founder Jonas Ullman. We’ll be focusing on developing products and solutions around the SC family of products. The company web site (http://www.gridpro.se) should be up within a few weeks but this blog will live on as my, Jonas and Gridpro’s contribution to the SC community. Now to the solution!

First, let’s take a look at what we want to accomplish. Using the Incident settings in Service Manager it’s possible to configure a priority matrix which is used by built in workflows to calculate the priority of an incident given the values of impact and urgency. Based on the calculated priority value I want to color code the incident in incident views in the Service Manager console according to the following table:

Priority Color
1 Red
2 Red
3 Red
4 Yellow
5 Yellow
6 Yellow
7 Green
8 Green
9 Green

The first thing to understand, which is what makes this possible, is that the column configuration of a view is XAML based code that is stored in a management pack. If you create a incident view containing the Priority property you’ll find the following line of XAML in the <Presentation> tag of the view definition within the management pack where you stored the view:

<mux:column name="Priority" displaymemberbinding="{Binding Path=Priority}"
 width="100" displayname="Priority.6bd567905b574b128823749bc7dc6e74"
 property="Priority" datatype="s:Int32" />

The code above defines the column that displays the priority value of each incident listed in the view. Now, after reading some documentation around the Service Manager Grid Views found here: Grid View Configuration and what it means. I found that the schema for the grid view column supports templates. This means that we can define how each cell in the column should be presented using any WPF control like Borders, Grids, Textboxes etc.

In the example below I’ve created a new view for incidents containing, amongst other properties, the incident priority. I’ve exported the management pack where I stored the view and added a new column definition using the cell template part of the schema. After a few minutes of serious designing Smile I came up with the following definition.

<mux:column datatype="s:Int32" property="Priority" displayname="Priority.6bd567905b574b128823749bc7dc6e74" width="110" name="PriorityColor">  
	<mux:column.celltemplate> 
		<datatemplate>  
			<border width="80" height="Auto" verticalalignment="Stretch" horizontalalignment="Stretch" background="{Binding Path=Priority, Converter={x:Static gridpro:PrioColorConverter.Default}}" cornerradius="5" borderbrush="Black" borderthickness="2"> 
				<dockpanel lastchildfill="True"> 
					<textblock verticalalignment="Center" horizontalalignment="Center" margin="0" text="{Binding Path=Priority}" /> 
				</dockpanel> 
			</border> 
		</datatemplate> 
	</mux:column.celltemplate> 
</mux:column>

I’ve basically copied the generated definition of the Priority column, given the column a new name, removed the DisplayMemberPath property and added the child elements that defines my new cell template. Also, notice that I’ve reused the generated DisplayName of the Priority column. Reusing the identifier of the Priority column DisplayName means that the column will get the header value “Priority”.

The most important part, which make the magic happen, is the binding defined of the Background property of the Border. Notice that I’m using a value converter. The value converter is a few lines of code that I’ve written in C# which takes a property value as input, applies my custom logics and returns a new value. Since the Background property expects a color I cannot simply bind the property to the incident priority, the border wouldn’t know which color to present for a value like “8”. Using the value converter which maps a priority value (1-9) and translates each of these values to value of either “green”, “yellow” or “red” the border can be displayed with the “correct” background color.

In the download, found in last in this post I’ve included an example management pack containing the view from the picture below, the assembly containing the value converter and the source code for the value converter. To try the example out:

  1. Copy the assembly “Gridpro.ChristmasGifts.Converters.dll” (containing the value converter) to the Service Manager installation path, usually “C:\Program Files\Microsoft System Center\Service Manager 2010”.
  2. Import the management pack “Gridpro.ChristmasGifts.ColorCodedView.xml”
  3. Make sure you’ve configured the Priority calculation within Incident Settings (Administration – Settings – Incident Settings).
  4. Make sure you have some incidents in your env. and go to Work Items – Incident – Merry Christmas

The result can be seen in the picture below, enjoy!

ColorCodedView

Download solution files: Color Coded Views – Gridpro Christmas Gift

Note: To learn how you can modify the cell template you should read up on basic Windows Presentation Foundation.

Be sure to read the comments made in the provided management pack.
Merry Christmas!

Comments (24)
Nov
18

Affected User SMTP Address

Posted by: | Comments (6)

Last week I got a question on how to get the smtp address of the affected user in an incident using PowerShell. The scenario where the question surfaced was when a friend was trying to execute a PowerShell script that would send an email message to the affected user of an incident in a custom console task (targeting the Incident class).

After some investigation I came up with the following PowerShell script which should do the trick.

?View Code POWERSHELL
# Author: Patrik Sundqvist
# Author blog: http://blogs.litware.se
# Requires: SCSM PowerShell Cmdlets (Beta 1 or later)
 
#Pass the id of the incident you want to get the affected user smtp address for as command line argument to the script
#Example: "GetAffectedUserSMTP.ps1 IR58"
param($incidentId)
 
Import-module smlets -Force
 
$affectedUserRelClass = Get-SCSMRelationshipClass System.WorkItemAffectedUser$
$userPref = Get-SCSMRelationshipClass System.UserHasPreference$
 
#Update done 2011-04-05 based on blog post comment from German Minicucci, thanks for the feedback!
#Locate the incident you want to find the affected user smtp address for
$incident = Get-SCSMIncident -ID $incidentId |%{$_.Object}
 
#Make sure there was an incident located with the given id
if($incident -eq $null)
{
    Write-Host 'No incident exists with given id'
}
else
{
    #Get the affected user in the located incident
    $affectedUser = Get-SCSMRelatedObject -SMObject $incident -Relationship $AffectedUserRelClass
 
    #Make sure there is an affected user specified in the incident, else abort
    if($affectedUser -eq $null)
    {
        Write-Host 'No affected user is given for the incident'$incident.DisplayName
    }
    else
    {
        #Get the endpoint related to the affected user that specifies the smtp address
        $endPoint = Get-SCSMRelatedObject -SMObject $affectedUser -Relationship $userPref|?{$_.DisplayName -like '*SMTP'}
        if($endPoint -eq $null)
        {
            Write-Host 'There is no smtp channel declared for the affected user'$affectedUser.DisplayName
        }
        else
        {
        if($endPoint.TargetAddress.length -gt 0)
            {
                #Output the located smtp address
                Write-Host $endPoint.TargetAddress
            }
            else
            {
                #No valid smtp address located for the user in the CMDB
            	Write-Host 'No valid smtp address found for user'$affectedUser.DisplayName
            }
        }
    }
}

The result can be seen in the picture!

Enjoy!

Updated 2012-02-17

  • The performance of the script has been greatly improved.
  • When comparing time I had missed to convert to UTC which is what is stored in the database.

A big THANK YOU goes out to Andrew Gault for testing and finding the UTC bug.


A common request for Service Manager is to be able to notify upon Incidents breaching
their “Response Time SLA”, the time between registration of an incident and when
the incident is assigned to someone who starts working on it. In this post I’ll
describe a solution for reacting on a “Response Time Breaches” and using Incident
Event Workflows triggered by the breach to send notifications. The solution I describe
below will be configured to send a notification to the “primary owner” of an Incident
if the Incident has been registered for more than 30 minutes without being assigned
to anyone. The solution consists of the following components:

  • Response Time Breached Property: A class type extension of the
    Incident class type consisting of one property Name: RTBreached Type: Integer DefaultValue:
    0
  • Scheduled workflow: A re-occurring workflow created using the Authoring
    Tool that runs every other minute to locate all Incidents that has breached the
    “Response Time Threshold” using the following criteria: Status equal Active CreatedDate
    less than (“Now – 30 minutes”) AssignedTo equal Null RTBreached not equal 0
  • Notification Template: Defines how the email will look like when
    the notifications are sent regarding the breach.
  • Incident Event Workflow: A workflow created directly in the console
    that will be indirectly triggered for each Incident detected by the re-occurring
    workflow.

You’ll need the following PowerShell module to make things work:

http://smlets.codeplex.com

Extending the Incident Class Type

  1. Create a new strong name key using sn.exe (if you don’t already
    have a suitable one) In case you aren’t using the “Visual Studio Command Prompt”
    (Start >> Microsoft Visual Studio 2010 >> Visual Studio Tools >>
    Visual Studio Command Prompt (2010)) you can locate sn.exe at %ProgramFiles%\Microsoft
    SDKs\Windows\v7.0A\bin\sn.exe
  2. Start the Service Manager Authoring Tool
  3. Create new Management Pack called Litware.Demo.ResponseTimeSLA.Library
  4. In the new MP, right click “Classes” and select “Create
    other class…”
  5. Choose Incident as base class
  6. Name the new class Litware.Incident.ResponseTimeExtension
  7. Set class properties as follows: Extension: True
  8. In the list of class type properties, delete the automatically added property (called
    something like “Property26”).
  9. Add a new property as follows: Internal name: RTBreached Data type: Integer Default
    value: 0
  10. Save the MP
  11. Right click Litware.Demo.ResponseTimeSLA.Library in the
    Mangement Pack Explorer
    , select Seal Management Pack
  12. In the Seal Management Pack dialog
    1. Choose choose a suitable output directory
    2. Browse to and select your strong name key
    3. Enter your company name
    4. Click Seal then Close

Creating the re-occurring workflow

  1. Back in the Authoring Tool, create a new Management Pack called Litware.Demo.ResponseTimeSLA.Config
  2. In the new MP, right click Workflows, select Create
  3. Enter the following information then click Next Name: DetectResponseTimeBreaches
    Description: “Used to detect incidents breaching their Response Time SLA”
  4. Select “Run at a scheduled time or at scheduled intervals” then
    click Next
  5. Select “Other Interval”, specify the frequency “2 minutes”
    and click Next
  6. Verify the summary and click “Create” then “Close”
  7. Drag a Windows PowerShell Script activity onto
    the design surface from the Activities ToolBox
  8. Change the name property of the activity to “MarkRTBreachedIncidents”
  9. In the Script Body of the PowerShell Activity
    paste the following script:
  10. ?View Code POWERSHELL
        # Author: Patrik Sundqvist
    # Author blog: http://blogs.litware.se
    # Requires: SCSM PowerShell Cmdlets (Beta 1 or later)
    # Update 2012-02-15 - Major imp. on performance.
     
    Import-Module -Name smlets -Force
    # Get the type projection
    $IncidentProjection = Get-SCSMTypeProjection System.WorkItem.Incident.View.ProjectionType$
    # Get id for Incident Status "Active"
    $IncidentActive = (Get-SCSMEnumeration IncidentStatusEnum.Active$).Id
    # Set the target response time to 30 minutes
    $RTSLA = New-Object TimeSpan 0,0,30,0,0
     
     
    # Define the required ObjectProjectionCriteria
    $CTYPE = "Microsoft.EnterpriseManagement.Common.ObjectProjectionCriteria"
     
    # After defining the XML based criteria, I had to replace the following charcters, "<>$" with their ASCII representation 
    $lt = [char]60 
    $gt = [char]62 
    $dollar = [char]36 
     
    $C = @"
    $($lt)Criteria xmlns="http://Microsoft.EnterpriseManagement.Core.Criteria/"$($gt)
      $($lt)Reference Id="System.WorkItem.Incident.Library" PublicKeyToken="31bf3856ad364e35" Version="7.0.6555.0" Alias="CoreIncident" /$($gt)
      $($lt)Reference Id="System.WorkItem.Library" PublicKeyToken="31bf3856ad364e35" Version="7.0.6555.0" Alias="CoreWorkItem" /$($gt)
      $($lt)Expression$($gt)
        $($lt)And$($gt)
          $($lt)Expression$($gt)
            $($lt)SimpleExpression$($gt)
              $($lt)ValueExpressionLeft$($gt)
                $($lt)Property$($gt)$($dollar)Context/Property[Type='CoreIncident!System.WorkItem.Incident']/Status$($dollar)$($lt)/Property$($gt)
              $($lt)/ValueExpressionLeft$($gt)
              $($lt)Operator$($gt)Equal$($lt)/Operator$($gt)
              $($lt)ValueExpressionRight$($gt)
                $($lt)Value$($gt){0}$($lt)/Value$($gt)
              $($lt)/ValueExpressionRight$($gt)
            $($lt)/SimpleExpression$($gt)
          $($lt)/Expression$($gt)
          $($lt)Expression$($gt)
            $($lt)SimpleExpression$($gt)
              $($lt)ValueExpressionLeft$($gt)
                $($lt)Property$($gt)$($dollar)Context/Property[Type='CoreIncident!System.WorkItem.Incident']/CreatedDate$($dollar)$($lt)/Property$($gt)
              $($lt)/ValueExpressionLeft$($gt)
              $($lt)Operator$($gt)Less$($lt)/Operator$($gt)
              $($lt)ValueExpressionRight$($gt)
                $($lt)Value$($gt){1}$($lt)/Value$($gt)
              $($lt)/ValueExpressionRight$($gt)
            $($lt)/SimpleExpression$($gt)
          $($lt)/Expression$($gt)
          $($lt)Expression$($gt)
            $($lt)UnaryExpression$($gt)
              $($lt)ValueExpression$($gt)
                $($lt)GenericProperty Path="$($dollar)Context/Path[Relationship='CoreWorkItem!System.WorkItemAssignedToUser' SeedRole='Source']$($dollar)"$($gt)Id$($lt)/GenericProperty$($gt)
              $($lt)/ValueExpression$($gt)
              $($lt)Operator$($gt)IsNull$($lt)/Operator$($gt)
            $($lt)/UnaryExpression$($gt)
          $($lt)/Expression$($gt)
          $($lt)Expression$($gt)
            $($lt)SimpleExpression$($gt)
              $($lt)ValueExpressionLeft$($gt)
                $($lt)Property$($gt)$($dollar)Context/Property[Type='CoreIncident!System.WorkItem.Incident']/RTBreached$($dollar)$($lt)/Property$($gt)
              $($lt)/ValueExpressionLeft$($gt)
              $($lt)Operator$($gt)NotEqual$($lt)/Operator$($gt)
              $($lt)ValueExpressionRight$($gt)
                $($lt)Value$($gt)1$($lt)/Value$($gt)
              $($lt)/ValueExpressionRight$($gt)
            $($lt)/SimpleExpression$($gt)
          $($lt)/Expression$($gt)
        $($lt)/And$($gt)
      $($lt)/Expression$($gt)
    $($lt)/Criteria$($gt)
    "@
     
    $cc = [string]::Format($C, $IncidentActive, (Get-Date).Subtract($RTSLA).ToUniversalTime())
    $criteria = new-object $CTYPE $cc,$IncidentProjection.__Base,$IncidentProjection.managementgroup
     
    # Get incidents matching the criteria
    $BreachedIncidents = get-scsmobjectprojection -criteria $criteria|%{$_.Object}
     
    # Marking all located incidents to trigger notification workflow
    if ($BreachedIncidents -ne $null)
    {
        $BreachedIncidents|Set-SCSMObject -Property RTBreached -Value 1
    }
  11. Save the Management Pack
  12. Go to the directory where you created the Management Packs
  13. Copy the assembly (dll) called DetectResponseTimeBreaches.dll to
    %ProgramFiles%\Microsoft System Center\Service Manager 2010\
  14. In the Service Manager console, go to Administration – Management Packs
    and import the MPs in the following order

    1. Litware.Demo.ResponseTimeSLA.Library.mp
    2. Litware.Demo.ResponseTimeSLA.Config.xml

You might need to restart the Service Manager services after deploying the workflow
assemblies.

Creating the notification template

  1. In the console, go to Administration – Notifications – Templates
  2. Create a template with the following characteristics: Name: Response
    Time Breached Template Description: Notify the primary user of
    an unassigned incident when Response Time SLA is breached Target class:
    Incident Management pack: Litware.Demo.SLAResponseTime.Config
    Subject
    : Response Time SLA Breached Message body:
    The Response Time has breached for the incident described below.Name: $Context/Property[Type='System!System.Entity']/DisplayName$
    Priority: $Context/Property[Type='CustomSystem_WorkItem_Library!System.WorkItem.TroubleTicket']/Priority$
    Primary Owner: $Context/Path[Relationship='CustomSystem_WorkItem_Incident_Library!System.WorkItem.IncidentPrimaryOwner'
    TypeConstraint='System!System.User']$?$DisplayName$? Please act now!

Creating the Incident Event Workflow

  1. Go to Administration – Workflows – Configuration
  2. Create an Incident Event Workflow with the following characteristics : Name:
    Response Time Breached Notification Check for event: When an incident
    is updated Management pack: Litware.Demo.ResponseTimeSLA.Config
    Criteria – Changed From: “RTBreached equals 0” Criteria – Changed
    To
    : “RTBreached equals 1” Enable notification: “True”
    (Checked) Add a notification: User – Primary Owner, Message Template
    – Response Time Breached Template

To try things out – create an incident, assign yourself as the primary owner and
leave it for 30 minutes then check your mailbox :)