Azure AD app registration secrets: a quick script to check who expires soon

When setting Azure AD app registration for using OAUTH 2 authentication, you need to create a client secret:

A client secret has an expiration date that now (from the Azure Portal) can be set to 24 months as maximum:

The option “Never” (for creating a secret that never expires) is disappeared from the UI for security reasons and Microsoft doesn’t want that you create secrets with an unlimited expiration date.

To be more clear, at the time of writing this post it could be possible to create a secret that expires after more than 2 years directly with Powershell with something like the following:

$startDate = Get-Date
$endDate = $startDate.AddYears(99)
$aadAppsecret01 = New-AzureADApplicationPasswordCredential -ObjectId YOURAPPID -StartDate $startDate -EndDate $endDate -CustomKeyIdentifier YOURSECRETNAME

but I cannot guarantee how long this will be supported. Microsoft was quite clear on this:

There are plans to limit lifetimes of the secret administratively. However, there are no current timelines or ETAs of when this will happen. Removing the UX option to have never expiring secrets is a first step of that process (you can still create secrets that never expire with PowerShell, AZ CLI and Graph API).

Obviously, a secret that expires could have a serious impact on your working applications. So, how you can monitor the expiration of your client secrets? This was a question that many partners (also internally) have raised to me recently.

Unfortunately, there’s not (at the moment) a native Microsoft’s features that sends an alert when a secret expires, so you need to create your own checks.

A possible solution could be to use Graph APIs and getting informations on your App Registrations in your Azure tenant by using a timer triggered Azure Logic Apps that sends an email with the secret that expires.

But if you want a more quick and easy solution, here there’s a Powershell script for doing that:

#secret expiration date filter (for example 30 days)
$LimitExpirationDays = 30

#Retrieving the list of secrets that expires in the above range of days
$SecretsToExpire = Get-AzureADApplication -All:$true | ForEach-Object {
    $app = $_
    @(
        Get-AzureADApplicationPasswordCredential -ObjectId $_.ObjectId
        Get-AzureADApplicationKeyCredential -ObjectId $_.ObjectId
    ) | Where-Object {
        $_.EndDate -lt (Get-Date).AddDays($LimitExpirationDays)
    } | ForEach-Object {
        $id = "Not set"
        if($_.CustomKeyIdentifier) {
            $id = [System.Text.Encoding]::UTF8.GetString($_.CustomKeyIdentifier)
        }
        [PSCustomObject] @{
            App = $app.DisplayName
            ObjectID = $app.ObjectId
            AppId = $app.AppId
            Type = $_.GetType().name
            KeyIdentifier = $id
            EndDate = $_.EndDate
        }
    }
}

#Printing the list of secrets that are near to expire
if($SecretsToExpire.Count -EQ 0) {
    Write-Output "No secrets found that will expire in this range"
}
else {
    Write-Output "Secrets that will expire in this range:"
    Write-Output $SecretsToExpire.Count
    Write-Output $SecretsToExpire
}

This script permits you to select a number of days until your secret expires (for example, I want to retrieve all my secrets that expires in the next 30 days), it retrieves the secret details from your Azure AD app registrations and lists the secrets that are near to expire.

The output will be something like the following:

You can execute this script on a Runbook, send the results via email and much more (this is totally up to you). The goal of this post is just for saying that you need to consider this check into your set of admin tasks for your tenants, and here you have a quick and simple possible solution.

The complete script is available on my Github repo here.

11 Comments

  1. If you need a modern version of this for Powershell 7 and the new Azure AD module I adapted the above.

    #secret expiration date filter (for example 30 days)
    $LimitExpirationDays = 30

    #Retrieving the list of secrets that expires in the above range of days
    $SecretsToExpire = Get-AzADApplication | ForEach-Object {
    $app = $_
    @(
    Get-AzADAppCredential -ObjectId $_.Id
    ) | Where-Object {
    $_.EndDateTime -lt (Get-Date).AddDays($LimitExpirationDays)
    } | ForEach-Object {
    $id = “Not set”
    if($_.CustomKeyIdentifier) {
    $id = [System.Text.Encoding]::UTF8.GetString($_.CustomKeyIdentifier)
    }
    [PSCustomObject] @{
    App = $app.DisplayName
    ObjectID = $app.Id
    AppId = $app.AppId
    Type = $_.GetType().name
    KeyIdentifier = $id
    EndDate = $_.EndDateTime
    }
    }
    }

    #Printing the list of secrets that are near to expire
    if($SecretsToExpire.Count -EQ 0) {
    Write-Output “No secrets found that will expire in this range”
    } else {
    Write-Output “Secrets that will expire in this range:”
    Write-Output $SecretsToExpire.Count
    Write-Output $SecretsToExpire
    }

    Like

    1. Hi, thanks for the script. Do you something cooked up to address below:
      1>trigger an email to Owner for client secret & certificate expiry in the intervals 60 days, 30 days.

      Like

    1. You can write the output of the script to a mail messabe body directly from Powershell.
      Here is a basic example for sending an email via SMTP:
      $Username = “MyUserName”;
      $Password = “MyPassword”;

      function Send-ToEmail([string]$email, [string]$body){

      $message = new-object Net.Mail.MailMessage;
      $message.From = “YourName@gmail.com”;
      $message.To.Add($email);
      $message.Subject = “subject text here…”;
      $message.Body = $body;
      $attachment = New-Object Net.Mail.Attachment($attachmentpath);
      $message.Attachments.Add($attachment);

      $smtp = new-object Net.Mail.SmtpClient(“smtp.gmail.com”, “587”);
      $smtp.EnableSSL = $true;
      $smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
      $smtp.send($message);
      write-host “Mail Sent” ;
      $attachment.Dispose();
      }
      Send-ToEmail -email “reciever@gmail.com”;

      Like

  2. Hi. Do you just add that to the bottom of the script to send it via email? Do you have an updated script that you can share that will send an email? I created the script in a runbook and scheduled it to run every week just need it to email the output. Thanks for the script btw, it works great, just need this last part.

    Like

  3. What kind of privileges are required for Azure Automation Account to perform this operation?
    I get error:
    [Authorization_RequestDenied] : Insufficient privileges to complete the operation.

    Like

      1. What do you mean by you, this is working as MSI and it has to have some privileges I suppose and mine is lacking some, so I probably have to assign (by someone, because I am just User) some kind of role for this Automation Account app.

        Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.