The way most teams find out a Key Vault secret expired is that something breaks: an app can no longer authenticate, a certificate stops validating, a pipeline fails at 2 a.m. Key Vault does have built-in expiry notifications, but they are wired per vault through Event Grid, and once you have more than a handful of vaults spread across teams and environments, nobody actually has one place that answers the question: what is expiring across all of our vaults, and what has already expired?
Why the built-in notifications do not scale
- They are per vault. Each vault needs its own Event Grid subscription and its own handler. With dozens of vaults, that is dozens of pieces of plumbing to set up, keep pointed somewhere useful, and remember to add to every new vault.
- They fire once. An expiry event is a single notification. If it lands in a channel nobody was watching that day, it is gone. There is no built-in way to escalate, no "still not renewed, here it is again" until someone acts.
- They say nothing about objects with no expiry date at all. An enabled secret or key that was created without an expiry never triggers anything, and those are exactly the objects that are easiest to forget.
The result is a blind spot shaped like your vault sprawl: each vault might be individually wired up, or might not be, and no single view covers the whole subscription.
The DIY approach: enumerate every vault on a schedule
Instead of pushing per-vault events at handlers, flip the model: pull. A scheduled job can walk every vault in the subscription and build one report:
- Run a daily timer job (an Azure Function works well) under a managed identity.
- Use the ARM management-plane list APIs to enumerate every Key Vault in the subscription, then list the keys, secrets, and certificates in each. The management plane returns object metadata (names, enabled state, expiry dates), so the job never needs data-plane access and can never read a secret value or key material.
- Bucket each object: expired, expiring within your warning thresholds, or no expiry set while enabled.
- Post the result to a Teams channel. In current Teams, add the Workflows app to the channel and create a flow from the "Post to a channel when a webhook request is received" template (the legacy Office 365 Incoming Webhook connector is being retired), then have the job POST to that URL.
- Repeat daily. Because the job re-reports anything still unrenewed, an ignored warning escalates by simply showing up again tomorrow, and the day after, until someone fixes it.
One detail worth knowing: certificates in Key Vault are backed by a secret, so certificate expiry surfaces through the backing secret's attributes; keys and secrets carry their expiry directly. And because the job discovers vaults by enumeration, a new vault created next week is covered automatically, with no per-vault wiring.
A ready-made option: Key Vault Digest
Key Vault Digest is that pattern packaged. Every day at 08:00 UTC it enumerates every Key Vault in the subscription and posts one Teams digest (an Adaptive Card) covering expired objects, objects expiring within your thresholds (re-reported daily until renewed, so it escalates), and enabled objects with no expiry set at all. It reads only object metadata through the ARM management plane, so it can never read secret values or key material, and the only role it needs, granted post-deploy by a one-line script, is Key Vault Reader at subscription scope. There is no vendor backend: alerts go only to the Teams webhook you configure. Flat $29/mo.
Key facts
- Native expiry notifications: per vault via Event Grid, fire once, no recurring escalation.
- No-expiry objects: never trigger anything natively; a subscription-wide sweep is the only way to surface them.
- Metadata is enough: the ARM management plane exposes names and expiry dates without any access to secret values or key material.
- Least privilege: a subscription-wide digest needs only Key Vault Reader at subscription scope.
- Teams webhooks: use the Workflows app template; the legacy Incoming Webhook connector is being retired.
Related reading: set an Entra ID account to expire on a date.
Written by the team at Katabarwa Labs. We build small, single-purpose Azure tools that run entirely inside your own tenant. Questions: abaho@llmgraph.ai.