Skip to content

Building the C# Workflow

Adam edited this page Sep 4, 2019 · 5 revisions

This walk through is currently in development

How the solution was built

The general idea here is we need a way to kick off Action Log notifications to the Affected or Assigned User and optionally perform language translation when the language of the comment is different than that of the other party per their SCSM Locale setting. The initial thought process to this whole thing was as follows:

  1. Need to trigger a WF when a Comment is added to the Action Log (doable in the Authoring Tool)
  2. Need to trigger some custom logic on that event (doable in the Authoring Tool with a PowerShell step)
  3. If the Comment Language Code is the same the Assigned/Affected User Language notify as usual. If it's different, submit to Azure, create a New Action Log Entry (e.g. Add-ActionLogEntry from the SMLets Exchange Connector) with the translated comment, and notify on the translated comment.
  4. Need to send the email to the Affected User/Assigned To but won't be able to use PowerShell's Send-MailMessage as everyone's environment is different/it would introduce something very hardcoded

So this is rather problematic as while most of those steps can be acclomplished in the Authoring tool, "most" isn't good enough for a final solution. Which means we have to turn to a C# based workflow with the SCSM SDK.

So the first course of action to understanding all of this was creating an empty management pack in the SCSM Console and creating a Subscription based on when a Comment is added to the Analyst Action Log. After all, the GUI would build the entire MP/XML structure so we can export it and examine. While it wouldn't paint the whole picture, it should tell us something about how Step 4 should theoretically work. Like any MP created in the console, it has the standard references and language section. But the part that stood out was the actual workflow section.

<AssemblyName>Microsoft.EnterpriseManagement.Notifications.Workflows.dll</AssemblyName>
<WorkflowTypeName>Microsoft.EnterpriseManagement.Notifications.Workflows.SendNotificationsActivity</WorkflowTypeName>
<WorkflowParameters>
    <WorkflowParameter Name="SubscriptionId" Type="guid">$MPElement$</WorkflowParameter>
    <WorkflowArrayParameter Name="DataItems" Type="string">
    <Item>$Data/.$</Item>
    </WorkflowArrayParameter>
    <WorkflowArrayParameter Name="InstanceIds" Type="string">
    <Item>$Data/BaseManagedEntityId$</Item>
    </WorkflowArrayParameter>
    <WorkflowArrayParameter Name="TemplateIds" Type="string">
    <Item>2ab27605-d3df-2426-825b-7eb1bef363e5</Item>
    </WorkflowArrayParameter>
    <WorkflowArrayParameter Name="PrimaryUserList" Type="string">
    <Item>b7d24f94-6285-44be-ad90-ca06dff0749e</Item>
    </WorkflowArrayParameter>
    <WorkflowArrayParameter Name="PrimaryUserRelationships" Type="string">
    <Item>
    $Context/Path[Relationship='CustomSystem_WorkItem_Library!System.WorkItemHasCommentLog' SeedRole='Target' TypeConstraint='CustomSystem_WorkItem_Library!System.WorkItem']/Path[Relationship='CustomSystem_WorkItem_Library!System.WorkItemRequestedByUser' TypeConstraint='CustomSystem_Library!System.User']$
    </Item>
    </WorkflowArrayParameter>
</WorkflowParameters>
<RetryExceptions />
<RetryDelaySeconds>60</RetryDelaySeconds>
<MaximumRunningTimeSeconds>7200</MaximumRunningTimeSeconds>

This whole section feels pretty telling. Not only are there 5 Workflow parameters, but they call a stock Microsoft SCSM DLL (AssemblyName) and a dedicated function/workflow (WorkflowTypeName) within it. Given my only experience tying an MP to a DLL is through SMlets Exchange Connector Admin Settings UI this whole syntax felt rather familiar. So the assumption made here is that function/workflow takes these 5 inputs and then it magically does the rest (e.g. it knows how to poll the Notification server and work with it whether it be anonymous/Windows Auth, etc). Because as it stands with a standard notification in the console, that's exactly how it works. Now if these DLLs are anything like the DLLs the Authoring Tool generates for PowerShell workflows, we should be able to find them on the Workflow server. Then using PowerShell we should be able to create an Object from that DLL similiar to how it's done for MimeKit in the SMLets Exchange Connector.

$commonDLL = "C:\Program Files\Microsoft System Center 2016\Service Manager\Microsoft.EnterpriseManagement.Common.dll"
$notificationsDLL = "C:\Program Files\Microsoft System Center 2016\Service Manager\Microsoft.EnterpriseManagement.Notifications.Workflows.dll"
[void][System.Reflection.Assembly]::LoadFile($commonDLL)
[void][System.Reflection.Assembly]::LoadFile($notificationsDLL)

New-Object Microsoft.EnterpriseManagement.Notifications.Workflows.SendNotificationsActivity

Which when run, outputs the following in the shell -

DataItem                 :
DataItems                :
InstanceId               :
InstanceIds              :
TemplateIds              :
TemplateIdLogString      :
SubscriptionId           : 00000000-0000-0000-0000-000000000000
PrimaryUserList          :
CarbonCopyUserList       :
BlindCarbonCopyUserList  :
PrimaryUserRelationships :
Parent                   :
Name                     : SendNotificationsActivity
Enabled                  : True
QualifiedName            : SendNotificationsActivity
Description              :
ExecutionStatus          : Initialized
ExecutionResult          : None
IsDynamicActivity        : False
UserData                 : {}
Site                     :

Now, the question that almost inevitably follows this kind of example is "why" or "how" is the Common.dll there? It's much easier than you'd think, because when you attempt to import just the Workflows.dll. PowerShell throws a very telling error message.

New-Object : Exception calling ".ctor" with "0" argument(s): "Could not load file or assembly 'Microsoft.EnterpriseManagement.Common, Version=7.0.5000.0, 
Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified."
At line:14 char:17
+ ... ification = new-object Microsoft.EnterpriseManagement.Notifications.W ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

The properties of this New-Object are interesting to say the least - as you can now see a 1 to 1 match of those Workflow Parameters for DataItem, InstanceId, TemplateID, PrimaryUserList, and PrimaryUserRelationships. Looking at the MP's XML again, depending on how you pick your users in the Subscription UI of the console - some you hard define and others you pick the relationship. What's interesting here is that the PrimaryUserList is GUIDs whereas the PrimaryUserRelationships are...well something else? Not entirely sure. Anyway, we're more interested in the GUIDs anyway because when it comes to PowerShell, SMLets or C# we should be able to lookup an object by its GUID without having to know the class. From there, swing over and grab the User's Email and Locale.

So now that we have the workflow MP that's been constructed entirely through the console, our next step would be creating a custom C# assmebly. How do we build the required assembly (DLL)? The same exact way the SMLets Exchange Connector's UI was built - a Class file in Visual Studio.

Clone this wiki locally