Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptive Cards Support #3799

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

6fears7
Copy link

@6fears7 6fears7 commented Apr 3, 2024

To continue #3503.

Credit to @aw1cks provided the original code, I slightly tweaked it.

Notables from that feature request:

  • MessageCards is deprecated, MSTeams integration should support AdaptiveCards
  • The original proposal suggested that by switching the payload format to adaptive web cards, the responsibility for creating valid adaptive cards is entirely upon the user's template. Additionally, perhaps this could be a config option.
  • I rest the case with the Alertmanager team for the option of a config option versus hard cutover.
  • I have two template examples. One is raw key/value, the other cleans things up.

Firing

Template 1 (Fancy)

image

Template 2 (Key/ Value)

image

Resolved

Template 1 (Fancy)

image

Template 2 (Key/ Value)

image

Template 1 (Fancy)

Label / Annotations View:

image

Silences:

Template 1 (Fancy)

Uses .CommonLabels

image

Click to view key/value Template
{{ define "msteams.ddefault.text" }}
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.2",
    "padding": "None",
    "msteams": {
        "width": "Full"
    },
    "body": [
        {
            "type": "Container",
            "id": "alert-msg",
            "padding": "Default",
            "items": [
                {
                    "type": "TextBlock",
                    "id": "alert-summary-title",
                    "text": "[{{- if gt (len .Alerts.Firing) 0 }}FIRING: {{ .Alerts.Firing | len }}] πŸ”₯ {{end}} {{- if gt (len .Alerts.Resolved) 0 }}RESOLVED: {{ .Alerts.Resolved | len }}] βœ… {{end}} {{ with index .Alerts 0 -}}{{ .Labels.alertname }}{{ end }}",
                    "weight": "Bolder",
                    "color": "{{- if gt (len .Alerts.Firing) 0 }}Attention{{end}}{{- if gt (len .Alerts.Resolved) 0 }}Good{{end}}",
                    "size": "ExtraLarge",
                    "horizontalAlignment": "Left"
                },
                {{ if gt (len .Alerts.Firing) 0 }}
                {{ range $index, $alert := .Alerts.Firing }}
                {
                    "type": "TextBlock",
                    "id": "alert-{{ $index }}-label-title",
                    "text": "Labels",
                    "weight": "Bolder",
                    "size": "Medium",
                    "horizontalAlignment": "Left"
                },
                {
                    "type": "FactSet",
                    "facts": [
                        {{ range $key, $value := $alert.Labels}}
                        {
                            "title": "{{ $key }}",
                            "value": "{{ $value }}"
                        },
                        {{ end }}
                        {
                            "type": "TextBlock",
                            "id": "alert-{{ $index }}-annotations-title",
                            "text": "Annotations",
                            "weight": "Bolder",
                            "size": "Medium",
                            "horizontalAlignment": "Left"
                        },
                        {{ range $key, $value := $alert.Annotations}}
                        {
                            "title": "{{ $key }}",
                            "value": "{{ $value }}"
                        },
                        {{ end }}
                        {}
                    ]
                },
                {{ end }}
                {{ end }}
                {{ if gt (len .Alerts.Resolved) 0 }}
                {{ range $index, $alert := .Alerts.Resolved }}
                {
                    "type": "TextBlock",
                    "id": "alert-{{ $index }}-label-title",
                    "text": "Labels",
                    "weight": "Bolder",
                    "size": "Medium",
                    "horizontalAlignment": "Left"
                },
                {
                    "type": "FactSet",
                    "facts": [
                        {{ range $key, $value := $alert.Labels}}
                        {
                            "title": "{{ $key }}",
                            "value": "{{ $value }}"
                        },
                        {{ end }}
                        {
                            "type": "TextBlock",
                            "id": "alert-{{ $index }}-annotations-title",
                            "text": "Annotations",
                            "weight": "Bolder",
                            "size": "Medium",
                            "horizontalAlignment": "Left"
                        },
                        {{ range $key, $value := $alert.Annotations}}
                        {
                            "title": "{{ $key }}",
                            "value": "{{ $value }}"
                        },
                        {{ end }}
                        {}
                    ]
                },
                {{ end }}
                {{ end }}
                {{ with index .Alerts 0 }}
                {
                    "type": "TextBlock",
                    "id": "alert-annotations-title",
                    "text": "Source: {{ .GeneratorURL }}",
                    "weight": "Bolder",
                    "size": "Medium",
                    "horizontalAlignment": "Left"
                }
                {{ end }}
            ]
        }
    ]
}
{{ end }}
Click to view fancy Template
{{ define "new.text" }}
{
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "type": "AdaptiveCard",
    "version": "1.2",
    "padding": "None",
    "msteams": {
        "width": "Full"
    },
    
    "body": [
        {
            "type": "Container",
            "id": "alert-msg",
            "padding": "Default",
            "items": [
                {
                    "type": "TextBlock",
                    "id": "alert-summary-title",
                    "text": "[{{- if gt (len .Alerts.Firing) 0 }}FIRING: {{ .Alerts.Firing | len }}] πŸ”₯ {{end}} {{- if gt (len .Alerts.Resolved) 0 }}RESOLVED: {{ .Alerts.Resolved | len }}] βœ… {{end}} {{ with index .Alerts 0 -}}{{ .Labels.alertname }}{{ end }}",
                    "weight": "Bolder",
                    "color": "{{- if gt (len .Alerts.Firing) 0 }}Attention{{end}}{{- if gt (len .Alerts.Resolved) 0 }}Good{{end}}",
                    "size": "ExtraLarge",
                    "horizontalAlignment": "Left"
                },
                {
                    "type": "Container",
                    "id": "alert-summary-container",
                    "padding": "None",
                    "items": [
                        {
                            "type": "TextBlock",
                            "id": "alert-summary-description",
                            "text": "{{ .Alerts.Firing | len }} alerts are firing",
                            "wrap": true
                        },
                         {
                            "type": "ActionSet",
                            "id": "alert-silence-action",
                            "actions": [
                                {
                                    "type": "Action.OpenUrl",
                                    "id": "silence",
                                    "title": "Silence",
                                    "url": "{{ .ExternalURL }}/#/silences/new?filter=%7B
                                    {{- range $key, $value := .CommonLabels }}
                                    {{- if eq $key "alertname" }}{{ $key }}%3D%22{{ reReplaceAll "\\\\" "" $value }}%22{{ end }}
                                    {{- end }}
                                    {{- range $key, $value := .CommonLabels }}
                                    {{- if ne $key "alertname" }}%2C{{ $key }}%3D%22{{ reReplaceAll "\\\\" "" $value }}%22{{ end }}
                                    {{- end -}}%7D"
                                },
                                {{ with $alert := index .Alerts 0}}
                                {
                                    "type": "Action.OpenUrl",
                                    "id": "prom",
                                    "title": "View in Prometheus",
                                    "url": "{{ .GeneratorURL }}"
                                }
                                
                                {{ end }}      
                            
                            ]
                        }

                    ]
                },
                 {{ range $index, $alert := .Alerts.Firing }}
                {
                    "type": "Container",
                    "id": "{{ $index }}-alerts-container",
                    "isVisible": true,
                    "padding": "None",
                    "items": [

                        {
                            "type": "Container",
                            "id": "alert-{{ $index }}-msg-container",
                            "padding": "None",
                            "separator": true,
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "id": "alert-{{ $index }}-summary",
                                    "text": "{{ $alert.Labels.alertname}}",
                                    "wrap": true,
                                    "size": "Medium",
                                    "weight": "Bolder"
                                },
                                {
                                    "type": "TextBlock",
                                    "id": "alert-{{ $index }}-description",
                                    "text": "{{ $alert.Annotations.description }}",
                                    "wrap": true,
                                    "weight": "Lighter",
                                    "size": "Small"
                                },
                                {
                                    "type": "ActionSet",
                                    "id": "alert-{{ $index }}-actions",
                                    "actions": [
                                        {
                                            "type": "Action.ShowCard",
                                            "title": "View Labels",
                                            "card": {
                                                "type": "AdaptiveCard",
                                                "body": [
                                                    {
                                                    "type": "TextBlock",
                                                    "wrap": true,
                                                    "text": "{{ $alert.Labels }}"
                                                    }
                                                ]
                                            }
                                        },
                                          {
                                            "type": "Action.ShowCard",
                                            "title": "View Annotations",
                                            "card": {
                                                "type": "AdaptiveCard",
                                                "body": [
                                                    {
                                                    "type": "TextBlock",
                                                    "wrap": true,
                                                    "text": "{{ $alert.Annotations }}"
                                                    }
                                                ]
                                            }
                                        }
                
                                    ]
                                },

                                {
                                    "type": "Container",
                                    "id": "alert-{{ $index }}-backup-labels-container",
                                    "padding": "None",
                                    "isVisible": false,
                                    "items": [
                                        {
                                            "type": "FactSet",
                                            "id": "alert-{{ $index }}-backup-labels-factset",
                                            "facts": []
                                        }

                                    ]
                                }
                            ]
                        }
                    ]
                },
                {{ end }}
                {{ range $index, $alert := .Alerts.Resolved }}
                {
                    "type": "Container",
                    "id": "{{ $index }}-alerts-container",
                    "isVisible": true,
                    "padding": "None",
                    "items": [

                        {
                            "type": "Container",
                            "id": "alert-{{ $index }}-msg-container",
                            "padding": "None",
                            "separator": true,
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "id": "alert-{{ $index }}-summary",
                                    "text": "{{ $alert.Labels.alertname}}",
                                    "wrap": true,
                                    "size": "Medium",
                                    "weight": "Bolder"
                                },
                                {
                                    "type": "TextBlock",
                                    "id": "alert-{{ $index }}-description",
                                    "text": "{{ $alert.Annotations.description }}",
                                    "wrap": true,
                                    "weight": "Lighter",
                                    "size": "Small"
                                },
                                {
                                    "type": "ActionSet",
                                    "id": "alert-{{ $index }}-actions",
                                    "actions": [
                                        {
                                            "type": "Action.ShowCard",
                                            "title": "View Labels",
                                            "card": {
                                                "type": "AdaptiveCard",
                                                "body": [
                                                    {
                                                    "type": "TextBlock",
                                                    "wrap": true,
                                                    "text": "{{ $alert.Labels }}"
                                                    }
                                                ]
                                            }
                                        },
                                          {
                                            "type": "Action.ShowCard",
                                            "title": "View Annotations",
                                            "card": {
                                                "type": "AdaptiveCard",
                                                "body": [
                                                    {
                                                    "type": "TextBlock",
                                                    "wrap": true,
                                                    "text": "{{ $alert.Annotations }}"
                                                    }
                                                ]
                                            }
                                        }
                
                                    ]
                                },

                                {
                                    "type": "Container",
                                    "id": "alert-{{ $index }}-backup-labels-container",
                                    "padding": "None",
                                    "isVisible": false,
                                    "items": [
                                        {
                                            "type": "FactSet",
                                            "id": "alert-{{ $index }}-backup-labels-factset",
                                            "facts": []
                                        }

                                    ]
                                }
                            ]
                        }
                    ]
                },
                {{ end }}
                {
                    "type": "Container",
                    "id": "backup-alerts-container",
                    "isVisible": false,
                    "padding": "None",
                    "items": [
                        {
                            "type": "Container",
                            "id": "alert-catch-msg-container",
                            "padding": "None",
                            "separator": false,
                            "items": []
                        }
                    ]
                }
            ]
        }

    ]
}
{{ end }}
receivers:
  - name: sandbox
    msteams_configs:
      -  webhook_url: "YOUR_WEBHOOK_HERE"
         text: '{{ template "new.text" . }}'

