DEV Community

Cover image for Solved: Alerting on GitHub Actions Workflow Failures via Microsoft Teams
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: Alerting on GitHub Actions Workflow Failures via Microsoft Teams

🚀 Executive Summary

TL;DR: Silent failures in GitHub Actions workflows can significantly delay development. This guide provides a robust solution by integrating GitHub Actions with Microsoft Teams to send real-time, actionable alerts for workflow failures, ensuring immediate team notification and faster resolution.

🎯 Key Takeaways

  • GitHub repository secrets are essential for securely storing sensitive credentials like Microsoft Teams webhook URLs, preventing their exposure in workflow files.
  • Reusable GitHub Actions workflows (triggered by on: workflow\_call) enable modular and DRY (Don’t Repeat Yourself) notification logic, allowing multiple CI/CD pipelines to invoke a single alert mechanism.
  • The if: failure() condition is crucial for triggering notification jobs exclusively when upstream jobs fail, ensuring targeted alerts for critical pipeline breakdowns and leveraging dynamic context variables like job.status and github.workflow.

Alerting on GitHub Actions Workflow Failures via Microsoft Teams

Introduction

In modern CI/CD pipelines, speed and reliability are paramount. A broken main branch or a failed deployment can block an entire team. The silent failure of a GitHub Actions workflow is a common scenario that can lead to significant delays. Developers might merge subsequent pull requests, assuming the pipeline is green, only to discover later that a critical build or test step has been failing for hours. The key to mitigating this is proactive, real-time communication.

This tutorial provides a comprehensive guide to integrating your GitHub Actions workflows with Microsoft Teams. By setting up automated alerts for failed runs, you can ensure that the right team members are notified immediately, enabling a faster response and resolution. We will create a robust and reusable notification system that sends detailed, actionable alerts directly to a designated Teams channel.

Prerequisites

Before you begin, ensure you have the following:

  • Administrative access to a GitHub repository where you want to implement alerts.
  • A Microsoft Teams account with permissions to add a connector to a channel.
  • A basic understanding of GitHub Actions YAML syntax and repository secrets.

Step-by-Step Guide

We’ll break down the process into four clear steps: creating the Teams webhook, securing it in GitHub, building a reusable notification workflow, and finally, integrating it into your main CI/CD pipeline.

Step 1: Create an Incoming Webhook in Microsoft Teams

First, we need to generate a unique URL in Microsoft Teams that will act as the endpoint for our notifications. This is called an “Incoming Webhook.”

  1. Navigate to the Microsoft Teams channel where you want to receive alerts.
  2. Click the three dots () next to the channel name and select Connectors.
  3. In the search box, type “Incoming Webhook” and click Add, then Configure.
  4. Provide a name for your webhook that clearly identifies its purpose, such as “GitHub Actions Alerts”. You can also upload a custom icon to make the notifications more recognizable.
  5. Click Create. Teams will generate a unique URL.
  6. Crucially, copy this URL to your clipboard. Treat this URL as a sensitive credential, as anyone with it can post messages to your channel. Click Done.

Step 2: Store the Webhook URL as a GitHub Secret

Never hardcode sensitive information like a webhook URL directly into your workflow files. Instead, we’ll use GitHub’s encrypted secrets to store it securely.

  1. In your GitHub repository, go to Settings > Secrets and variables > Actions.
  2. Click the New repository secret button.
  3. For the Name, enter MSTEAMS_WEBHOOK_URL. It’s important to use a consistent and descriptive name.
  4. In the Secret text box, paste the webhook URL you copied from Microsoft Teams in the previous step.
  5. Click Add secret to save it. Now, your workflow can securely access this URL using the expression ${{ secrets.MSTEAMS_WEBHOOK_URL }}.

Step 3: Create a Reusable Notification Workflow

To avoid duplicating notification logic across multiple workflows, we will create a single, reusable workflow dedicated to sending Teams alerts. This “callable” workflow can be invoked by any other workflow in your repository.

Create a new file named notify-teams-on-failure.yml inside the .github/workflows/ directory of your repository.

# .github/workflows/notify-teams-on-failure.yml

name: Notify MS Teams on Failure

# This workflow is designed to be called by other workflows
on:
  workflow_call:
    inputs:
      job_status:
        required: true
        type: string
      workflow_name:
        required: true
        type: string
    secrets:
      MSTEAMS_WEBHOOK_URL:
        required: true

jobs:
  send-teams-notification:
    runs-on: ubuntu-latest
    steps:
      - name: Send Teams Notification Card
        env:
          MSTEAMS_WEBHOOK_URL: ${{ secrets.MSTEAMS_WEBHOOK_URL }}
          JOB_STATUS: ${{ inputs.job_status }}
          WORKFLOW_NAME: ${{ inputs.workflow_name }}
          REPO_NAME: ${{ github.repository }}
          COMMIT_SHA: ${{ github.sha }}
          RUN_ID: ${{ github.run_id }}
          ACTOR: ${{ github.actor }}
        run: |
          # Bash Script
          # Sanitize workflow name for JSON
          WORKFLOW_NAME_SANITIZED=$(echo "$WORKFLOW_NAME" | sed 's/"/\\"/g')

          # Set colors and titles based on status
          if [[ "$JOB_STATUS" == "failure" ]]; then
            THEME_COLOR="FF0000" # Red
            STATUS_MESSAGE="Workflow Failed"
          else
            # Default case, can be extended for success
            THEME_COLOR="00FF00" # Green
            STATUS_MESSAGE="Workflow Succeeded"
          fi

          # Construct the Adaptive Card JSON payload
          JSON_PAYLOAD=$(cat <<EOF
          {
              "type": "message",
              "attachments": [
                  {
                      "contentType": "application/vnd.microsoft.card.adaptive",
                      "content": {
                          "type": "AdaptiveCard",
                          "version": "1.2",
                          "msteams": { "width": "Full" },
                          "body": [
                              {
                                  "type": "Container",
                                  "style": "emphasis",
                                  "items": [
                                      {
                                          "type": "TextBlock",
                                          "text": "${STATUS_MESSAGE}: ${WORKFLOW_NAME_SANITIZED}",
                                          "size": "large",
                                          "weight": "bolder",
                                          "wrap": true,
                                          "color": "attention"
                                      }
                                  ],
                                  "bleed": true,
                                  "backgroundColor": "${THEME_COLOR}"
                              },
                              {
                                  "type": "FactSet",
                                  "facts": [
                                      { "title": "Repository:", "value": "${REPO_NAME}" },
                                      { "title": "Triggered By:", "value": "${ACTOR}" },
                                      { "title": "Commit:", "value": "${COMMIT_SHA::7}" }
                                  ]
                              }
                          ],
                          "actions": [
                              {
                                  "type": "Action.OpenUrl",
                                  "title": "View Workflow Run",
                                  "url": "https://github.com/${REPO_NAME}/actions/runs/${RUN_ID}"
                              }
                          ]
                      }
                  }
              ]
          }
          EOF
          )

          # Use curl to send the POST request
          curl --request POST \
               --header 'Content-Type: application/json' \
               --data "${JSON_PAYLOAD}" \
               "${MSTEAMS_WEBHOOK_URL}"
Enter fullscreen mode Exit fullscreen mode

Code Explanation:

  • on: workflow_call: This trigger makes the workflow reusable. It can only be run when called by another workflow.
  • inputs and secrets: We define expected inputs (like the job status) and secrets (the webhook URL) that the calling workflow must provide.
  • Bash Script Logic: Inside the run step, a bash script dynamically constructs an Adaptive Card JSON payload. This format allows for rich, well-structured notifications in Teams.
  • Dynamic Values: We use GitHub Actions context variables like ${{ github.repository }} and ${{ github.run_id }} to populate the card with relevant details, including a direct link to the failed workflow run.
  • curl Command: Finally, curl sends the generated JSON payload to the Teams webhook URL, creating the alert in your channel.

Step 4: Integrate the Notification into a Main CI/CD Workflow

Now, let’s modify an existing CI/CD workflow to call our new notification workflow upon failure. Here is an example of a simple build-and-test pipeline named ci.yml.

# .github/workflows/ci.yml

name: Main CI Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      - name: Install Dependencies
        run: npm install
      - name: Run Tests
        run: npm test # This step might fail

  # This job runs ONLY if the 'build' job fails
  notify-on-failure:
    # 'needs' ensures this job runs after 'build' completes
    needs: build
    # 'if: failure()' is the key condition for triggering the alert
    if: failure()
    # This job is a "caller" that invokes our reusable workflow
    uses: ./.github/workflows/notify-teams-on-failure.yml
    with:
      job_status: ${{ job.status }}
      workflow_name: '"${{ github.workflow }}"' # Pass the workflow name
    secrets:
      MSTEAMS_WEBHOOK_URL: ${{ secrets.MSTEAMS_WEBHOOK_URL }}
Enter fullscreen mode Exit fullscreen mode

Code Explanation:

  • needs: build: This ensures the notify-on-failure job waits for the build job to finish before starting.
  • if: failure(): This is the most critical condition. It tells GitHub Actions to run this job only if any of the jobs listed in needs (in this case, build) has a status of “failure”.
  • uses: ./.github/...: This line calls our reusable workflow from the same repository.
  • with and secrets: We pass the required inputs and secrets to the callable workflow. ${{ job.status }} will dynamically resolve to “failure” when this job runs.

Common Pitfalls

  1. Mismatched Secret Names: A common error is defining a secret in GitHub with one name (e.g., TEAMS_HOOK) but referencing another in the YAML file (e.g., secrets.MSTEAMS_WEBHOOK_URL). The names must match exactly. If the notification fails, double-check that the secret is correctly named and passed to the callable workflow.
  2. Invalid JSON in the Payload: Microsoft Teams is strict about the JSON format for Adaptive Cards. A missing comma, an unescaped quote in a workflow name, or an extra bracket can cause the entire curl request to be rejected, often silently. When customizing the payload, it’s wise to validate your JSON structure using an online linter before committing the changes. Our example script includes a basic sanitization step for the workflow name to help prevent this.

Conclusion

You have now successfully configured a powerful, automated alerting system for your GitHub Actions. By immediately pushing failure notifications to a centralized Microsoft Teams channel, you close the feedback loop between your CI/CD pipeline and your development team. This enhanced visibility reduces downtime, prevents broken code from being overlooked, and fosters a more responsive and resilient engineering culture. Feel free to expand on this foundation by creating different card designs for successful runs, or by routing alerts to different channels based on the repository or workflow that failed.


Darian Vance

👉 Read the original article on TechResolve.blog


Support my work

If this article helped you, you can buy me a coffee:

👉 https://buymeacoffee.com/darianvance

Top comments (0)