# 🔐 Proactive Azure App Security: How to Automate Secret Expiry Alerts with PowerShell

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;

1. Filters secrets that expire within a configurable number of days (default: 30)
    
2. Generates a styled HTML report (saves it locally)
    
3. 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

* Create an app in **Entra ID &gt; App Registrations**
    
* Assign **API Permissions**:
    
    * `Application.Read.All`
        
    * `Directory.Read.All`
        
    * `Mail.Send`
        

#### 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:

```plaintext
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;

> ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1743347626960/ec5702a0-7409-4101-9dcc-1a1ac03be32f.png align="left")

*(This is an example of the generated HTML report)*

---

## 🔗 GitHub Repository

All the code is available here:  
👉 [GitHub – secret-expiry-alert](https://github.com/bitnash/m365-powershell-scripts/tree/main/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:
    
    ```plaintext
    NotifyEmail=team-azureops@yourdomain.com
    ```
    
* When the script runs, it searches for this tag in [`Internal Notes`](http://Application.Info) 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:
    
    ```plaintext
    NotifyEmail=team-azureops@yourdomain.com
    ```
    

#### 📄 Script Changes Summary

| Feature | Description |
| --- | --- |
| **NotifyEmail** parsing | Reads from [`Internal Notes`](http://Application.Info) 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!
