If you're managing Azure App Registrations across multiple environments or teams, you've probably dealt with expiring client secrets at the worst possible time; usually right before something breaks. 😅
Let’s fix that.
In this post, I’ll walk you through a PowerShell solution I built to automatically audit Azure App secrets and notify you before they expire; with a clean HTML report and optional email alerts.
🚨 Why You Need This
App secrets don’t last forever (nor should they). They typically expire in 6–12 months, but there’s no built-in alerting system from Azure unless you build one yourself.
Miss a secret rotation? Suddenly, your production app can’t authenticate, and you’re scrambling to debug authentication failures during a deploy.
This script automates:
✅ Checking all Azure App Registrations
✅ Identifying secrets expiring in the next N days
✅ Generating a sortable HTML report (When running script with UseLocalParameters)
✅ Sending it via email
🛠️ How It Works
The script uses the Microsoft Graph API to authenticate using a service principal (client ID/secret) and fetch all application registrations. It then;
Filters secrets that expire within a configurable number of days (default: 30)
Generates a styled HTML report (saves it locally)
Sends an email report using Microsoft Graph's sendMail endpoint
Bonus: If you include a line like NotifyEmail = team@yourdomain.com in the app’s Notes field, it’ll extract that automatically and show it in the report.
🚀 Getting Started
🧾 Prerequisites
1. App Registration in Entra ID
2. Licensed Sender Mailbox
Ensure your app is authorized to send mail on behalf of a licensed user, such as noreply@yourdomain.com.
3. Installed modules;
* Microsoft.Graph.Applications
* Microsoft.Graph.Users
* Microsoft.Graph.Authentication
4. Optional: Azure Automation Setup
Store sensitive variables as Automation Variables:
ClientID
ClientSecret
TenantID
WarningDays
SenderEmail
ToEmail
💻 Local Usage
Here’s how to run it locally with manual parameters:
powershellCopyEdit.\Notify-AppSecretExpire.ps1 `
-ClientId "<your-client-id>" `
-ClientSecret "<your-client-secret>" `
-TenantId "<your-tenant-id>" `
-SenderEmail "noreply@yourdomain.com" `
-ToEmail "admin@yourdomain.com" `
-OutputPath "C:\Reports\AppSecretsExpirationReport.html" `
-UseLocalParameters
☁️ Azure Automation Usage
If you're running this in an Azure Automation Account, you don’t need to pass parameters. Instead, define Automation Variables like:
ClientId
ClientSecret
TenantId
SenderEmail
ToEmail
The script auto-detects that it's running in Automation and pulls these values in.
📄 What the Report Looks Like
The script outputs a responsive, clean HTML report with:
App name and ID
Secret name (linked to the Azure portal)
Expiration date and days remaining
Optional NotifyEmail extracted from notes
Clickable column headers for sorting
Here’s a preview of the UI;

(This is an example of the generated HTML report)
🔗 GitHub Repository
All the code is available here:
👉 GitHub – secret-expiry-alert
🔔 New Feature: Notify Application Owners via NotifyEmail
In the latest version of the script (last edited June 20, 2025), there’s now support for automatically notifying the responsible team for each Azure AD App Registration using a custom tag in the app’s branding properties.
✅ How It Works
You can specify a NotifyEmail tag in the Notes field under Branding & properties of an App Registration:
NotifyEmail=team-azureops@yourdomain.com
When the script runs, it searches for this tag in Internal Notes and extracts the email address.
If credentials (client secrets or certificates) are expiring within the configured warning window (e.g. 30 days), the script:
Sends a HTML report to the global ToEmail.
Sends a separate, targeted email to each unique NotifyEmail recipient with only the credentials related to their apps.
✏️ Example Usage in Branding properties:
Go to Azure Portal → Entra ID → App Registrations → [Your App] → Branding & Properties
In the Internal Notes add:
NotifyEmail=team-azureops@yourdomain.com
📄 Script Changes Summary
| Feature | Description |
| NotifyEmail parsing | Reads from Internal Notes for NotifyEmail=<email> |
| Per‑team alerts | Sends a separate email to each unique NotifyEmail address with only relevant expiring credentials |
| Fallback behavior | If no NotifyEmail is defined, a credential expiry still appears in the global report, but no separate email is sent |
💡 Benefits of Using NotifyEmail
Eliminates alert noise by notifying only those who need to act.
Ensures accountability by routing alerts to the correct team.
Simplifies onboarding of new apps—just define your tag once and the automation handles the rest.
🤝 Contributions Welcome
Have ideas? Found a bug? Want to add secret expiration to KeyVault secrets too?
Open an issue or send a PR; I’d love to collaborate.
🙌 Final Thoughts
Security is proactive; not reactive.
Automating app secret monitoring is one small investment that saves a lot of future pain.
Let me know if you try this in your environment or have feedback!