Testing, Tips, and Tricks for Inbound Webhooks

Liquid Template Language

The Liquid Template Language is a powerful tool that really makes it easy to manipulate the data that has come in from your webhooks. It’s not feasible for us to document the whole language here, but we will include some of the basics and some of the resources we have found the most helpful.

The Basics

The core of Liquid comes down to three basic concepts, Objects, Tags, and Filters. If you can learn the basics of each of these you should be well on your way to making the most of it in Connection Center.

Objects

Objects are the things that contain your data and are used to output it to any component that uses Liquid. Objects often have properties that you can dig into. In Liquid these are displayed when they are referenced using double curly braces: {{ and }}. To provide an example:

JSON Input

Liquid Transformation

Raw Output

{
    "user": {
        "firstName": "Feathers",
        "lastName": "McGraw"
    }
}
{{user.firstName}}
Feathers

In the example, Liquid is rendering the content of the firstName property on the user object, which contains the text Feathers.

Tags

Tags allow you to create logic and flow through your template. They are referenced using curly brace percentage delimiters: {% and %} and the text that they surround does not produce any output. They allow you to assign variables, check conditions, and create loops without showing any of the Liquid logic driving the process.

JSON Input

Liquid Transformation

Raw Output

{
    "user": {
        "firstName": "Feathers",
        "lastName": "McGraw"
    }
}
{% if user.firstName and user.lastName %}
  {{user.firstName}} {{user.lastName}}
{% elsif user.firstName %}
  {{user.firstName}}
{% else %}
  I don't know what else to write
{% endif %}
⠀
  Feathers McGraw
⠀

When using tags, by default whitespace is honored. You can override this behavior by using curly brace percentage hyphen delimiters: {%- and -%}. To control whitespace on the left, add the hyphen to the left side of a tag {%- %}. To control whitespace on the right, add the hyphen to the right side of the tag {% -%}. If you don’t want any of your tags to print whitespace, you can add them to both sides:

JSON Input

Liquid Transformation

Raw Output

{
    "user": {
        "firstName": "Feathers",
        "lastName": "McGraw"
    }
}
{%- if user.firstName and user.lastName -%}
  {{user.firstName}} {{user.lastName}}
{%- elsif user.firstName -%}
  {{user.firstName}}
{%- else -%}
  I don't know what else to write
{%- endif -%}
Feathers McGraw

Filters

Filters are used to manipulate your objects and variables. They are used within double curly braces {{ }} like objects but are separated from the source by a pipe character |.

JSON Input

Liquid Transformation

Raw Output

{
    "computer": "EXAMPLE01"
}
{{ computer | append: ".domain.tld" }}
EXAMPLE01.domain.tld

You can also chain together multiple filters:

JSON Input

Liquid Transformation

Raw Output

{
    "computer": "EXAMPLE01"
}
{{ computer | downcase | append: ".domain.tld" }}
example01.domain.tld

Back to top

External Resources

The support team has a little Liquid tool that helps with testing/generating liquid transformations. Let us know if this is something you might be interested in.

Back to top

Criteria Expression Syntax

SCOM provides what is known internally as its Criteria Expression Syntax. This is used to find objects in the Operations Manager database and is an integral part of dynamically assigning Events to objects when using Inbound Event Webhooks You can view the full documentation on the topic here, however, we also have the key points below.

Comparison Operators

You can use comparison operators when you are constructing a criteria expression. The valid operators are described in the following table.

Operator

Description

Examples

===

Evaluates to true if the left and right operand are equal.

Name = 'mymachine.mydomain.com'

!=<>

Evaluates to true if the left and right operand are unequal.

Name != 'mymachine.mydomain.com'

>

Evaluates to true if the left operand is greater than the right operand.

Severity > 0

<

Evaluates to true if the left operand is less than the right operand.

Severity < 2

>=

Evaluates to true if the left operand is greater than or equal to the right operand.

