Skip to content

Integrating External Ticketing Systems

Adam edited this page Feb 16, 2022 · 31 revisions

Whether it be an vendor's custom ticket pattern, a monitoring system's alerts, a partner notifying you of maintenance/outages, or a supplier sending you a copy of an Invoice. With Custom Rules, you can sync any system with Service Manager entirely through email. This works as generally speaking, notifications coming into SCSM from third parties/external systems would not have a known SCSM Work Item ID in the Subject of their email. In this case, rather than create the Default configured Work Item type you can define their path forward. To do this, you'll need to first enable Custom Rules within the Settings UI (v4.x and up) and define a Custom Rule. A Custom Rule consists of a few different parts:

  • Name: A friendly name you can refer to the rule by
  • Message Class: The class of message that you need to parse (more on this below)
  • Message Part: Whether to look in the Subject or the Body for a specific text pattern
  • Regex: The regular expression used to identify the pattern (more on this below)
  • Create/Update: When the pattern is found, what type of SCSM Item should be Created and/or Updated? Out of box, you can choose IR, SR, CR, or PR. You can optionally override these values to full SCSM Class names so as to drive wholly custom processing with your own PowerShell and Custom Events. (more on custom processing below)
  • Property to write/match against: When the pattern is found, the SMLets Exchange Connector will write the value to this field so subsequent updates can be performed and keyed off of this. For Work Items, it's recommended to create a Class Extension for that class such as "ExternalTicketID" or "ThirdPartyTicketID". This also has the positive side effect of simplifying reporting as well.

PLEASE NOTE: If you are using Multi-Mailbox + Custom Rules, Multi-Mailbox functionality will always take precedence if the email in question matches a Custom Rule Pattern. While this likelihood is small, it is never the less important to recognize. For example, if you have a Multi-Mailbox rule for "HR@domain.tld" that creates Service Requests and assigns them to your HR team, but the email subject/body matches a pattern for something else defined within Custom Rules the email will always generate the HR Service Request.

SMLets Exchange Connector Settings Custom Rules configuration pane

Understanding Messages Classes

Message Classes are behind the scenes descriptors for the type of message being sent. Whether it be an email, encrypted email or meeting request. The following are such examples:

  • Emails: IPM.Note
  • Encrypted Emails: IPM.Note.SMIME
  • Digitally Signed Emails: IPM.Note.SMIME.MultipartSigned
  • Meetings: IPM.Schedule.Meeting.Request
  • Meeting Cancellations: IPM.Schedule.Meeting.Canceled

You can view Message Classes in Outlook by navigating to "View", then "View Settings", click "Columns", and then on the left side of Available Columns scroll until you find "Message Class". Add this field to the right underneath "Subject" but before "From". When you click OK you'll see that your Outlook view has been updated to show the type or Message Class of the item in your inbox.

Outlook View Settings

Outlook View Settings

Outlook View Settings Adding and Removing Columns

Outlook Inbox view showing Message Class

If the message you need to process uses a custom Message Class, just head into the SCSM Console -> Lists -> Message Class and add it as a value. As always, it is reccomended to create a new unsealed MP to store your values into.

Looking for a run down of Message Classes? Check out this Microsoft doc

Building Regular Expressions

If you're not familiar with using regular expressions within PowerShell or at all, they can feel like a completely different language. But when it comes to matching text patterns, they are the tool of choice. So how can you quickly build match patterns with minimal regex experience? Here are a few examples to get started.

