Find expiring client secrets using Graph API and PowerShell

In the past it was possible to create non-expiring client secrets. When creating a client secret the drop down menu showed the option Never expires. This was easy for an administrator but absolutely not in terms of security.

Even when the expiration mentioned they would never expire, they still had an 99 year life-time. Microsoft removed the non-expiring option back in April 2021. That means that we have to renew client secrets all the time (before they expire).

When managing several tenants it can be hell of a job to monitor and maintain all the client secrets. Therefor I’ve created the following PowerShell script to query all the client secrets created within a sigle tenant.

The script simply queries all the Entra ID Applications (Azure AD Applications) and their client secrets. If the client secret has an expire date less then 30 or 90 days it will report back to the console.

Graph Explorer

For those who don’t know about Graph Explorer should definitely have a look at it! With Graph Explorer you can query tenant data using Graph API. I was able to find the endDateTime value of a client secret. This is the data I’m looking for!


With the use of this PowerShell script you are able to retrieve all applications in Entra ID (Azure AD) and query all the client secrets attached to the applications. If they have a expiring client secret within 30 days it will report back to the console which application and client secret should be rotated before it expires.

Use the following code for client secrets expiring within 30 days.

# Install required module if not already installed
Install-Module -Name Microsoft.Graph.Authentication -Force -AllowClobber

# Import required modules
Import-Module Microsoft.Graph.Authentication

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.Read.All"

# Retrieve all applications
$allApplications = @()
$pageSize = 100
$nextLink = $null

do {
    $applicationsPage = Invoke-MgGraphRequest -Method GET -Uri "\$($nextLink -replace '\?', '&')"

    $allApplications += $applicationsPage.Value

    $nextLink = $applicationsPage.'@odata.nextLink'
} while ($nextLink)

# Query each application
foreach ($application in $allApplications) {
# Retrieve secrets
    $secretsUri = "$($"
    $secrets = Invoke-MgGraphRequest -Method GET -Uri $secretsUri

# Query secrets
    foreach ($secret in $secrets.value) {
        try {
            $expiryDateTime = [DateTime]$secret.endDateTime
            $expiryDate = $expiryDateTime.Date

            if ($expiryDate -ne $null) {
                $daysUntilExpiry = ($expiryDate - (Get-Date).Date).Days

                if ($daysUntilExpiry -le 30) {
                    Write-Host -ForegroundColor Red "Secret Expiring within a Month:"
                    Write-Host "Application Name: $($application.displayName)"
                    Write-Host "Application ID: $($"
                    Write-Host "  Key ID: $($secret.keyId)"
                    Write-Host "  Expiry Date: $($expiryDate.ToString("yyyy-MM-dd"))"
                    Write-Host "  Days Until Expiry: $daysUntilExpiry"
            } else {
                throw "Invalid DateTime format"
        catch {
            Write-Host "Error parsing secret expiry date. Skipping secret."


# Disconnect from Microsoft Graph

The script can be found in my Github repository. There’s also a 90 days example available. If you want a different time-range you can simply modify $daysUntilExpiry -le 30 in the code where 30 is the amount of days.

In a later blog I’ll try to do some automation/notification around expiring client secrets. Maybe some automated renewing (before expiring) and or notifications via e-mails/teams.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

3 × 2 =