6

Get Application Certificate and Secret Expiration with Graph API

In the world of Azure cloud automation we always need to ensure that our accounts are able to properly authenticate. Accounts with username and password might have Active Directory alert you when your password expires, however, what can we use to ensure the secrets or certificates tied to an App registration aren’t nearing expiration ( or worse, already expired). Today I am going to share a PowerShell script that shows you how to get application certificate and secret expiration with Graph API.

Requirements

In order to run this, there are a few things that need to be in place to ensure we don’t run into any errors. Let’s touch on those item now.
 

  • Directory.Read.All Permissions
  • Application.Read.All Permissions
  • Microsoft.Graph PowerShell SDK Module

Check an App Registration for Expired Keys in Azure Portal

Before we get into the PowerShell script, let’s take a look at how to check this manually so we know exactly what to expect when looking at the results of the script.
 

Within Azure AD:

Azure portal expired keys
 

  • Go to Certificates & secrets blade

Certificate Expiration
 

Here we can see the status of the certificate for this specific application. Specifically, we’re interested in the expiration date, certificate thumbprint and Key ID (certificate ID).
 

While the portal does give you a visual of when keys are expiring, it still requires you to take time out of your day to manually check. Let’s take a look at how we can accomplish the same thing automatically using the PowerShell script I wrote.

Get Application Certificate and Secret Expiration with Graph API PowerShell

Now that we’ve gone over the manual method, let’s use PowerShell and Graph API to our advantage and show the same information in an automated fashion. Since we know how to automate Powershell Scripts With Task Scheduler, we can schedule this on a daily basis and let it alert you without any additional effort on your end.
 

Now for the PowerShell script:

Function Get-MgApplicationCertificateAndSecretExpiration {
<#
.SYNOPSIS
    This will display all Applications that have certificates or secrets expiring within a certain timeframe


.NOTES
    Name: Get-MgApplicationCertificateAndSecretExpiration
    Author: Paul Contreras
    Version: 1.3
    DateCreated: 2022-Feb-8

.LINK
    https://thesysadminchannel.com/get-application-certificate-and-secret-expiration-with-graph-api-powershell -

.EXAMPLE
    Get-MgApplicationCertificateAndSecretExpiration

.EXAMPLE
    Get-MgApplicationCertificateAndSecretExpiration -ShowExpiredKeys
#>

    [CmdletBinding(DefaultParameterSetName='Default')]
    param(
        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'CertOnly'
        )]
        [switch]    $ShowOnlyCertificates,

        [Parameter(
            Mandatory = $false,
            ParameterSetName = 'SecretOnly'
        )]
        [switch]    $ShowOnlySecrets,


        [Parameter(
            Mandatory = $false
        )]
        [switch]    $ShowExpiredKeys,


        [Parameter(
            Mandatory = $false
        )]
        [ValidateRange(1,720)]
        [int]    $DaysWithinExpiration = 30,


        [Parameter(
            Mandatory = $false,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias('ApplicationId', 'ClientId')]
        [string]    $AppId
    )

    BEGIN {
        $ConnectionGraph = Get-MgContext
        if (-not $ConnectionGraph) {
            Write-Error "Please connect to Microsoft Graph" -ErrorAction Stop
        }
        #Adding an extra day to account for hour differences and offsets.
        $DaysWithinExpiration++
    }

    PROCESS {
        try {
            if ($PSBoundParameters.ContainsKey('AppId')) {
                $ApplicationList = Get-MgApplication -Filter "AppId eq '$AppId'" -ErrorAction Stop
                $AppFilter = $true
            } else {
                $ApplicationList = Get-MgApplication -All -Property AppId, DisplayName, PasswordCredentials, KeyCredentials, Id -PageSize 999 -ErrorAction Stop
            }

            #If certs are selected, show certs
            if ($PSBoundParameters.ContainsKey('ShowOnlyCertificates') -or

                #If neither Certs or Secrets are selected show both.
               (-not $PSBoundParameters.ContainsKey('ShowOnlyCertificates') -and
                -not $PSBoundParameters.ContainsKey('ShowOnlySecrets'))) {

                    $CertificateApps  = $ApplicationList | Where-Object {$_.keyCredentials}

                    $CertApp = foreach ($App in $CertificateApps) {
                        foreach ($Cert in $App.keyCredentials) {
                            if ( $Cert.endDateTime -le (Get-Date).AddDays($DaysWithinExpiration) -or ($AppFilter) ) {
                                [PSCustomObject]@{
                                    AppDisplayName      = $App.DisplayName
                                    AppId               = $App.AppId
                                    KeyType             = 'Certificate'
                                    ExpirationDate      = $Cert.EndDateTime
                                    DaysUntilExpiration = (($Cert.EndDateTime) - (Get-Date) | select -ExpandProperty TotalDays) -as [int]
                                    ThumbPrint          = [System.Convert]::ToBase64String($Cert.CustomKeyIdentifier)
                                    Id                  = $App.Id
                                    KeyId               = $Cert.KeyId
                                    Description         = $Cert.DisplayName
                                }
                            }
                        }
                    }

                    if ($PSBoundParameters.ContainsKey('ShowExpiredKeys')) {
                        $CertApp | Sort-Object DaysUntilExpiration
                    } else {
                        $CertApp | Sort-Object DaysUntilExpiration | Where-Object {$_.DaysUntilExpiration -ge 0}
                    }
            }

            #If secrets are selected, show secrets
            if ($PSBoundParameters.ContainsKey('ShowOnlySecrets') -or

                #If neither Certs or Secrets are selected show both.
               (-not $PSBoundParameters.ContainsKey('ShowOnlySecrets') -and
                -not $PSBoundParameters.ContainsKey('ShowOnlyCertificates'))) {

                    $ClientSecretApps = $ApplicationList | Where-Object {$_.passwordCredentials}

                    $SecretApp = foreach ($App in $ClientSecretApps){
                        foreach ($Secret in $App.PasswordCredentials) {
                            if ( $Secret.EndDateTime -le (Get-Date).AddDays($DaysWithinExpiration) -or ($AppFilter) ) {
                                [PSCustomObject]@{
                                    AppDisplayName      = $App.DisplayName
                                    AppId               = $App.AppId
                                    KeyType             = 'ClientSecret'
                                    ExpirationDate      = $Secret.EndDateTime
                                    DaysUntilExpiration = (($Secret.EndDateTime) - (Get-Date) | select -ExpandProperty TotalDays) -as [int]
                                    ThumbPrint          = 'N/A'
                                    Id                  = $App.Id
                                    KeyId               = $Secret.KeyId
                                    Description         = $Secret.DisplayName
                                }
                            }
                        }
                    }

                    if ($PSBoundParameters.ContainsKey('ShowExpiredKeys')) {
                        $SecretApp | Sort-Object DaysUntilExpiration
                    } else {
                        $SecretApp | Sort-Object DaysUntilExpiration | Where-Object {$_.DaysUntilExpiration -ge 0}
                    }
            }
        } catch {
            Write-Error $_.Exception.Message
        }
    }

    END {}
}