Let's say you watch to match:

  • CASE#09734985

    We need to match the word CASE

    [C][A][S][E]

    We need to match a # sign

    [#]

    We need to match any number [0-9], of any length +

    [0-9]+

    Now let's put it all together:

    [C][A][S][E][#][0-9]+
  • [CASE#09734985]

    Square brackets are special character within regular expressions, so we need to ensure the square bracket is taken literally. This can be done with a blackslash

    \[
    \]

    We need to match the word CASE

    [C][A][S][E]

    We need to match a # sign

    [#]

    We need to match any number [0-9], of any length +

    [0-9]+

    Now let's put it all together:

    \[[C][A][S][E][#][0-9]+\]

Want to use PowerShell to quickly assemble some skeleton Regex?

$var = "CASE#"
$letters = $var.ToCharArray()
$regex = @()
foreach ($letter in $letters)
{
    $regex += "[" + $letter + "]"
}

$regex -join ""

Need help creating your own regex? Head over to regexr.com.

Create or Update

When the pattern is found, you can choose to drive the creation of an Incident, Service Request, Problem, Change Request, or Release Record by choosing one of the following values in the drop down - IR, SR, PR, or CR. In this way, it's possible to process emails with certain patterns ever so slightly differently than every other email that comes into SCSM.

But if you're looking to do more than what's available out of box, you can instead write different values into the drop down and with Custom Events process emails matching certain patterns how you see fit. For example, let's say when [CASE#09734985] comes in. You want to do further processing as the Body of the email contains relevant information. Such as "Telephony Outage" or "Telephony Maintenance". So long as it comes from a specific email address, you want to change the Status of your "Telephony" Business Service instead of creating a Work Item. Type in "Microsoft.SystemCenter.BusinessService" into the drop down, then head into the Invoke-CustomRuleAction function within the smletsExchangeConnector_customEvents.ps1. This function executes anytime the item in this drop down is not one of the default values (IR, SR, PR, CR). This function also contains an extensive list of variables that are available for your use.

Next, let's paste in the following PowerShell:

#get the class based on what was defined in the Settings UI
$definedClass = Get-SCSMClass -name ($customWIPattern.CustomRuleItemType + "$")

#switch statement based on the Name of the Class
switch ($definedClass.Name)
{
    "Microsoft.SystemCenter.BusinessService"
    {
        if ($email.From -eq "myVendor@domain.tld")
        {
            $bizServ = Get-SCSMObject -Class $definedClass -filter "DisplayName -eq $($matches[0])"
            if ($email.Body -like "*maintenance*")
            {
                #set the business service Status as "In Maintenance"
                Set-SCSMObject -smobject $bizServ -PropertyHashtable @{"Status" = "531abc29-fe5d-0c8a-3429-8ce99fbb63dc"}
            }
            elseif ($email.Body -like "*outage*")
            {
                #set the business service Status as "Out of Service"
                Set-SCSMObject -smobject $bizServ -PropertyHashtable @{"Status" = "61082dd5-0757-b2c6-37bf-841b6ae21036"}
            }
            elseif ($email.Body -like "*service restored*")
            {
                #set the business service Status as "In Service"
                Set-SCSMObject -smobject $bizServ -PropertyHashtable @{"Status" = "5637c28c-9775-2d83-e709-937ea4835516"}
            }

            #log the event
            New-SMEXCOEvent -Source CustomEvents -EventID 2001 -Severity Information -LogMessage "The Business Service $($matches[0]) has changed it's status."
        }
        else
        {
            #wasn't from the right email address, create an Incident
            New-WorkItem -message $email -wiType "IR"
        }
    }
}

Now anytime the [[C][A][S][E][#][0-9]+] pattern matches and the email is from a specific address, change a Business Service Status. Else, create an Incident per the connector's known functionality. As a bonus, since this a SWITCH statement, you can easily scale to new use cases by introducing new switch criteria. For example, anytime anytime an Email has a Body that contains the [G][0-9]+ pattern (e.g. Azure Billing) create a Cireson Asset Management Invoice and relate it to a known Cireson Asset Management Vendor. If the Vendor doesn't exist, create it from the email domain name. Finally, attach the email to the invoice for record keeping:

#get the class based on what was defined in the Settings UI
$definedClass = Get-SCSMClass -name ($customWIPattern.CustomRuleItemType + "$")

#switch statement based on the Name of the Class
switch ($definedClass.Name)
{
    "Cireson.AssetManagement.Invoice"
    {
        #as long as the Invoice does not exist already, create it
        $invoice = Get-SCSMObject -class $definedClass -filter "$($customWIPattern.CustomRuleRegexMatchProperty) -eq $($matches[0])"
        if ($null -eq $invoice)
        {
            $invoice = New-SCSMObject -Class $definedClass -PropertyHashtable @{
                "Name" = $matches[0];
                "ReceivedDate" = $Email.DateTimeReceived
            } -PassThru

            #relate it to a Vendor based on the From, using $TextInfo we can ensure the vendor name is Capitalized
            $TextInfo = (Get-Culture).TextInfo
            $senderDomain = $TextInfo.ToTitleCase($email.From.Split("@")[1].Split(".")[0])
            $vendorClass = Get-SCSMClass -name "Cireson.AssetManagement.Vendor$"
            $invHasVendorRel = Get-SCSMRelationshipClass -name "Cireson.AssetManagement.InvoiceHasVendor$"
            $vendor = Get-SCSMObject -Class $vendorClass -Filter "Name -eq '$senderDomain'"

            #create or relate to the vendor
            if ($vendor)
            {
                New-SCSMRelationshipObject -Source $invoice -Relationship $invHasVendorRel -Target $vendor -Bulk
            }
            else
            {
                $vendor = New-SCSMObject -Class $vendorClass -PropertyHashtable @{"Name" = $senderDomain; "DisplayName" = $senderDomain} -PassThru
                New-SCSMRelationshipObject -Source $invoice -Relationship $invHasVendorRel -Target $vendor -Bulk
            }

            #add the email to the Invoice
            Add-EmailToSCSMObject -message $email -smobject $invoice
        }
        else
        {
            #the Invoice exists, attach the Email to the Invoice
            $email.Attachments | ForEach-Object {Add-FileToSCSMObject -attachment $_ -smobject $invoice}
        }

        #log the event
        New-SMEXCOEvent -Source CustomEvents -EventID 2010 -Severity Information -LogMessage "Created Invoice $($matches[0]) from email."
    }
    "Microsoft.SystemCenter.BusinessService"
    {
        if ($email.From -eq "myVendor@domain.tld")
        {
            $bizServ = Get-SCSMObject -Class $definedClass -filter "DisplayName -eq $($matches[0])"
            if ($email.Body -like "*maintenance*")
            {
                #set the business service Status as "In Maintenance"
                Set-SCSMObject -smobject $bizServ -PropertyHashtable @{"Status" = "531abc29-fe5d-0c8a-3429-8ce99fbb63dc"}
            }
            elseif ($email.Body -like "*outage*")
            {
                #set the business service Status as "Out of Service"
                Set-SCSMObject -smobject $bizServ -PropertyHashtable @{"Status" = "61082dd5-0757-b2c6-37bf-841b6ae21036"}
            }
            elseif ($email.Body -like "*service restored*")
            {
                #set the business service Status as "In Service"
                Set-SCSMObject -smobject $bizServ -PropertyHashtable @{"Status" = "5637c28c-9775-2d83-e709-937ea4835516"}
            }

            #log the event
            New-SMEXCOEvent -Source CustomEvents -EventID 2001 -Severity Information -LogMessage "The Business Service $($matches[0]) has changed it's status."
        }
        else
        {
            #wasn't from the right email address, create an Incident
            New-WorkItem -message $email -wiType "IR"
        }
    }
}

Propertry to write/match against

When the pattern is found and when the object from the previous step is created, the matched value will be written to this property. For example, let's say you're looking for [CASE#09734985] and you've chosen "IR". You now need to define which field [CASE#09734985] will be written to on the Incident. For this reason, it's suggested to have a Class Extension on Incidents such as "ExternalTicketID" or "ThirdPartyTicketID". Furthermore, you may choose to introduce this Class Extension to the other core Work Item classes to provide a level of consistency for this functionality and reporting needs.

If you're leveraging the Invoke-CustomRuleAction function and controlling Configuration Items, it's very possible that Class Extensions are not required as the property you wish to use already exists. Such as the Name of a Computer, the ID of an Invoice, the Name of a Business Service, etc.

Testing Custom Patterns against Known Patterns

If you're looking to do some sanity checking before pushing to production, you can use the following PowerShell script to locally test your match criteria and ensure that you only return a single result.

$subject = "this is about [CASE#0738454]"

#define work item prefix regexes
$irRegex = "[I][R]"
$srRegex = "[S][R]"
$prRegex = "[P][R]"
$crRegex = "[C][R]"
$maRegex = "[M][A]"
$raRegex = "[R][A]"

#the core switch within SMEXCO
switch -Regex ($subject)
{
    #SCSM core classes
    "\[$irRegex[0-9]+\]" {write-host "this was an incident" $matches[0]}
    "\[$srRegex[0-9]+\]" {write-host "this was an service request" $matches[0]}
    "\[$prRegex[0-9]+\]" {write-host "this was an problem" $matches[0]}
    "\[$crRegex[0-9]+\]" {write-host "this was an change request" $matches[0]}
    "\[$maRegex[0-9]+\]" {write-host "this was an review activity" $matches[0]}
    "\[$raRegex[0-9]+\]"  {write-host "this was a manual activity" $matches[0]}
    #merge replies test = subject is a Reply but does not contain any of the Work Item prefixes
    "([R][E][:])(?!.*\[(($irRegex)|($srRegex)|($prRegex)|($crRegex)|($maRegex)|($raRegex))[0-9]+\])(.+)" {$matches[0]}
    #Some external ticketing system
    "\[[C][A][S][E][#][0-9]+\]" {write-host "this was a 3rd party ticket ticket" $matches[0]}

    #no matches
    default {write-warning "NO MATCHES"}
}