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
External Resources
Ultimately Liquid is transforming a JSON payload, so being able to recognize what you have received is very helpful
The Liquid Template Language Documentation
The main Liquid documentation is the best place to look up the specifics
Useful to quickly lookup details without going through the full documentation
Comes with a search and categorizations, making it very quick if you know loosely what you are looking for
Liquid Template Transformer (Light mode)
A quick Liquid sandbox to try things out in
Liquid Template Playground (Darkish mode)
A different Liquid sandbox, with a darker theme for people who like such things
We know of bugs in the underlying package currently, this is where you can keep up to date with known bugs.
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.
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. |
|
!=, <> | Evaluates to true if the left and right operand are unequal. |
|
> | Evaluates to true if the left operand is greater than the right operand. |
|
< | Evaluates to true if the left operand is less than the right operand. |
|
>= | Evaluates to true if the left operand is greater than or equal to the right operand. |
|
<= | Evaluates to true if the left operand is less than or equal to the right operand. |
|
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. |
Evaluates to true if the Name value is "SQLEngine."
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. |
Evaluates to true if the Name value is "SQL2005." |
IS NULL | Evaluates to true if the value of the left operand 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. |
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 |
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. |
|
OR | Evaluates to true if either the left or right operand is true. |
|
NOT | Evaluates to true if the right operand is not true. |
|
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. |
Evaluates to true if the Name value is "SQLEngine."
Evaluates to true if the Name value is "MySQLEngine." |
_ | A wildcard that matches a single character. |
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. |
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. |
Evaluates to true for "SQL2003." The expression evaluates to false for "SQL2000" and "SQL2005." |
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%'
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
Further Reading
For Liquid, we’re barely scratching the surface here. If you are looking for further reading, we’d highly recommend looking at:
Types (including accessing objects in arrays)
Control Flow Tags (such as If, ElseIf, Case, etc.)
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).