Short answer: A custom script is the better choice when you want no extra vendor and full control, and someone on your team will maintain it. AppAlert is the better choice when you do not want to own the code for collection, owner routing and delivery records.
- A script can read secret and certificate expiry dates on app registrations and service principals through Microsoft Graph. You build the paging, retries, recipients and fallback yourself.
- AppAlert reads expiry metadata for application secrets, application certificates, SAML signing certificates and Application Proxy SSL certificates with read-only consent. For service principals it reads signing certificates only, not service principal secrets, and it skips Microsoft-owned service principals. It emails your configured admin recipients, and application owners if you turn on owner routing.
- A script has no licence cost, but its Azure resources and upkeep do. AppAlert is free during Early Access, then an intended $200 per month per organization (see pricing).
AppAlert is our product. We wrote this page, so weigh it accordingly.
Microsoft behaviour on this page was checked against Microsoft Learn on 2026-10-17.
For the three-way overview of built-in emails, DIY and AppAlert, see the feature comparison.
How we compared
Here, a custom script means a scheduled PowerShell and Microsoft Graph script or an Azure Automation runbook. We compared against what Microsoft Learn documents for Graph and Azure Automation, and against what AppAlert does today. We did not run benchmarks. Script cells describe what you must build, rather than a specific script.
We used these criteria:
- Setup
- Maintenance
- Coverage of secrets, certificates and SAML signing certificates
- Who gets notified
- Ownerless credentials
- Auditability
- Cost
Comparison table
| Criterion | Custom script or runbook | AppAlert |
| --- | --- | --- |
| Setup | Build authentication, scheduling and collection. An Azure Automation runbook can use a system-assigned managed identity. Configure access through managed identity app-role assignments. | Approve read-only Microsoft Graph consent and configure recipients and thresholds. See permissions and data and alert settings. |
| Maintenance | Own error handling, updates and failed-run detection. Follow every @odata.nextLink and back off on 429 Too Many Requests responses using Retry-After. | AppAlert runs collection, owner routing and delivery tracking. You keep recipients and thresholds up to date. |
| Coverage of secrets, certificates and SAML signing certificates | Build collection for password credentials and key credentials. Include enterprise apps for SAML signing certificates. | Covers application secrets, application certificates, SAML signing certificates and Application Proxy SSL certificates. For service principals, reads signing certificates only, not service principal secrets, and skips Microsoft-owned service principals. |
| Who gets notified | Build recipient selection and threshold logic. Graph sendMail requires Mail.Send and sends as the user you call it for. A log search alert notifies the action group you select. | Emails configured admin recipients before expiry. Owner routing is off by default; when you turn it on, application owners get the email and admin recipients get a BCC unless you turn BCC off. For SAML signing certificates, owners are those of the enterprise app's service principal. Set thresholds per credential type (30, 14, 7 or 1 days before expiry). |
| Ownerless credentials | Read owners of applications and service principals, then build a fallback for missing or unusable addresses. | With owner routing on, credentials without usable owner addresses fall back to configured admin recipients. With no admin recipients configured, the alert is recorded as skipped. |
| Auditability | Depends on your build. You decide which collection runs and email attempts to record and where to keep them. | Notification Audit lists every retained alert email attempt, with records kept for 365 days. Nothing is recorded while email notifications are turned off. |
| Cost | No licence cost for the script itself. Budget maintenance and hosting. Log Analytics defaults to pay-as-you-go pricing based on ingested data volume and retention, billed to your Azure subscription. | Free during Early Access, then an intended $200 per month per organization (see pricing). |
Which option fits which case
A custom script fits better when
- You want no extra vendor and no third-party consent.
- You want full control over logic and output.
- You want no licence cost for the script itself and accept its operating costs.
- The check fits automation you already run and review.
If you choose a script, name a maintainer and decide who acts when a run fails.
AppAlert fits better when
- You do not want to own the collection, owner routing and delivery tracking code.
- You want configured admin recipients and optional application owner routing.
- You want Notification Audit to list every retained alert email attempt, with records kept for 365 days.
AppAlert asks for read-only Microsoft Graph consent. It cannot create, rotate or delete credentials. Review permissions and data before choosing the hosted option.
What a script has to handle
For app registrations, List applications lists Application.Read.All as the least privileged application permission. The List application owners endpoint also lists Application.Read.All as least privileged. Microsoft's PowerShell export sample exports app registrations with expiring secrets and certificates.
For a wider inventory, include service principals. The example below lists service principals and flags their credentials when List service principal owners returns no owners. It skips Microsoft-owned service principals, whose appOwnerOrganizationId is Microsoft's services organization ID f8cdef31-a31e-4b4a-93e4-5f571e91255a, because they would otherwise show up as false NeedsOwner rows. It does not check whether an existing owner has a usable email address. It does not filter expiry dates or send notifications.
The example makes one owner call per service principal. In a large organization that can hit Graph throttling, so handle 429 responses and Retry-After, or batch the owner reads.
Untested example. Review and test it in your environment before you rely on it.
Connect-MgGraph -Scopes Application.Read.All
$MicrosoftOrganizationId = 'f8cdef31-a31e-4b4a-93e4-5f571e91255a'
Get-MgServicePrincipal -All -Property id,appId,displayName,appOwnerOrganizationId,passwordCredentials,keyCredentials -ErrorAction Stop |
Where-Object { $_.AppOwnerOrganizationId -ne $MicrosoftOrganizationId } |
ForEach-Object {
$App = $_
$Owners = @(Get-MgServicePrincipalOwner -ServicePrincipalId $App.Id -All -ErrorAction Stop)
if ($Owners.Count -eq 0) {
foreach ($Type in @('PasswordCredentials', 'KeyCredentials')) {
foreach ($Credential in $App.$Type) {
[pscustomobject]@{
ObjectId = $App.Id
AppId = $App.AppId
CredentialType = $Type
KeyId = $Credential.KeyId
EndDateTime = $Credential.EndDateTime
NeedsOwner = $true
}
}
}
}
}
Both password credentials and key credentials expose endDateTime. Build your threshold checks around those dates. Keep failed reads separate from a successful empty inventory. Test missing owners, unusable owner addresses, expired credentials, failed collection and failed delivery before relying on your alerts.
See the full build guide for the collection and alert pipeline.
FAQ
Can a script cover SAML signing certificates?
Yes, if you build that coverage. SAML signing certificates belong to the enterprise app, represented by a service principal. Microsoft sends expiry emails at 60, 30 and 7 days before expiry, as documented in the SAML certificate guide. Include service principal collection rather than relying only on app registrations.
Does AppAlert see secret values?
No. AppAlert stores credential metadata only and never receives secret values or private keys. See permissions and data. Graph returns a secret value only at creation, as documented in the password credential reference.
What does AppAlert cost?
AppAlert is free during Early Access, then an intended $200 per month per organization (see pricing).
Can I keep my script and use AppAlert?
Yes. Decide which alerts each one sends, so recipients do not get two warnings for the same credential without knowing which to act on.
Who gets the email when a credential has no owner?
With owner routing on, AppAlert falls back to configured admin recipients when a credential has no usable owner addresses. The admin recipient list is empty by default; with no admin recipients configured, the alert is recorded as skipped. Review alert settings. For your own script, define and test that fallback yourself.
Related guides
- Build credential expiry monitoring
- Understand built-in expiration notifications
- Review permissions and data
Updated 2026-10-17.