Azure App registration Client Secret expiration notification with MS Teams Adaptive Cards

Logic App Microsoft Graph

SITEMAP

Introduction

Have you ever had a situation where your Azure App registration Client Secret was about to expire soon or has already expired? Or have you wondered what to do to be informed in advance that your password will expire? This post is an answer. It shows how to set up a simple Logic App in Azure that checks periodically for Secret expiration dates and notifies you in advance when they are going to expire soon.

Azure App registration

An App registration is required to set up notifications in a way I am showing in this blog article. You can either create a dedicated App registration for this task or use the existing one. There are a few Apps in my App registrations. 

There are two requirements:

  • your App registration must have a valid Client Secret:
  • your App needs the following Application-Type API permission for Microsoft Graph. Please notice that the permission can be granted by Tenant Admin only:

Logic App

Recurrence

The requirement is to check the expiration of Secrets every day. To accomplish this we need to start with the Recurrence block.

Initialization of Variables

There are three variables that we are going to use. We must initialize them first:

  • appID – it will store the Application ID. Type = String.
  • displayName – it will store the name of our App registration. Type = String.
  • passwordCredential – it will store our Secret details. Type = Array.

Please be careful when selecting the types of each variable. Our Log App looks as follows now:

Microsoft Graph API Call

The next step is to make an API call to Microsoft Graph to get all the information we need. The important parts are:

  • Method = GET
  • URI = https://graph.microsoft.com/v1.0/applications?$filter=startswith(displayName,’Aviatrix’)&$select=id,appId,displayName,passwordCredentials
  • Authentication type = Active Directory OAuth
  • Tenant is your App registration Tenant ID (Directory ID)
  • Audience = https://graph.microsoft.com
  • Client ID is your Application ID
  • Credential Type = Secret
  • Secret is a real Value of your Secret
Please notice that I used an argument called ‘filter’ when selecting the Applications. You can omit this argument and run the API call without it.

Optionally, you could run a Logic App now. If everything is working you will get an output in JSON with the details about your Applications. This output could be used in the next step to generate the schema. If you do not Run the Logic App now, I will put a schema for you anyway.

Data Parsing

There will be some data received after executing the API call. We must tell the Log App what the schema of data looks like. “Parse JSON” block will be used. There are two options to provide the Schema:

  • you could copy the output that you got from the API Call and paste it into “Use sample payload to generate schema”
  • or you could just paste the Schema I put below in the “Schema” field.

Both ways work just fine.

				
					{
    "properties": {
        "@@odata.context": {
            "type": "string"
        },
        "@@odata.nextLink": {
            "type": "string"
        },
        "value": {
            "items": {
                "properties": {
                    "appId": {
                        "type": "string"
                    },
                    "displayName": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string"
                    },
                    "passwordCredentials": {
                        "items": {
                            "properties": {
                                "customKeyIdentifier": {},
                                "displayName": {
                                    "type": "string"
                                },
                                "endDateTime": {
                                    "type": "string"
                                },
                                "hint": {
                                    "type": "string"
                                },
                                "keyId": {
                                    "type": "string"
                                },
                                "secretText": {},
                                "startDateTime": {
                                    "type": "string"
                                }
                            },
                            "required": [
                                "customKeyIdentifier",
                                "displayName",
                                "endDateTime",
                                "hint",
                                "keyId",
                                "secretText",
                                "startDateTime"
                            ],
                            "type": "object"
                        },
                        "type": "array"
                    }
                },
                "required": [
                    "id",
                    "appId",
                    "displayName",
                    "passwordCredentials"
                ],
                "type": "object"
            },
            "type": "array"
        }
    },
    "type": "object"
}
				
			

As you can see the Schema contains the parts important for us: appId, displayName, and passwordCredentials array (with argument endDateTime).

Checking Expiration Date and Sending Notifications

The last part is to extract the required information from the received data and put them into variables that we created at the beginning. This has to be done for each Application (this is why we need for-each loop).

For each value, we are going to get from the “Parse JSON” step we want to set the variables:

Once the variables are set, there is another for-each loop (created “inside” the first loop). Please keep in mind that an App could have more than just one Secret. This time we need to iterate through all the Secrets that the App could have.

The output from previous steps is a Dynamic Content from Parse JSON = passwordCredentials

The next step is to add Condition with the following arguments:

  • value before the operator (Expression): items(‘For_each_passwordCredential’)?[‘endDateTime’]
  • operator: is less than
  • value after the operator (Expression): addToTime(utcNow(),30,’day’)

Notifications - Adaptive Cards

The condition checks whether endDateTime is less than 30 days. If TRUE we want a notification (Adaptive Card) to be sent to the MS Teams Channel with all the details about the expiring Secret.

There are two things to keep in mind:

  • the Adaptive Card block we are going to use is a type of “wait for a response”. What does it mean? It means that if the reception is not confirmed your Run will stuck in the “Running” state. This is the reason why I included an Action in the Adaptive Card with a Submit button. After clicking the submit button (by a user/receiver) your Run will be finished successfuly. 

The Message for Adaptive Card:

				
					{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "TextBlock",
            "size": "Large",
            "weight": "Bolder",
            "text": "WARNING! Secret Key Expiration for @{items('For_each_Application')?['displayName']}",
        },
        {
            "type": "TextBlock",
            "size": "Medium",
            "text": "The application @{items('For_each_Application')?['displayName']} has a Secret Key that either already expired or is about to expire.",
        },
        {
            "type": "TextBlock",
            "size": "Medium",
            "text": "Secret expiration date: @{items('For_each_passwordCredential')?['endDateTime']}",
        },
    ],
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.0",
    "msTeams": 
		{ 
			"width": "full" 
		},
   "actions": [
        {
            "type": "Action.Submit",
            "title": "OK"
        },
    ],
}
				
			
  • you must connect to your MS Teams to set this type of integration

The complete Logic App (from high-level perspective) looks like the following:

Testing Logic App

You can test that your Logic App is working properly by running it from an Azure Portal (use the “Run Trigger” option):

As mentioned earlier. The Logic App will wait for a response from the MS Teams. The clock will keep ticking unless you confirm the reception of the message by clicking the “OK” button in the MS Teams. Before confirming the reception, the Run will stuck in the “Running” state.

Verifying MS Teams Notifications

Let’s see how the notifications look like in the MS Teams Channel. There is one notification per application.

Please notice that there is an “OK” button. Once you click it, the Run will change its state from “Running” to “Successful”.

Ending Word

As you can see, it is fairly easy to set up notifications for your Secrets expiration. There is one drawback though, you must grant Microsoft Graph permissions to your Logic App (it is not always possible or easy in huge organizations). 

Of course, instead of using the MS Teams channel, you could set up an e-mail to be sent to your mailbox. The implementation of such flow is almost the same as what I presented above. The only difference is the type of block you use for the “TRUE” action of a condition at the very end of the flow.

Useful Links

Leave a Reply

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