A GitHub Action that sends deployment notifications via email using Microsoft Graph API and Microsoft Entra ID (formerly Azure Active Directory).
- Sends deployment notification emails when deployments complete
- Integrates with Microsoft Graph API for email delivery
- Uses Microsoft Entra ID for secure authentication
- Includes repository, environment, timestamp, and recent commit messages
- Validates recipient email addresses with clear error messages for misconfiguration
- Automatic retry with exponential backoff for transient errors (429, 503, 504, timeouts)
- Structured log output with repository and environment context on every log line
- Cross-platform (runs on Linux, macOS, and Windows runners)
- name: Send Deployment Notification
uses: marcus-hooper/deployment-notification-o365@v1
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
NOTIFICATION_TO: [email protected]
NOTIFICATION_FROM: [email protected]This action uses environment variables instead of with: inputs. See Environment Variables for configuration.
This action does not produce outputs.
- name: Send Deployment Notification
uses: marcus-hooper/deployment-notification-o365@v1
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_ENVIRONMENT: production
NOTIFICATION_TO: [email protected], [email protected]
NOTIFICATION_FROM: [email protected]To include recent commit messages in the notification, create a commit_message.txt file before calling the action:
- name: Get recent commits
run: git log --oneline -5 > commit_message.txt
- name: Send Deployment Notification
uses: marcus-hooper/deployment-notification-o365@v1
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_ENVIRONMENT: production
NOTIFICATION_TO: [email protected], [email protected]
NOTIFICATION_FROM: [email protected]Here's a complete deployment workflow with 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: Capture recent commits
run: git log --oneline -5 > commit_message.txt
- name: Send Deployment Notification
uses: marcus-hooper/deployment-notification-o365@v1
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_ENVIRONMENT: ${{ github.environment }}
NOTIFICATION_TO: ${{ vars.NOTIFICATION_TO }}
NOTIFICATION_FROM: ${{ vars.NOTIFICATION_FROM }}The action sends a plain text email with the following format:
Subject:
Deployment Successful: owner/repo to production on 2026-01-15 14:30:45
Body:
Repository: owner/repo
Environment: production
Deployment Time: 2026-01-15 14:30:45
Status: Successful
Started by: username
Repository URL: https://github.com/owner/repo
Recent Commit Messages:
abc1234 Fix authentication bug
def5678 Add new feature
-
Register an application in Azure Portal > Microsoft Entra ID > App registrations > New registration
- Name:
GitHub Deployment Notifications(or your preference) - Supported account types: Single tenant
- Redirect URI: Leave blank
- Name:
-
Add API permissions
- Go to API permissions > Add a permission > Microsoft Graph
- Select Application permissions (not Delegated)
- Search for and add
Mail.Send - Click Grant admin consent (requires admin privileges)
-
Create a client secret
- Go to Certificates & secrets > New client secret
- Set an expiration (recommend 12-24 months)
- Copy the secret value immediately (shown only once)
-
Note your credentials
- Tenant ID: Found in Overview
- Client ID: Found in Overview (Application ID)
- Client Secret: The value you copied above
Add the following secrets to your repository (Settings > Secrets and variables > Actions):
| Secret | Description |
|---|---|
AZURE_TENANT_ID |
Azure AD Tenant ID |
AZURE_CLIENT_ID |
Azure AD Application Client ID |
AZURE_CLIENT_SECRET |
Azure AD Application Client Secret |
| Variable | Description |
|---|---|
AZURE_TENANT_ID |
Microsoft Entra ID Tenant ID |
AZURE_CLIENT_ID |
Microsoft Entra ID Application Client ID |
AZURE_CLIENT_SECRET |
Microsoft Entra ID Application Client Secret |
NOTIFICATION_TO |
Comma-separated recipient email addresses |
NOTIFICATION_FROM |
Sender email address (must have a mailbox in your tenant) |
These variables are automatically available in GitHub Actions workflows and do not need to be explicitly set:
| Variable | Description |
|---|---|
GITHUB_REPOSITORY |
Repository name (owner/repo) - automatically set |
GITHUB_ACTOR |
User who triggered the deployment - automatically set |
GITHUB_ENVIRONMENT |
Deployment environment - set when using environment: in job |
| File | Description |
|---|---|
commit_message.txt |
File containing recent commit messages to include |
- Loads Azure credentials from environment variables
- Reads
commit_message.txtif present in the working directory - Formats email with repository, environment, timestamp, and commits
- Authenticates via Microsoft Entra ID using client credentials flow
- Sends email via Microsoft Graph API (
/users/{sender}/sendMail) with automatic retry for transient errors
- Plain text emails only - HTML formatting is not supported
- US/Eastern timezone - Timestamps use US/Eastern timezone; not configurable
- Limited retry scope - Retries transient Graph API errors (429/503/504/timeout) up to 3 times with exponential backoff; non-retryable errors (400/401/403/404) fail immediately
- Environment variables only - Uses
env:rather thanwith:inputs
| Error | Cause | Solution |
|---|---|---|
AADSTS7000215: Invalid client secret |
Client secret is incorrect or expired | Regenerate the client secret in Azure Portal |
AADSTS700016: Application not found |
Wrong Client ID or Tenant ID | Verify IDs in Azure Portal > App registrations |
Authorization_RequestDenied |
Missing Mail.Send permission |
Add permission and grant admin consent |
ErrorItemNotFound / MailboxNotFound |
NOTIFICATION_FROM email doesn't exist |
Use an email with a valid mailbox in your tenant |
Invalid email address(es) |
NOTIFICATION_TO contains malformed emails |
Check that all addresses have exactly one @ with non-empty local and domain parts |
No valid email addresses provided |
NOTIFICATION_TO is empty or all entries are blank |
Provide at least one valid email address |
Missing required environment variable |
Env var not set or empty | Check secrets are correctly configured in GitHub |
- Check workflow logs - Expand the "Send notification" step for detailed error messages
- Verify Azure AD setup - Ensure admin consent is granted (green checkmark in API permissions)
- Test sender email - The
NOTIFICATION_FROMaddress must be a valid mailbox, not just an alias - Check secret expiration - Client secrets expire; regenerate if needed
- Python 3.13+
# Install dev dependencies
pip install -r requirements-dev.txt
# Run tests
pytest
# Run linter
ruff check .
# Run type checker
mypy send_deployment_notification.py --ignore-missing-importsFor end-to-end testing with real credentials:
export AZURE_TENANT_ID='your-tenant-id'
export AZURE_CLIENT_ID='your-client-id'
export AZURE_CLIENT_SECRET='your-secret'
export NOTIFICATION_TO='[email protected]'
export NOTIFICATION_FROM='[email protected]'
export GITHUB_REPOSITORY='owner/repo'
export GITHUB_ACTOR='username'
export GITHUB_ENVIRONMENT='test'
python send_deployment_notification.pydeployment-notification-o365/
├── .github/
│ ├── dependabot.yml
│ ├── ISSUE_TEMPLATE/
│ └── workflows/
│ ├── ci.yml # Linting, type checking, tests
│ ├── codeql.yml # CodeQL SAST analysis
│ ├── dependabot-auto-merge.yml # Auto-merge Dependabot PRs
│ ├── labels.yml # Repository label sync
│ ├── release.yml # Version tag management
│ ├── schedule.yml # Scheduled maintenance
│ ├── scorecard.yml # OSSF Scorecard analysis
│ ├── security.yml # Security scanning
│ └── validate.yml # action.yml validation
├── tests/ # Unit tests
├── action.yml # GitHub Action definition
├── send_deployment_notification.py
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Development dependencies
└── pyproject.toml # Tool configuration
Contributions are welcome! Please see CONTRIBUTING.md for detailed guidelines.
Quick start:
- Check existing issues or open a new one
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Make your changes and add tests if applicable
- Ensure CI passes (lint, format, and test)
- Submit a pull request
See the issue templates for bug reports and feature requests.
- Microsoft Graph API - Send Mail - API endpoint documentation
- send-teams-notification - Teams notification action using Adaptive Cards
- action-send-mail - Alternative email action using SMTP
- Store Azure credentials in GitHub Secrets (never as plain text)
- Do not echo or log
AZURE_CLIENT_SECRETin workflow steps - Rotate client secrets before expiration (check Azure Portal for expiry dates)
- Use minimal Graph API permissions (
Mail.Sendonly)
See SECURITY.md for security policy and reporting vulnerabilities.
See CHANGELOG.md for version history.
This project is licensed under the MIT License - see the LICENSE file for details.