Is there an appetite for this?

@aw1cks
Copy link

aw1cks commented Apr 4, 2024

Thanks for making this MR @6fears7 ! Happy to assist on this.

Signed-off-by: Preston <[email protected]>
Signed-off-by: Preston <[email protected]>
@6fears7
Copy link
Author

6fears7 commented Apr 4, 2024

@aw1cks, Thank you for the initial contribution / idea. I actually would love your assistance on this as I'm strapped for free time currently. If you can address the CI test failures / implement the test cases, that would be awesome. Until a maintainer says otherwise, perhaps it would be best to just write this as a hard cut to AdaptiveCards versus offering support for both.

The original template file for msteams seemed to be using internal markdown to submit the alert data and is the cause for the test failure since it's expecting JSON format.

Feel free to ping me as needed.

@grobinson-grafana
Copy link
Contributor

Thanks for opening a PR for this! I think support for Adaptive Cards would be great feature to have in Alertmanager.

However, I do think there are a couple of problems with this specific implementation:

  1. Anyone who wants to customize their Microsoft Teams notifications will have to write a template with the contents of the JSON as you have done in default.tmpl. This is really complicated and very error prone. For example, it will be very easy to create invalid JSON from a Go template, especially when using loops and if statements.
  2. All other integrations we support allow customization of fields without having to know the specific data format that is expected. In this case, the schema for Adaptive Cards. This is very different from how all other integrations work.

My suggestion would be to try and come up with an abstraction for Adaptive Cards in YAML. An example of this is the draft PR I opened for Slack Block Kit (see #3381). I am interested to see what you can come up with for Microsoft Teams.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants