diff --git a/pkg/services/alerting/notifiers/teams.go b/pkg/services/alerting/notifiers/teams.go
new file mode 100644
index 00000000000..9bb4d636867
--- /dev/null
+++ b/pkg/services/alerting/notifiers/teams.go
@@ -0,0 +1,123 @@
+package notifiers
+
+import (
+ "encoding/json"
+
+ "github.com/grafana/grafana/pkg/bus"
+ "github.com/grafana/grafana/pkg/log"
+ m "github.com/grafana/grafana/pkg/models"
+ "github.com/grafana/grafana/pkg/services/alerting"
+)
+
+func init() {
+ alerting.RegisterNotifier(&alerting.NotifierPlugin{
+ Type: "teams",
+ Name: "Microsoft Teams",
+ Description: "Sends notifications using Incomming Webhook connector to Microsoft Teams",
+ Factory: NewTeamsNotifier,
+ OptionsTemplate: `
+
Teams settings
+
+ Url
+
+
+ `,
+ })
+
+}
+
+func NewTeamsNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
+ url := model.Settings.Get("url").MustString()
+ if url == "" {
+ return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
+ }
+
+ return &TeamsNotifier{
+ NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
+ Url: url,
+ log: log.New("alerting.notifier.teams"),
+ }, nil
+}
+
+type TeamsNotifier struct {
+ NotifierBase
+ Url string
+ Recipient string
+ Mention string
+ log log.Logger
+}
+
+func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
+ this.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
+
+ ruleUrl, err := evalContext.GetRuleUrl()
+ if err != nil {
+ this.log.Error("Failed get rule link", "error", err)
+ return err
+ }
+
+ fields := make([]map[string]interface{}, 0)
+ fieldLimitCount := 4
+ for index, evt := range evalContext.EvalMatches {
+ fields = append(fields, map[string]interface{}{
+ "name": evt.Metric,
+ "value": evt.Value,
+ })
+ if index > fieldLimitCount {
+ break
+ }
+ }
+
+ if evalContext.Error != nil {
+ fields = append(fields, map[string]interface{}{
+ "name": "Error message",
+ "value": evalContext.Error.Error(),
+ })
+ }
+
+ message := this.Mention
+ if evalContext.Rule.State != m.AlertStateOK { //dont add message when going back to alert state ok.
+ message += " " + evalContext.Rule.Message
+ }
+
+ body := map[string]interface{}{
+ "@type": "MessageCard",
+ "@context": "http://schema.org/extensions",
+ "summary": message,
+ "title": evalContext.GetNotificationTitle(),
+ "themeColor": evalContext.GetStateModel().Color,
+ "sections": []map[string]interface{}{
+ {
+ "title": "Details",
+ "facts": fields,
+ "images": []map[string]interface{}{
+ {
+ "image": evalContext.ImagePublicUrl,
+ },
+ },
+ "text": message,
+ "potentialAction": []map[string]interface{}{
+ {
+ "@context": "http://schema.org",
+ "@type": "ViewAction",
+ "name": "View Rule",
+ "target": []string{
+ ruleUrl,
+ },
+ },
+ },
+ },
+ },
+ }
+
+ data, _ := json.Marshal(&body)
+ print(string(data))
+ cmd := &m.SendWebhookSync{Url: this.Url, Body: string(data)}
+
+ if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
+ this.log.Error("Failed to send teams notification", "error", err, "webhook", this.Name)
+ return err
+ }
+
+ return nil
+}
diff --git a/pkg/services/alerting/notifiers/teams_test.go b/pkg/services/alerting/notifiers/teams_test.go
new file mode 100644
index 00000000000..a9647350736
--- /dev/null
+++ b/pkg/services/alerting/notifiers/teams_test.go
@@ -0,0 +1,75 @@
+package notifiers
+
+import (
+ "testing"
+
+ "github.com/grafana/grafana/pkg/components/simplejson"
+ m "github.com/grafana/grafana/pkg/models"
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+func TestTeamsNotifier(t *testing.T) {
+ Convey("Teams notifier tests", t, func() {
+
+ Convey("Parsing alert notification from settings", func() {
+ Convey("empty settings should return error", func() {
+ json := `{ }`
+
+ settingsJSON, _ := simplejson.NewJson([]byte(json))
+ model := &m.AlertNotification{
+ Name: "ops",
+ Type: "teams",
+ Settings: settingsJSON,
+ }
+
+ _, err := NewTeamsNotifier(model)
+ So(err, ShouldNotBeNil)
+ })
+
+ Convey("from settings", func() {
+ json := `
+ {
+ "url": "http://google.com"
+ }`
+
+ settingsJSON, _ := simplejson.NewJson([]byte(json))
+ model := &m.AlertNotification{
+ Name: "ops",
+ Type: "teams",
+ Settings: settingsJSON,
+ }
+
+ not, err := NewTeamsNotifier(model)
+ teamsNotifier := not.(*TeamsNotifier)
+
+ So(err, ShouldBeNil)
+ So(teamsNotifier.Name, ShouldEqual, "ops")
+ So(teamsNotifier.Type, ShouldEqual, "teams")
+ So(teamsNotifier.Url, ShouldEqual, "http://google.com")
+ })
+
+ Convey("from settings with Recipient and Mention", func() {
+ json := `
+ {
+ "url": "http://google.com"
+ }`
+
+ settingsJSON, _ := simplejson.NewJson([]byte(json))
+ model := &m.AlertNotification{
+ Name: "ops",
+ Type: "teams",
+ Settings: settingsJSON,
+ }
+
+ not, err := NewTeamsNotifier(model)
+ teamsNotifier := not.(*TeamsNotifier)
+
+ So(err, ShouldBeNil)
+ So(teamsNotifier.Name, ShouldEqual, "ops")
+ So(teamsNotifier.Type, ShouldEqual, "teams")
+ So(teamsNotifier.Url, ShouldEqual, "http://google.com")
+ })
+
+ })
+ })
+}
diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts
index 2c273c93a01..b65a559e426 100644
--- a/public/app/features/alerting/alert_tab_ctrl.ts
+++ b/public/app/features/alerting/alert_tab_ctrl.ts
@@ -95,6 +95,7 @@ export class AlertTabCtrl {
case "hipchat": return "fa fa-mail-forward";
case "pushover": return "fa fa-mobile";
case "kafka": return "fa fa-random";
+ case "teams": return "fa fa-windows";
}
return 'fa fa-bell';
}