Severity >= 1

<=

Evaluates to true if the left operand is less than or equal to the right operand.

Severity <= 3

LIKE

Evaluates to true if the left operand matches the pattern that is defined by the right operand. Use the characters in the wildcard table in this topic to define the pattern.

Name 'LIKE SQL%'

Evaluates to true if the Name value is "SQLEngine."

Name LIKE '%SQL%'

Evaluates to true if the Name value is "MySQLEngine."

MATCHES

Evaluates to true if the left operand matches the regular expression that is defined by the right operand. For information about and examples of regular expression syntax, see the MSDN topic, .NET Framework Regular Expressions.

Name MATCHES 'SQL*05'

Evaluates to true if the Name value is "SQL2005."

IS NULL

Evaluates to true if the value of the left operand is null.

ConnectorId IS NULL

Evaluates to true if the ConnectorId property does not contain a value.

IS NOT NULL

Evaluates to true if the value of the left operand is not null.

ConnectorId IS NOT NULL

Evaluates to true if the ConnectorId property contains a value.

IN

Evaluates to true if the value of the left operand is in the list of values that are defined by the right operand.

The IN operator is valid for use only with properties of type Guid

Id IN ('080F192C-52D2-423D-8953-B3EC8C3CD001', '080F192C-53B2-403D-8753-B3EC8C3CD002')

Evaluates to true if the value of the Id property is one of the two globally unique identifiers provided in the expression.

AND

Evaluates to true if the left and right operands are both true.

Name = 'SQL%' AND Description LIKE 'MyData%'

OR

Evaluates to true if either the left or right operand is true.

Name = 'SQL%' OR Description LIKE 'MyData%'

NOT

Evaluates to true if the right operand is not true.

NOT (Name = 'IIS' OR Name = 'SQL')

Back to top

Wildcard Characters

The following table defines the wildcard characters you can use to construct a pattern when using the LIKE operator.

Wildcard

Description

Example

%

A wildcard that matches any number of characters.

Name LIKE 'SQL%'

Evaluates to true if the Name value is "SQLEngine."

Name LIKE '%SQL%'

Evaluates to true if the Name value is "MySQLEngine."

_

A wildcard that matches a single character.

Name LIKE 'SQL200_'

Evaluates to true for the following Name values:

"SQL2000"

"SQL2005"

The expression evaluates to false for "SQL200" because the symbol _ must match exactly one character in the Name value.

[]

A wildcard that matches any one character that is enclosed in the character set.

Brackets are also used for qualifying references to MonitoringObject properties. For more information, see Defining Queries for Monitoring Objects.

Name LIKE 'SQL200[05]'

Evaluates to true for the following Name values:

"SQL2000"

"SQL2005"

The expression evaluates to false for

"SQL2003."

[^]

A wildcard that matches any one character that is not enclosed in the character set.

Name LIKE 'SQL200[^05]'

Evaluates to true for

"SQL2003."

The expression evaluates to false for

"SQL2000" and

"SQL2005."

Back to top

Improving Query Performance

Whenever it is possible, avoid using wildcard characters in the criteria expression for a query. If it is necessary to use a wildcard character, limit the criteria as narrowly as possible before you use the character.

For example, when you define a criteria expression to find management servers named MSTest01, MSTest02A, and MSTest02B, the criteria expression:

Name LIKE 'MSTest0%'

is more efficient than the expression:

Name LIKE 'MSTest%'

Both of the preceding expressions are more efficient than the following expression:

Name LIKE '%Test%'

Back to top

Testing with PowerShell

PowerShell usually provides a quick and easy method of calling one of our webhooks for testing purposes. For the most part, we have written the following based on PowerShell V3 (V1 and 2 users, you have our deepest sympathies), however, we will attempt to call out specifically anything that has a version requirement higher than that.

Identify URI

The first thing to do is identify the URI you will be testing. From the SCOM UI you can get the framework from the UI using the 'Connection Details' pane:

Next check out the ‘Global settings' and the 'Listening Hostname’ If you are using the default hostname of + you should be able to use any valid address to connect. If you are not, then adjust our examples as required.

Similarly, you should check to see if there is an API Key in place.

Remoting onto the Management Server itself is usually a good place to start. That way you can simply use localhost as part of the URI:

$URI = 'http://localhost:7847/Alert/AlertEndpoint'

If you are using an API Key append this as part of the query:

$URI = 'http://localhost:7847/Alert/AlertEndpoint?ApiKey=APIKey'

If your API Key has special characters in it, you may be required to URL encode them first:

$APIKey = [System.Web.HttpUtility]::UrlEncode('SpecialChars^&*')

$URI = "http://localhost:7847/Alert/AlertEndpoint?ApiKey=$APIKey"

Cmdlets

Invoke-WebRequest and Invoke-RestMethod are the two cmdlets that would be used. The former requires you to do more to manipulate the data, the latter drops data in favor of doing additional processing. We’re using Invoke-WebRequest in these examples, but both examples work very similarly.

Parameters

All of our Webhooks currently require the POST Method, so we set this using the -Method parameter:

Invoke-WebRequest -Method Post

We need to set the Content-Type header. We can either do this using the -ContentType parameter or the -Headers parameter:

Invoke-WebRequest -Method Post -Uri $URI -ContentType 'application/json'
Invoke-WebRequest -Method Post -Uri $URI -Headers @{'Content-Type' = 'application/json'}

If we wish to provide data to the webhook, we do this using the -Body parameter. There are two major approaches to this, using a hashtable to build up content and then converting it using the ConvertTo-JSON cmdlet. Or using Here-Strings.

Invoke-WebRequest -Method Post -Uri $URI -ContentType 'application/json' -Body (@{message = 'Paste your sample payload here'} | ConvertTo-Json)
Invoke-WebRequest -Method Post -Uri $URI -ContentType 'application/json' -Body @'
{
    "message":"Paste your sample payload here"
}
'@

Here-Strings are typically easier when you can simply copy and paste the payload. Conversion is typically easier for creating things from scratch.

SSL

If you are using SSL Certificates, there are some extra hoops that you may need to jump through.

All of the following will need to be run before you call your webhook

Firstly, you may need to add TLS 1.2 support:

If ([Net.ServicePointManager]::SecurityProtocol -notlike '*Tls12*'){
  [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12
}

If you are using PowerShell Core you can use the -SslProtocol parameter instead

If you are using self-signed (or otherwise untrusted) certificates, you may also need to ignore these issues:

add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@

[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

If you are using PowerShell Core you can use the -SkipCertificateCheck parameter instead

Output

If you are using Invoke-WebRequest, you can expect something similar to:

StatusCode        : 200
StatusDescription : OK
Content           : "Event inserted\r\n "
RawContent        : HTTP/1.1 200 OK
                    Content-Length: 21
                    Content-Type: application/json; charset=utf-8
                    Date: Tue, 30 Nov 2021 17:28:00 GMT
                    Server: Microsoft-HTTPAPI/2.0
                    
                    "Event inserted\r\n "
Forms             : {}
Headers           : {[Content-Length, 21], [Content-Type, application/json; charset=utf-8], [Date, Tue, 30 Nov 2021 17:28:00 GMT], [Server, Microsoft-HTTPAPI/2.0]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 21

From Invoke-RestMethod, you should simply get:

Event inserted

Back to top

Further Reading

For Liquid, we’re barely scratching the surface here. If you are looking for further reading, we’d highly recommend looking at:

And just taking a look at filters (such as split, where, uniq, etc.) in general, to get a feel for what is possible.

The other topics here we feel are well fleshed out, however, you may wish to go back and delve into them further using the inline links provided (such as the article on .NET Framework Regular Expressions).