Sending notifications from Jenkins to Microsoft Teams

This is a typical issue. There must be a ready solution for that - right?

Yes and No. There is a plugin with rather ’limited’ features list. For basic ‘as is’ information sending this is fine. Another possibility is to create bot but complexity here is starting to vastly overweight the pros of this approach.

Idea for ‘dumb’ script

Assuming there may be another use cases for notification feature it would be nice to decouple it from jenkins entirely. Thanks to that it may be reused with tools using Unix pipelining approach.

Language

As a language of choice I started with python - another ideas were GoLang, Rust and JS. On the one hand, it is good to use this kind of small projects as a stepping stone but the ease of maintenance is also a crucial metric. I had a rather unpleasant experience modifying/debugging AWK script after colleague left company a month before. If this is done for personal usage, then feel free to use what you feel more comfortable in.

Notification template

For template construction, the easiest way would be official adaptive cards designer. When schema is ready use eg. Postman for manual functionality testing. For more information refer to official Microsoft documentation.

MS Teams

There are a few necessary to perform steps in the Teams itself:

  • create/configure dedicated channel
  • open channel options -> Connectors
  • add Incoming Webhook, save webhook URL

Detailed description can be found here

Data handling

Storage of data is done using json files. One for adaptive card template and second for config. Config may contain ie. project <-> webhook url mappings and locations of placeholder variables in template.

Getting mentions (@) inside message

Well-known from other instant messages apps feature is usage of mentions (@). MS Teams also recently started to support this in adaptive cards. To use this modify template according to documentation.

Sending requests

The last one and the easiest part - sending notification. To do that I used requests module.

After getting all together the code may look something like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""
POC of MS Teams notification script.
Needs alignment with used config.json and template.
"""

import json
import requests
import os
import argparse
import string

def load_json(filename):
    data = json.load(open(filename))
    return data

def assign_value(payload, section_id, field_id, new_value):
    # TODO: Implement assignment
    # I used loaded json field location from preconfigured fields in config.json and created string command assignment to 'exec'
    # Surely it can be done in better way using string matching/replacing with regexp
    return payload

def send_notification(url, payload):
    response = requests.post(url, json=payload)
    print("Request URL: ", url)
    print("Response: ", response)

def parse_args():
    parser = argparse.ArgumentParser()

    parser.add_argument('--project', required=True, help="Project Name")
    parser.add_argument('--status', required=True, help="Jenkins pipeline status", choices=['SUCCESS', 'UNSTABLE', 'FAILURE'])
    # add another arguments here

    args = parser.parse_args()
    return args

if __name__ == "__main__":
    args = parse_args()
    payload = load_json("adaptive_card_template.json")
    config = load_json("adaptive_card_config.json")

    # Change project name in the message
    payload = assign_value(payload, "project", "text", args.project)
    
    # Change project status in the message, color is mapped according to jenkins pipeline status
    payload = assign_value(payload, "state", "text", string.capwords(args.status))
    color = "Default"
    if args.status == 'SUCCESS': color="Good"               # Green
    if args.status == 'UNSTABLE': color="Warning"           # Orange
    if args.status == 'FAILURE': color="Attention"          # Red
    payload = assign_value(payload, "state", "color", color)  

    # Sending notification with filled payload
    try:
        url = config['project_webhook'][args.project]
        send_notification(url, payload)
    except Exception as e:
        print('Project webhook URL not found in config.json - please check mapping')
<End of Post>

Marek Pawlak