Script Parameters

    No Parameters

DataType: N/A
Description: Gather all apps and display the ones that have a secret or certificate expiring within 30 days.
 

    -ShowOnlyCertificates

DataType: switch
Description: Only display certificates in the output. Expired keys and secrets will not be shown.
 

    -ShowOnlySecrets

DataType: switch
Description: Only display secrets in the output. Expired keys and certificates will not be shown.
 

    -ShowExpiredKeys

DataType: switch
Description: Display certificates or secrets are near expiration or have already expired.
 

    -DaysWithinExpiration

DataType: integer
Description: Set the time frame to include expiring keys. This is defaulted to 30 days.
 

    -AppId

DataType: string
Description: Specify an AppId (client Id) to see that specific applications keys.
 

Example 1 – Calling the script with no parameters

Since this is a function, you’ll need to dot source it to load it into memory. Assuming the file is your desktop you can run.

. $Home\Desktop\Get-MgApplicationCertificateAndSecretExpiration.ps1
Get-MgApplicationCertificateAndSecretExpiration

No Parameters
 

Using the default output, take a look at the KeyType, ExpirationDate, DaysUntilExpiration and ThumbPrint. Certificates will display a thumbprint while secrets will not.
 

Example 2 – Display Only Certificates that are expired or nearing expiration

Get-MgApplicationCertificateAndSecretExpiration -ShowOnlyCertificates -ShowExpiredKeys `
| Select-Object AppDisplayName, KeyType, ExpirationDate, DaysUntilExpiration, KeyId, ThumbPrint

Certificates and Expired Keys
 

If you recall Portal screenshot above, our “App Automation 1” application had an expired cert with the thumbprint starting in “2E8972E” and the Key ID (certificate ID) started with “6f395fd4”. This is the same information we can pull in PowerShell
 

Example 3 – Display Only secrets that are expiring within 5 days

Get-MgApplicationCertificateAndSecretExpiration -ShowOnlySecrets -DaysWithinExpiration 5
| Select-Object AppDisplayName, KeyType, ExpirationDate, DaysUntilExpiration

Show only Secrets

Conclusion

Hopefully this article was able to show you get an application certificate and secret expiration with Graph API. This is super useful to keep in your automation toolkit since the Azure world is now moving everything to Microsoft Graph.

5/5 - (6 votes)

Paul Contreras

Hi, my name is Paul and I am a Sysadmin who enjoys working on various technologies from Microsoft, VMWare, Cisco and many others. Join me as I document my trials and tribulations of the daily grind of System Administration.

6 Comments

  1. Hi Paul, thanks for the beautiful script and it works pefectly. only one error I am getting with certificates is
    “Get-MgApplicationCertificateAndSecretExpiration: Exception calling “ToBase64String” with “1” argument(s): “Value cannot be null. (Parameter ‘inArray’)” , though its working pefectly with Secrets, I tried to find any syntactical error in regards to Array but so far unable to get it. Can you shed some lights on it please ? Only problem is with the Certificate thing.

  2. Great tool, functional and also ideal as an introduction and starting point for someone with little powershell and less MS Graph. Clear and easy to change and scale.
    Great script, thanks a lot Paul!

  3. Nice script but it would be nice if you could add few more lines to send an alert to owner of the application whose certificate and/or client secrets are expiring.

  4. Hello
    Excellent Script. I need little bit more than that. How can I get App owner name, email and status of Certificates & secrets
    . Also like to know how can I send the notification to app owners
    Lastly how can run as unattended script using Certifiacate and App ID
    Please let me know
    Regards
    Abdul

Leave a Reply

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