A minimal C# console app that sends email via the Microsoft Graph API using app-only authentication (client credentials / service-principal flow).
| Requirement | Version |
|---|---|
| .NET SDK | 8.0 + |
| Azure AD App Registration | see below |
- Go to Azure Portal → Azure Active Directory → App registrations → New registration.
- Give it a name (e.g.
GraphEmailSender) and click Register. - Note down:
- Application (client) ID
- Directory (tenant) ID
- Under Certificates & secrets → New client secret — create a secret and copy the Value.
- Under API permissions → Add a permission → Microsoft Graph → Application permissions,
add
Mail.Send. - Click Grant admin consent for your tenant.
⚠️ Mail.Sendis an application permission (not delegated).
It lets the app send mail as any user in the tenant — restrict access via an application access policy if needed.
# Windows (PowerShell)
$env:AZURE_TENANT_ID = "your-tenant-id"
$env:AZURE_CLIENT_ID = "your-client-id"
$env:AZURE_CLIENT_SECRET = "your-client-secret"
$env:SENDER_EMAIL = "sender@yourdomain.com"
# macOS / Linux
export AZURE_TENANT_ID="your-tenant-id"
export AZURE_CLIENT_ID="your-client-id"
export AZURE_CLIENT_SECRET="your-client-secret"
export SENDER_EMAIL="sender@yourdomain.com"cd GraphEmailSender
dotnet runGraphEmailSender/
├── GraphEmailSender.csproj # NuGet references
├── Program.cs # All app logic
└── README.md
| Class / Record | Purpose |
|---|---|
AppConfig |
Loads credentials from environment variables |
GraphClientFactory |
Creates GraphServiceClient with ClientSecretCredential |
EmailMessage |
Immutable record describing an outbound email |
EmailSender |
Wraps the Graph sendMail API call |
| Package | Why |
|---|---|
Microsoft.Graph |
Typed Graph SDK (models + request builders) |
Azure.Identity |
ClientSecretCredential for app-only auth |
Edit the email variable in Program.cs:
var email = new EmailMessage
{
To = ["alice@example.com", "bob@example.com"],
Cc = ["manager@example.com"],
Subject = "Your subject here",
Body = "<p>HTML or plain text body</p>",
IsHtml = true,
};