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.
Table Of Contents
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:
- Navigate to App Registrations
- Select an App that has a certificate or secret added
- Go to Certificates & secrets blade
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
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
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
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.
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.
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!
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.
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
What version of the MS Graph module is being used here?
How would one go about importing this to an Azure Runbook? Thanks!