┌─────────────────────────────────────────────────────────────────┐
│ M365 Digest Email System │
│ │
│ Professional HTML email sending with inline images & OAuth2 │
└─────────────────────────────────────────────────────────────────┘
┌───────────────────┐ ┌──────────────────────┐ ┌──────────────────┐
│ │ │ │ │ │
│ CSV Data File │─────▶│ PowerShell │─────▶│ Microsoft 365 │
│ (Recipients) │ │ Control Script │ │ Exchange Online │
│ │ │ │ │ │
└───────────────────┘ └──────────────────────┘ └──────────────────┘
│
│ Uses
▼
┌──────────────────────┐
│ │
│ M365DigestEmail │
│ PowerShell Module │
│ │
└──────────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────┐ ┌──────────────────┐
│ Authentication │ │ Template │ │ SMTP Sending │
│ Module │ │ Processor │ │ with Batching │
└─────────────────┘ └──────────────┘ └──────────────────┘
│ │ │
┌──────────┴────────┐ │ ┌────────┴──────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────┐ ┌────────┐ ┌──────┐ ┌────────┐ ┌──────────┐
│ Basic │ │ OAuth2 │ │ HTML │ │ Inline │ │Checkpoint│
│ Auth │ │ (AAD) │ │ Body │ │ Images │ │ File │
└────────┘ └────────┘ └──────┘ └────────┘ └──────────┘
START
│
▼
┌─────────────────────────────────┐
│ Load Configuration │
│ - CSV path │
│ - HTML template │
│ - Inline images │
│ - SMTP settings │
│ - Auth method │
└─────────────────┬───────────────┘
│
▼
┌─────────────────────────────────┐
│ Validate Configuration │
│ - Check files exist │
│ - Verify image CIDs │
│ - Test authentication │
└─────────────────┬───────────────┘
│
▼
┌─────────────────────────────────┐
│ Load Checkpoint File │
│ (Resume from previous run) │
└─────────────────┬───────────────┘
│
▼
┌─────────────────────────────────┐
│ Import & Parse CSV │
│ - Filter already-sent emails │
│ - Build recipient objects │
└─────────────────┬───────────────┘
│
▼
┌─────────────────────────────────┐
│ For Each Batch (20 emails): │
│ │
│ ┌─────────────────────────┐ │
│ │ For Each Recipient: │ │
│ │ │ │
│ │ 1. Process HTML │ │
│ │ template │ │
│ │ 2. Replace placeholders │ │
│ │ 3. Create AlternateView │ │
│ │ 4. Embed inline images │ │
│ │ 5. Add attachments │ │
│ │ 6. Send via SMTP │ │
│ │ 7. Retry if failed (3x) │ │
│ │ 8. Write to checkpoint │ │
│ │ │ │
│ └─────────────────────────┘ │
│ │
│ Wait for batch window (3 min) │
└─────────────────┬───────────────┘
│
▼
┌─────────────────────────────────┐
│ Campaign Complete │
│ - Display statistics │
│ - Close connections │
└─────────────────────────────────┘
│
▼
END
M365DigestEmailModule.psm1
│
├── Get-EmailAuthenticationCredential
│ │
│ ├── Basic Authentication
│ │ └── PSCredential (username/password)
│ │
│ └── OAuth2 Authentication
│ ├── Token Endpoint Request
│ ├── Client Credentials Flow
│ └── PSCredential (username/token)
│
├── Get-ProcessedHtmlTemplate
│ │
│ ├── Load HTML file
│ ├── HTML-encode values (XSS protection)
│ └── Replace placeholders
│
├── New-EmailAlternateViewWithImages
│ │
│ ├── Create AlternateView object
│ ├── For each inline image:
│ │ ├── Create LinkedResource
│ │ ├── Set ContentType (image/png, image/jpeg)
│ │ ├── Set ContentId (CID reference)
│ │ └── Add to AlternateView
│ └── Return AlternateView
│
├── Send-HtmlEmail
│ │
│ ├── Create MailMessage object
│ ├── Set To, From, Subject, BCC
│ ├── Create AlternateView with images
│ ├── Add attachments
│ ├── Create SmtpClient
│ ├── Set credentials & SSL
│ ├── Send email
│ └── Dispose objects (cleanup)
│
└── Send-BulkHtmlEmail
│
├── Load checkpoint file
├── Filter pending recipients
├── For each batch:
│ ├── Process template per recipient
│ ├── Call Send-HtmlEmail
│ ├── Retry logic (exponential backoff)
│ ├── Update checkpoint file
│ └── Wait for window interval
└── Return statistics
User Script
│
▼
Get-EmailAuthenticationCredential
│
├── Convert password to SecureString
│
├── Create PSCredential object
│ (username + secure password)
│
└── Return credential
│
▼
SmtpClient.Credentials = PSCredential
│
▼
SMTP AUTH LOGIN
(Base64 encoded credentials)
│
▼
Office 365 validates
│
▼
Authentication SUCCESS/FAIL
User Script
│
▼
Get-EmailAuthenticationCredential
│
├── Build token request
│ - TenantId
│ - ClientId
│ - ClientSecret
│ - Scope: outlook.office365.com/.default
│
├── POST to token endpoint
│ https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
│
├── Receive access token
│
├── Create PSCredential
│ (username + token as password)
│
└── Return credential
│
▼
SmtpClient.Credentials = PSCredential
│
▼
SMTP AUTH XOAUTH2
(OAuth bearer token)
│
▼
Azure AD validates token
│
▼
Authentication SUCCESS/FAIL
┌────────────────────────────────────────────────────┐
│ MailMessage │
├────────────────────────────────────────────────────┤
│ │
│ Headers: │
│ From: sender@example.com │
│ To: recipient@example.com │
│ Subject: Microsoft 365 Monthly Digest │
│ BCC: admin@example.com │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ AlternateView (text/html) │ │
│ ├──────────────────────────────────────────────┤ │
│ │ │ │
│ │ <html> │ │
│ │ <body> │ │
│ │ ...HTML content... │ │
│ │ <img src="cid:logo1"> │ │
│ │ <img src="cid:icon1"> │ │
│ │ </body> │ │
│ │ </html> │ │
│ │ │ │
│ │ ┌────────────────────────────────────────┐ │ │
│ │ │ LinkedResource (logo1) │ │ │
│ │ │ - ContentId: logo1 │ │ │
│ │ │ - ContentType: image/png │ │ │
│ │ │ - TransferEncoding: Base64 │ │ │
│ │ │ - Data: [PNG bytes] │ │ │
│ │ └────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────┐ │ │
│ │ │ LinkedResource (icon1) │ │ │
│ │ │ - ContentId: icon1 │ │ │
│ │ │ - ContentType: image/png │ │ │
│ │ │ - TransferEncoding: Base64 │ │ │
│ │ │ - Data: [PNG bytes] │ │ │
│ │ └────────────────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────┘ │
│ │
│ Attachments: │
│ ├── Manual.pdf (application/pdf) │
│ └── Guide.pdf (application/pdf) │
│ │
└────────────────────────────────────────────────────┘
Batch 1 (emails 1-20)
├── Email 1 ────────┐
├── Email 2 │
├── Email 3 │
├── ... │ Sending Phase
├── Email 19 │ (~30-60 seconds)
├── Email 20 ───────┘
└── WAIT 3 minutes ◄─── Rate Limiting Window
Batch 2 (emails 21-40)
├── Email 21 ────────┐
├── Email 22 │
├── ... │ Sending Phase
├── Email 39 │
├── Email 40 ───────┘
└── WAIT 3 minutes
... Continue for all batches
Send Email
│
▼
┌─────────────────────┐
│ Attempt 1 │
│ (immediate) │
└──────┬──────────────┘
│
┌───┴───┐
│ OK? │
└───┬───┘
│
┌───┴───────────────────────┐
│ Success → Write checkpoint│
│ Fail → Wait 2s │
└───┬───────────────────────┘
│
▼
┌─────────────────────┐
│ Attempt 2 │
│ (after 2s) │
└──────┬──────────────┘
│
┌───┴───┐
│ OK? │
└───┬───┘
│
┌───┴───────────────────────┐
│ Success → Write checkpoint│
│ Fail → Wait 4s │
└───┬───────────────────────┘
│
▼
┌─────────────────────┐
│ Attempt 3 │
│ (after 4s) │
└──────┬──────────────┘
│
┌───┴───┐
│ OK? │
└───┬───┘
│
┌───┴───────────────────────┐
│ Success → Write checkpoint│
│ Fail → Log permanent error │
└───────────────────────────┘
┌───────────────────────────────────────────────────┐
│ Email Campaign Execution │
├───────────────────────────────────────────────────┤
│ │
│ Email 1 → Success ──┬─→ checkpoint.txt │
│ Email 2 → Success ──┤ user1@example.com │
│ Email 3 → Success ──┤ user2@example.com │
│ Email 4 → FAIL │ user3@example.com │
│ Email 5 → Success ──┤ user5@example.com │
│ Email 6 → Success ──┘ │
│ │
│ ╔════════════════════════════════════╗ │
│ ║ SCRIPT INTERRUPTED (Ctrl+C) ║ │
│ ╚════════════════════════════════════╝ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Restart script │ │
│ │ ↓ │ │
│ │ Load checkpoint.txt │ │
│ │ ↓ │ │
│ │ Skip: user1, user2, user3, user5│ │
│ │ ↓ │ │
│ │ Resume: Email 4, 7, 8, 9... │ │
│ └─────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────┘
Project Root
│
├── M365DigestEmailModule.psm1 ◄─── Core module (REQUIRED)
│
├── M365_Digest_Template.htm ◄─── HTML template (REQUIRED)
│
├── Send-M365Digest-BasicAuth.ps1 ◄─── Example script (Basic)
├── Send-M365Digest-OAuth.ps1 ◄─── Example script (OAuth)
├── Test-M365DigestConfig.ps1 ◄─── Configuration validator
│
├── recipients.csv ◄─── Recipient data (USER PROVIDED)
│
├── Images/ ◄─── Inline images (USER PROVIDED)
│ ├── datagroup_logo.png
│ ├── m365_icon.png
│ ├── exchange_icon.png
│ └── sharepoint_icon.png
│
├── Attachments/ ◄─── PDF files (USER PROVIDED)
│ ├── Manual1.pdf
│ ├── Manual2.pdf
│ └── Manual3.pdf
│
└── Logs/ ◄─── Runtime files (AUTO GENERATED)
├── smtp_send_checkpoint.txt
└── campaign_YYYYMMDD_HHmmss.log
┌─────────────────────────────────────────────────────┐
│ Campaign Size: 1000 recipients │
│ Batch Size: 20 emails │
│ Window Interval: 3 minutes │
├─────────────────────────────────────────────────────┤
│ │
│ Total Batches: 50 │
│ Total Time: ~150 minutes (2.5 hours) │
│ │
│ Time Breakdown: │
│ - Sending: ~50 minutes (1 min/batch) │
│ - Waiting: ~147 minutes (3 min × 49 intervals) │
│ - Retries: ~3 minutes (if 1% failure rate) │
│ │
│ Throughput: ~6.7 emails/minute (sustainable) │
│ │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Security Measures │
├─────────────────────────────────────────────────────┤
│ │
│ 1. HTML Encoding │
│ └─→ Prevents XSS injection attacks │
│ │
│ 2. Secure Credentials │
│ ├─→ PSCredential with SecureString │
│ └─→ OAuth tokens (short-lived) │
│ │
│ 3. TLS/SSL Encryption │
│ └─→ SMTP over TLS (port 587) │
│ │
│ 4. Application Permissions │
│ └─→ OAuth: Mail.Send only (least privilege) │
│ │
│ 5. Input Validation │
│ ├─→ File path validation │
│ ├─→ Email address format check │
│ └─→ Content size limits │
│ │
└─────────────────────────────────────────────────────┘
Architecture Documentation v1.0.0 | Built for Microsoft 365 Enterprise Email Campaigns