Skip to content

Reusable GitHub Action to send Adaptive Card notifications to Microsoft Teams via webhook. PowerShell 7, emoji-safe, status styling, collapsible commits.

License

Notifications You must be signed in to change notification settings

marcus-hooper/send-teams-notification

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

33 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Send Teams Notification (Adaptive Cards)

CI codecov CodeQL Security OpenSSF Scorecard GitHub release PowerShell 7 License: MIT

A composite GitHub Action that posts Adaptive Cards to Microsoft Teams via Incoming Webhooks.

Important Notice

Office 365 Connectors Deprecation: Microsoft has announced that Office 365 Connectors, including Incoming Webhooks, are being retired. While this action continues to work with existing webhooks, Microsoft recommends migrating to Workflows (Power Automate) for new integrations. See Microsoft's retirement announcement for timeline and migration guidance.

Features

  • Sends rich Adaptive Card notifications to Microsoft Teams channels
  • Status styling with colors and emoji (success/failure/warning)
  • Displays repository, actor, and optional environment information
  • Optional collapsible commit history section
  • UTF-8-safe emoji handling (constructed from Unicode code points)
  • PowerShell 7 with no external dependencies
  • Works on Linux, Windows, and macOS runners

Quick Start

Minimal usage (sends on success and failure):

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: Send Teams notification
        if: ${{ always() }}
        uses: marcus-hooper/send-teams-notification@v1
        with:
          job_status: ${{ job.status }}
          webhook_url: ${{ secrets.TEAMS_WEBHOOK_URL }}

Note: The if: ${{ always() }} condition ensures the notification step runs regardless of whether previous steps succeeded or failed. Without this, the notification would be skipped when the job failsβ€”which is usually when you most want to be notified.

With environment and custom title:

- name: Send Teams notification
  if: ${{ always() }}
  uses: marcus-hooper/send-teams-notification@v1
  with:
    job_status: ${{ job.status }}
    environment: production
    card_title: "πŸ”” Deployment"
    webhook_url: ${{ secrets.TEAMS_WEBHOOK_URL }}

With commit messages (collapsible section in card):

- name: Get recent commits
  id: commits
  run: |
    COMMITS=$(git log --pretty=format:'{"title":"%h","value":"[%s](https://github.com/${{ github.repository }}/commit/%H)"}' -3 | jq -s '.')
    echo "json=$COMMITS" >> $GITHUB_OUTPUT

- name: Send Teams notification
  if: ${{ always() }}
  uses: marcus-hooper/send-teams-notification@v1
  with:
    job_status: ${{ job.status }}
    commit_messages: ${{ steps.commits.outputs.json }}
    webhook_url: ${{ secrets.TEAMS_WEBHOOK_URL }}

Note: The git log format above may produce invalid JSON if commit messages contain double quotes or backslashes. For repositories with complex commit messages, consider using jq to properly escape the values, or limit to commit SHAs only.

Complete Workflow Example

Here's a complete deployment workflow with Teams notification:

name: Deploy and Notify

on:
  push:
    branches: [main]

jobs:
  deploy:
    name: Deploy Application
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 5

      - name: Deploy to production
        run: |
          # Your deployment steps here
          echo "Deploying application..."

      - name: Get recent commits
        id: commits
        run: |
          COMMITS=$(git log --pretty=format:'{"title":"%h","value":"[%s](https://github.com/${{ github.repository }}/commit/%H)"}' -3 | jq -s '.')
          echo "json=$COMMITS" >> $GITHUB_OUTPUT

      - name: Send Teams notification
        if: ${{ always() }}
        uses: marcus-hooper/send-teams-notification@v1
        with:
          job_status: ${{ job.status }}
          environment: ${{ github.environment }}
          card_title: "πŸš€ Production Deployment"
          commit_messages: ${{ steps.commits.outputs.json }}
          webhook_url: ${{ secrets.TEAMS_WEBHOOK_URL }}

Sample Card Output

The action sends an Adaptive Card to Teams with the following structure:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ πŸš€ Production Deployment                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ βœ… Success                                      β”‚
β”‚                                                 β”‚
β”‚ Repository    owner/repo                        β”‚
β”‚ Environment   production                        β”‚
β”‚ Actor         username                          β”‚
β”‚                                                 β”‚
β”‚ β–Ά Recent Commits (tap to expand)                β”‚
β”‚   abc1234  Fix authentication bug               β”‚
β”‚   def5678  Add new feature                      β”‚
β”‚   ghi9012  Update dependencies                  β”‚
β”‚                                                 β”‚
β”‚ [View Run]                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Status Styling:

Status Color Emoji
Success Green (#107C10) βœ…
Failure Red (#D13438) ❌
Cancelled/Other Yellow (#F2C744) ⚠️

Inputs

Input Required Default Description
job_status Yes Status of the job: success, failure, or cancelled
webhook_url Yes Teams Incoming Webhook URL (store in secrets)
commit_messages No [] JSON array for FactSet, e.g. [{"title":"SHA","value":"Message"}]
environment No Deployment environment label
card_title No πŸ”” GitHub Deployment Title for the card
repository No github.repository Repository name to display
actor No github.actor Actor name to display
run_id No github.run_id Workflow run ID for the "View Run" link

Outputs

Output Description
sent Whether a POST to Teams was attempted
payload_bytes Size of the JSON payload in bytes
run_url Link to the workflow run

Requirements

  • Any GitHub Actions runner (GitHub-hosted or self-hosted) with PowerShell 7 available
  • Microsoft Teams Incoming Webhook connector configured for the target channel

Limitations

Limitation Details
Teams webhook only Does not support Bot Framework or Graph API delivery
No retry logic Single attempt to send; fails immediately on HTTP error
Card size limit Teams limits Adaptive Cards to ~28KB; large commit lists may be truncated
Webhook rate limits Teams may throttle frequent webhook calls
No message updates Cannot update or delete sent cards (webhook limitation)
Encoding sensitivity Requires UTF-8 without BOM; emoji via code points to avoid YAML issues

Troubleshooting

Common Errors

Error Cause Solution
400/BadRequest Teams rejects malformed or oversized cards Keep payload < ~28 KB
Nothing appears in Teams Webhook URL invalid or connector disabled Verify the webhook URL and that the channel has the Incoming Webhook connector
Emoji or characters look wrong Encoding issues Ensure your YAML files are UTF-8 without BOM
No commits section Invalid JSON Ensure commit_messages is valid JSON (quote properly and avoid YAML mangling)
403/Forbidden Webhook URL expired or revoked Generate a new webhook URL in Teams
Connector not available Channel permissions restrict connectors Ask a Teams admin to enable connectors for the channel or team

Debug Tips

  1. Check workflow logs - Expand the "Send Teams notification" step for detailed output
  2. Verify webhook URL - Test the webhook URL manually with a simple curl/Invoke-RestMethod call
  3. Check payload size - The payload_bytes output shows the JSON size; keep it under 28KB
  4. Validate commit JSON - Add a step to echo ${{ steps.commits.outputs.json }} to verify format
  5. Test locally - Run the PowerShell script directly with environment variables set (see Development section)

How It Works

  1. Builds an Adaptive Card 1.5 with status styling and optional collapsible commit list
  2. Sends via Teams Incoming Webhook as an attachment payload
  3. Derives repo/actor from GitHub context if not provided

Development

Requirements

  • PowerShell 7+
  • Pester 5.x (for tests)
  • PSScriptAnalyzer (for linting)

Local Testing

# Run Pester tests
Invoke-Pester ./tests -Output Detailed

# Run tests with coverage
$config = New-PesterConfiguration
$config.Run.Path = './tests'
$config.Output.Verbosity = 'Detailed'
$config.CodeCoverage.Enabled = $true
$config.CodeCoverage.Path = @('./scripts/send-teams.ps1', './scripts/Send-TeamsNotification.psm1')
Invoke-Pester -Configuration $config

Linting

# Install PSScriptAnalyzer if needed
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser

# Run linter
Invoke-ScriptAnalyzer -Path ./scripts -Recurse -Settings PSGallery

Manual Testing

For end-to-end testing with a real Teams channel:

$env:INPUT_WEBHOOK_URL = 'https://outlook.office.com/webhook/...'
$env:INPUT_JOB_STATUS = 'success'
$env:INPUT_REPOSITORY = 'owner/repo'
$env:INPUT_ACTOR = 'username'
$env:INPUT_RUN_ID = '123456789'
./scripts/send-teams.ps1

Project Structure

send-teams-notification/
β”œβ”€β”€ action.yml                  # GitHub Action definition (composite action)
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ send-teams.ps1          # Entry point script
β”‚   └── Send-TeamsNotification.psm1  # PowerShell module with testable functions
β”œβ”€β”€ tests/
β”‚   └── Send-TeamsNotification.Tests.ps1  # Pester unit tests
β”œβ”€β”€ .github/
β”‚   β”œβ”€β”€ dependabot.yml          # Dependabot configuration
β”‚   β”œβ”€β”€ labels.yml              # Repository label definitions
β”‚   β”œβ”€β”€ PULL_REQUEST_TEMPLATE.md
β”‚   β”œβ”€β”€ ISSUE_TEMPLATE/
β”‚   β”‚   β”œβ”€β”€ bug_report.yml      # Bug report form
β”‚   β”‚   β”œβ”€β”€ feature_request.yml # Feature request form
β”‚   β”‚   └── config.yml          # Issue template chooser config
β”‚   └── workflows/
β”‚       β”œβ”€β”€ ci.yml              # CI workflow (lint, test, coverage)
β”‚       β”œβ”€β”€ codeql.yml          # CodeQL security analysis
β”‚       β”œβ”€β”€ dependabot-auto-merge.yml  # Auto-merge Dependabot PRs
β”‚       β”œβ”€β”€ labels.yml          # Label synchronization
β”‚       β”œβ”€β”€ release.yml         # Major version tag updates
β”‚       β”œβ”€β”€ schedule.yml        # Weekly health check
β”‚       β”œβ”€β”€ scorecard.yml       # OpenSSF Scorecard analysis
β”‚       β”œβ”€β”€ security.yml        # Security scanning
β”‚       └── validate.yml        # Action validation
β”œβ”€β”€ README.md                   # This file
β”œβ”€β”€ CHANGELOG.md                # Version history
β”œβ”€β”€ CONTRIBUTING.md             # Contribution guidelines
β”œβ”€β”€ SECURITY.md                 # Security policy
β”œβ”€β”€ LICENSE                     # MIT License
└── .gitignore                  # Git ignore patterns

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for detailed guidelines.

Quick start:

  1. Check existing issues or open a new one
  2. Fork the repository
  3. Create a feature branch (git checkout -b feature/my-feature)
  4. Make your changes and add tests if applicable
  5. Ensure CI passes (lint and test)
  6. Submit a pull request

See the issue templates for bug reports and feature requests.

Security

  • Store webhook_url in GitHub Secrets (e.g., TEAMS_WEBHOOK_URL)
  • Do not echo or log the webhook URL
  • Review branch protections for workflows that can send notifications

See SECURITY.md for security policy and reporting vulnerabilities.

Related Projects

Changelog

See CHANGELOG.md for version history.

License

MIT License - see LICENSE for details.

About

Reusable GitHub Action to send Adaptive Card notifications to Microsoft Teams via webhook. PowerShell 7, emoji-safe, status styling, collapsible commits.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •