Service Principal Secret Rotation Tracker
Triggered on a schedule, calls Microsoft Graph and Azure Key Vault APIs via HTTP to check app registration client secret expiry dates across all service principals used by Power Platform connections. Logs rotation status to Dataverse and posts a Teams adaptive card when any credential is within 14 days of expiration, preventing silent flow failures from expired secrets.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
Expiring Entra ID app-registration client secrets silently break downstream service-principal-based connections in Power Platform, Azure DevOps pipelines, custom connectors, and Logic Apps. This flow runs every Monday at 08:00 UTC, queries Microsoft Graph for every application registration in the tenant, evaluates the expiry date of every passwordCredential, logs each at-risk secret to a Dataverse table for audit, and sends an HTML alert to Teams and email when any secret falls inside the warning window.
Use Case
IT admins and platform owners need proactive visibility into upcoming service-principal secret expirations so they can rotate credentials before downstream integrations fail. This flow replaces ad-hoc PowerShell scripts and calendar reminders with a fully audited, automated check.
The flow is ideal for teams that:
- IT admins managing Entra ID app registrations and service principals
- Power Platform CoE teams overseeing connection references across environments
- Platform owners responsible for Azure DevOps pipelines, custom connectors, or Logic Apps that depend on service-principal credentials
- Security and compliance teams requiring an audit trail of credential rotation events
Flow Architecture
Run Weekly On Monday Morning
RecurrenceScheduled trigger that fires every Monday at 08:00 UTC to evaluate service principal secret expirations across the tenant.
Initialize Environment Variables
Initialize variable (multiple)Loads all flowlibs_* environment variables (tenant ID, client ID/secret, warning window days, Teams group/channel IDs, admin email, log table name) into flow-scoped variables for reuse.
Get App Registrations
HTTPCalls Microsoft Graph `GET /applications` using ActiveDirectoryOAuth (client credentials) authentication backed by the env-var tenantId/clientId/clientSecret.
Parse Applications Response
Parse JSONParses the Graph response body into a typed array of application objects exposing displayName, appId, and passwordCredentials.
For Each Application
Apply to each (concurrency = 1)Iterates each application registration sequentially to preserve ordering in the HTML alert table and Dataverse log rows.
For Each PasswordCredential
Apply to each (concurrency = 1)Inner loop over every passwordCredential on the current application; computes days remaining, rotation status tier, and status color for each secret.
If Secret Within Warning Window
If conditionChecks whether the computed days-remaining is at or below the configured `flowlibs_SecretExpiryWarningDays` threshold.
- Create Dataverse Log Row — Creates a record in the `flowlibs_secretrotationlogs` table capturing app id, app display name, secret key id, expiry date, days remaining, rotation status, and check date.
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_GraphTenantId | String | <your-tenant-id> | Entra ID tenant ID used for Graph API OAuth (client credentials flow). |
| flowlibs_GraphClientId | String | <configure> | Service principal application (client) ID used to call Microsoft Graph. |
| flowlibs_GraphClientSecret | String | <configure> | Client secret for the service principal that calls Graph. Store as a secret env var; rotate regularly. |
| flowlibs_AdminNotificationEmail | String | alerts@yourcompany.com | Email address that receives the high-importance rotation alert. |
| flowlibs_TeamsGroupId | String | <configure> | Team (group) ID where the Teams channel alert is posted. |
| flowlibs_TeamsChannelId | String | <configure> | Channel ID inside the Teams group where the alert is posted. |
| flowlibs_SecretExpiryWarningDays | String | 14 | Days before expiry that trigger a warning (e.g. 14). Drives the Warning - Rotate Soon tier. |
| flowlibs_SecretRotationLogTable | String | flowlibs_secretrotationlogs | Plural entity set name for the rotation log Dataverse table. |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| HTTP | http | HTTP (GET https://graph.microsoft.com/v1.0/applications using ActiveDirectoryOAuth (client credentials via env vars)) |
| Microsoft Dataverse | shared_commondataserviceforapps | CreateRecord (Logs each at-risk secret to flowlibs_secretrotationlogs) |
| Microsoft Teams | shared_teams | PostMessageToChannelV3 (Posts the HTML alert to the configured channel) |
| Office 365 Outlook | shared_office365 | SendEmailV2 (Sends high-importance email to the admin) |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Change the cadence
- Edit the Run_Weekly_On_Monday_Morning recurrence trigger. Daily runs catch same-day expirations; Monday-only reduces alert fatigue.
- Adjust the warning window
- Update the flowlibs_SecretExpiryWarningDays environment variable per environment. Compute_Rotation_Status uses three tiers - Expired (< 0 days), Critical - Rotate Now (<= 7 days), Warning - Rotate Soon (<= warning window), Healthy otherwise.
- Change notification targets
- Update flowlibs_TeamsGroupId, flowlibs_TeamsChannelId, and flowlibs_AdminNotificationEmail. No flow edit required.
- Filter specific apps
- Modify the Graph $filter query string in Get_App_Registrations (e.g. &$filter=startswith(displayName,'PP-SP-')) to scope the check to a naming convention.
- Include certificates
- Graph also returns keyCredentials on applications. Add a second Foreach alongside For_Each_PasswordCredential to monitor certificate expirations through the same logging and alerting pipeline.
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Days remaining
Computes days remaining until secret expiry by subtracting ticks(utcNow()) from the credential endDateTime and dividing by ticks-per-day. Coalesces a missing endDateTime to epoch so the result becomes a large negative (already-expired) value.
EXPR.02Rotation status
Nested if() on the computed days against `variables('varWarningDays')` producing four tiers: Expired, Critical, Warning, Healthy.
EXPR.03Status color
Maps each rotation status tier to a hex color used in the HTML alert table cell background.
EXPR.04Log row name
Builds the Dataverse primary-name column as `<appDisplayName> - <secretDisplayName or first 8 chars of keyId>` so the audit row is human-readable even when a secret has no display name.
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.