Skip to content

Instantly share code, notes, and snippets.

@DurandA
Created June 8, 2026 08:36
Show Gist options
  • Select an option

  • Save DurandA/e9d134a60a26f5196a74fc1b7d6fe829 to your computer and use it in GitHub Desktop.

Select an option

Save DurandA/e9d134a60a26f5196a74fc1b7d6fe829 to your computer and use it in GitHub Desktop.
Send test email from CLI using Microsoft Graph API
#!/usr/bin/env -S uv run
#
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "azure-identity",
# "msgraph-sdk",
# ]
# ///
#
# Minimal example to send an email using Microsoft Graph API.
#
# Prerequisites:
# 1. Register an app in Azure Portal (https://portal.azure.com)
# 2. Under "Certificates & secrets", create a client secret
# 3. Under "API permissions", add Microsoft Graph > Application > Mail.Send
# 4. Grant admin consent for the Mail.Send permission
# 5. Set the environment variables below
#
# Environment variables:
# MICROSOFT_GRAPH_TENANT_ID=your-azure-tenant-id
# MICROSOFT_GRAPH_CLIENT_ID=your-app-client-id
# MICROSOFT_GRAPH_CLIENT_SECRET=your-app-client-secret
#
# Usage:
# ./msgraph_send_email.py --from sender@example.com --to recipient@example.com \
# --subject "Hello" --body "Test email"
import argparse
import asyncio
import os
from azure.identity import ClientSecretCredential
from msgraph import GraphServiceClient
from msgraph.generated.models.body_type import BodyType
from msgraph.generated.models.email_address import EmailAddress
from msgraph.generated.models.item_body import ItemBody
from msgraph.generated.models.message import Message
from msgraph.generated.models.recipient import Recipient
from msgraph.generated.users.item.send_mail.send_mail_post_request_body import (
SendMailPostRequestBody,
)
TENANT_ID = os.environ.get("MICROSOFT_GRAPH_TENANT_ID")
CLIENT_ID = os.environ.get("MICROSOFT_GRAPH_CLIENT_ID")
CLIENT_SECRET = os.environ.get("MICROSOFT_GRAPH_CLIENT_SECRET")
SCOPES = ["https://graph.microsoft.com/.default"]
async def send_email(from_addr: str, to_addr: str, subject: str, body: str) -> None:
"""Send an email using Microsoft Graph API with client credentials authentication."""
credential = ClientSecretCredential(
tenant_id=TENANT_ID,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
)
client = GraphServiceClient(credentials=credential, scopes=SCOPES)
message = Message(
subject=subject,
body=ItemBody(content=body, content_type=BodyType.Text),
to_recipients=[
Recipient(email_address=EmailAddress(address=to_addr)),
],
)
request_body = SendMailPostRequestBody(message=message, save_to_sent_items=True)
await client.users.by_user_id(from_addr).send_mail.post(request_body)
print(f"Email sent from {from_addr} to {to_addr}")
def main() -> None:
parser = argparse.ArgumentParser(description="Send email via Microsoft Graph API")
parser.add_argument("--from", dest="from_addr", required=True, help="Sender email")
parser.add_argument("--to", required=True, help="Recipient email address")
parser.add_argument("--subject", required=True, help="Email subject")
parser.add_argument("--body", required=True, help="Email body text")
args = parser.parse_args()
missing = []
if not TENANT_ID:
missing.append("MICROSOFT_GRAPH_TENANT_ID")
if not CLIENT_ID:
missing.append("MICROSOFT_GRAPH_CLIENT_ID")
if not CLIENT_SECRET:
missing.append("MICROSOFT_GRAPH_CLIENT_SECRET")
if missing:
print(f"Error: Missing environment variables: {', '.join(missing)}")
return
asyncio.run(send_email(args.from_addr, args.to, args.subject, args.body))
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment