Azure AD App Tracking with Logic Apps


In this post, App Dev Manager Jason Venema outlines how Logic Apps is a great alternative to coding.


I’m a huge fan of Azure Logic Apps. Whenever I need a fast, practical solution to a problem and don’t want to write code, it’s my go-to service. With the plethora of out-of-the-box activities and connectors available, there is very little you can’t do with a Logic App.

Recently, I was asked by a customer with a very large internal Azure user base to help them find a way to keep track of Azure AD (AAD) application registrations in their directory. This customer has been on Azure for years, and the number of AAD application registrations has steadily grown during that time. There are so many applications now that it is hard for them to know which ones are still being used, and which are not. Furthermore, it’s common for application teams to create a secret key for their application and then forget that the key will eventually expire. How does the operations team know whom to contact when the expiration date is approaching?

image

It turns out, there are several ways to solve this problem. It’s simple to find all of the applications that no longer have any unexpired keys or certificates associated with them, and fairly safe to assume they are probably not in use anymore and can be deleted. To be extra careful, though, they wanted to send the owner of the application an email to give them a heads up that it was going to be deleted. The problem is, how do you know who the owner is?

Finding the Application Owner

A reasonable and obvious idea is to use the “Application Owner” field to keep track of the owner. The problem is, that field is optional and accepts any string of characters as input. If you allow end users to create their own app registrations, then there is no way to enforce them to put a value in that field. More often than not, the field is empty.

image

Our next thought was to leverage the AAD Audit Logs. Any time an event takes places in Azure AD – someone registers a new application or modifies an existing user account – that event gets recorded in the audit logs. In fact, the Azure AD team recently introduced the capability to route these audit logs to either a storage account or an Event Hub.

image

It would be a pretty simple matter to just check the “Archive to a storage account” box and be done with it. And that would work – if they ever needed to find out who registered a particular application, they could open up the storage account and search for it. But they wanted a solution that emphasized ease of searchability using a language like SQL.

Logic Apps and Cosmos DB to the Rescue

Rather than searching through log entries in a storage account, we decided to send the logs to an Event Hub and then trigger a Logic App whenever new events arrive. This gave us a lot more flexibility in terms of the types of events we wanted to react to (application creations) and the actions we wanted to take when those events occurred. The Logic App we created simply monitor the Event Hub for new messages, and when one arrives it examines the contents of the message to determine the type of event that occurred. If the event was for an application registration, then we take the event information – most importantly, the email address of the person who created it – and store it in a Cosmos DB database. We chose Cosmos DB because it makes querying for specific applications dead simple, and it’s easy and inexpensive to store data that will grow potentially very large over time.

Here are the steps we took to create the solution.

Create the Event Hub

First, we needed an Event Hub to send the AAD Audit log data to.

  1. From the Azure Portal, open the Event Hubs service.
  1. Select “Add” and fill out the form to create a new namespace. Keep a record of the region you select, so you can use the same one later for your Logic App and Cosmos DB instances.
  1. We chose 1 throughput unit, but you may have to adjust this accordingly based on how much activity your AAD instance gets.
  1. Click “Create”
  1. After the namespace is created, navigate to it in the portal. Then select “Event Hubs” (under “Entities”).
  1. Click on the “+Event Hub” button and fill out the form to create a new Event Hub. We expected a fairly low volume of data, so we went with 2 partitions.

Configure AAD Diagnostic Settings

Next, we configured the AAD diagnostic settings to export the audit logs to the Event Hub instance we just created.

  1. From the Azure Portal, open the Azure Active Directory service.
  1. Select “Audit Logs” from the left-hand menu, and then click “Export Data Settings” from the toolbar.
  1. Click on the “Add diagnostic setting” link.
  1. Select the “Stream to an Event Hub” checkbox. Directly below the checkbox, choose the Event Hub instance that you just created.
  1. Select the “AuditLogs” checkbox, but do not select “SignInLogs”. This will keep our logs to the bare minimum.
  1. Click on the “Save” button.

image

Create the Cosmos DB Database

Next, we need a Cosmos DB database to store the application registration events in.

  1. From the Azure Portal, open the Azure Cosmos DB service.
  1. Click the “+Add” button to create a new instance.
  1. In the “API” dropdown, choose “SQL”.
  1. Choose the same region that you used for the Event Hub in the “Location” dropdown.
  1. Click the “Create” button.
  1. Once the Cosmos DB instance is created, navigate to it in the Azure Portal and select the “+Add Collection” button.
  1. Fill out the form and keep a note of the “Database id” and “Collection Id” values that you use.
  1. To keep costs low, we chose “Fixed (10GB)” for storage capacity, and “Throughput” of 400 RU/s.
  1. Click the “OK” button.

image

Create the Logic App

The final step to tie everything together is to create the Logic App.

  1. From the Azure Portal, open the Logic Apps service.
  1. Click the “+Add” button to create a new instance, and be sure to choose the same region as in the previous steps for the “Location” field.
  1. When the Logic App is created, navigate to it in the portal.
  1. Choose the “Blank Logic App” template.

image

  1. Search for the “Event Hubs” trigger and select “When events are available in Event Hub”.

image

  1. Configure the Event Hub connection by choosing the namespace and Event Hub created previously, and then click “Create”.
  1. The trigger for your Logic App should now look like this.

image

  1. Add a new action after the Event Hub trigger. Search for “Parse JSON” in the search box and select the action to create it.
  1. In the “Content” field of the action use the Dynamic Content menu to select the “Content” field from the Event Hub action (see screenshot below). Note that it might take a few minutes after you create the Event Hub trigger for the dynamic content to populate with all of the fields.
  1. Below the “Schema” field, copy and paste in the following schema (Note: It is usually simpler to click the “Use sample payload to generate schema” option, but in this case I found that the generated schema needed to be modified slightly to account for the data.)

image

Copy and paste in the following schema:

{"type":"object","properties":{"records":{"type":"array","items":{"type":"object","properties":{"time":{"type":"string"},"resourceId":{"type":"string"},"operationName":{"type":"string"},"operationVersion":{},"category":{},"tenantId":{},"resultType":{},"resultSignature":{},"durationMs":{},"callerIpAddress":{},"correlationId":{},"identity":{},"Level":{},"properties":{"type":"object","properties":{"id":{},"category":{},"correlationId":{},"result":{},"resultReason":{},"activityDisplayName":{},"activityDateTime":{},"loggedByService":{},"initiatedBy":{"type":"object","properties":{"app":{"type":"object","properties":{"appId":{},"displayName":{},"servicePrincipalId":{},"servicePrincipalName":{}}}}},"targetResources":{"type":"array","items":{"type":"object","properties":{"id":{},"displayName":{},"modifiedProperties":{}}}},"additionalDetails":{"type":"array"}}}}}}}}

  1. Add a new step and search for “Condition”, then choose the first result.

image

  1. In the properties for the condition, use the Dynamic Content box to enter “operationName” as the value. Once you do this, the Logic App designer will automatically add a For-Each around the operation. This is expected, since the Event Hub event may contain multiple messages (and therefore, multiple operationName’s). Simply click the “Got It” button.

image

  1. Add two conditions for operationName equal to “Add application” or “Update application” in the condition step.

image

  1. Inside of the “If true” branch of the condition, add a new Azure Cosmos DB action “Create or update document (preview)”.

image

  1. Select the Cosmos DB instance that you created earlier.
  1. In the “Document” field, enter the values from the Event Hub message that we parsed earlier. Here is an example of the document that I created:

image

NOTE: You will notice that when you add certain properties to your document from the Dynamic Content selector, your action is automatically wrapped in a For-Each loop. That is expected any time you add a property that is an array. In the example above, the “id” property is embedded inside of “targetResources”, which is an array in the original JSON event. The “modifiedProperties - Item” property is also an array. As a result, my Cosmos DB action was wrapped inside of two For-Each loops.

Once completed, you should click the “Save” button. The completed Logic App looks like this:

image

Querying the Results

At this point, every time a new application gets registered in Azure AD you will see a record of that event stored in Cosmos DB. At this point, it is simple to query Cosmos DB to find out who registered a specific application. You can easily test your queries from the Cosmos DB data explorer in the Azure Portal:

image

Conclusion

By combining Azure AD audit logs with Logic Apps and Cosmos DB, you can easily store information about selected events that are important to your business in an easy to query format. This approach has many other applications beyond audit logs. If you come up with other great use cases, let me know!

Comments (1)

  1. JNGGG says:

    Thanks for the article Jason!

Skip to main content