Entra Enterprise Application Cleanup

Any large organization knows that the number of applications tied to their identity provider can quickly become difficult to manage. As an organization’s number of enterprise application integrations increases, the Entra (formerly Azure Active Directory) Admin Portal quickly becomes less usable.

By default, the Admin Portal shows the last 7 days of sign-ins for each app. Navigating through all the enterprise applications to look at sign-in logs is unwieldy. For most large tasks, using the Admin Portal isn’t very helpful.

For example, consider all the apps shown in Figure 1. Are they even in use anymore? Going through each app to look at the sign-in logs would take an inordinate amount of time. The collection of apps in Figure 1 is nowhere near what most organizations would have, but even in a lab environment you can see how difficult it would be to go through each app.

Review which Azure AD enterprise applications are in use
Figure 1 – Review which Entra enterprise applications are in use

So how do we get this situation under control? Scripting to the rescue!

What do we need?

Our goal is to determine whether an application has been used recently to see if we can remove it. We can use the PowerShell command Get-AzureADAuditSignInLogs to obtain this information. However, Azure sign-in logs are kept for only 30 days, so another method may need to be used for longer retention, such as logging to an Azure storage account or connecting Entra ID to Microsoft Sentinel. You can also use Azure Sentinel’s Log Analytics to query the data.

For the purposes of this article, we focus on existing logs and how we achieve our goal.

At the time of this writing, the Get-AzureADAuditSignInLogs argument is only available in the AzureADPreview PowerShell module. Before installing or updating this module, check to see if Get-AzureADAuditSignInLogs is included in the generally available AzureAD module. We can install or update the AzureADPreview module by running Install-Module -Name AzureADPreview or Update-Module -Name AzureADPreview.

Gathering Data

Once we have the AzureADPreview module installed, we connect to Entra ID using Connect-AzureAD. The credentials used to connect will need the ability to read—and if deleting, the ability to modify—enterprise applications. It’s recommended to use the least privilege necessary to complete the task. I’m using an account with the Security Reader role in Entra ID. The Application Administrator role will also allow us to read log information, but limiting to Security Reader will give us much more restricted access.

On to the fun stuff! We need to get a list of our applications. Achieving this is pretty straightforward; run Get-AzureADApplication. This command will provide you with the ObjectID, AppID, and DisplayName of your connected applications that have been registered in your Azure tenant. The Get-AzureADServicePrincipal command returns additional enterprise applications. Let’s put those attributes into a variable for safe keeping: $Apps = Get-AzureADApplication.

From here we can use our fancy new preview command to parse our logs and output data. Note that Microsoft throttles connections when using Get-AzureADAuditSignInLogs. Microsoft also throttles connections for retrieving sign-in logs, so we add a Start-Sleep parameter at the end of the loop to provide a break between applications. The following code is a proof of concept to determine in-use applications and provide some rudimentary reporting:

#To enable verbose 

[CmdletBinding()] 

Param() 

  

#Retrieve list of applications  

$Apps = Get-AzureADApplication  

  

#Loop through each application 

ForEach($App in $Apps){ 

    Write-Verbose "Processing $($App.DisplayName)" 

    #Retrieve logs filtered on AppID 

    $Log = Get-AzureADAuditSignInLogs -All $true -filter "appid eq '$($App.AppID)'" 

    #Create a custom object for output 

    [PSCustomObject]@{ 

        ApplicationName = $App.DisplayName 

        ApplicationID = $App.AppID 

        SignIns = $Log.count 

    } 

    #To prevent throttling on Sign-in Log querying, insert a sleep 

    Start-Sleep 1 

} 
Code language: PowerShell (powershell)

We now have output that we can sort through to determine whether an application has been in use in the past 30 days. This code will return ApplicationName, ApplicationID, and a count of how many sign-ins there have been for each application.

Cleanup

Once we have a list of enterprise applications we want to dispose of, we can use an administrative account with the Application Administrator role to perform cleanup. If there are only a handful of applications, it may be easiest to use the portal and disable or delete applications from there. If automation is desired or if there are a large number of applications to work with, there are a few commands that will help us along the way. We can use the Remove-AzureADApplication and Set-AzureADApplication commandlets. Removing is quite destructive, so we recommend just disabling the application instead. Use Set-AzureADApplication -IsDisabled $true. This gives us the ability to ensure that the application is not in use for however long we’d like to wait before removing it.

Final Thoughts

Although the process of cleaning up your enterprise applications can be quite time-consuming, there are several useful ways to report and manage applications in Azure. Ravenswood Technology Group has the expertise you need to generate valuable reports. We can also help design your enterprise application life cycle for optimal efficiency. Contact our experts today!

[RELEVANT BLOG CONTENT]

6 Tips to Harden Your Windows LAPS Deployment

In a previous blog post, we covered how to migrate to Windows Local Administrator Password Solution (LAPS). With Windows LAPS deployments gaining traction, it’s important

Migrating to Windows LAPS

Windows Local Administrator Password Solution (LAPS), now integrated into the OS, is the replacement for Microsoft LAPS, which was a separate installation. Windows LAPS is

7 thoughts on “Entra Enterprise Application Cleanup”

  1. Hi. This is a great script but it seems like it checks only “User Sign-Ins” it does not check “Service principal sign-ins” and “Managed identity sign-ins”. Is there any way to check it? Thanks.

    1. Avatar photo
      Tony Brzoskowski

      Thanks for the reply! I actually do have something written for that and will be updating to include what we did.

      1. Hi Tony. I have managed to get all the sign-ins by using following cmdlet:

        Get-AzureADAuditSignInLogs -All $true -filter “appid eq ‘$($App.AppID)’ and signInEventTypes/any(t: t eq ‘interactiveUser’ or t eq ‘nonInteractiveUser’ or t eq ‘servicePrincipal’ or t eq ‘managedIdentity’)”

        Regards.

  2. Hi Tony, your original script worked great. Do you have the amended one to check “Service principal sign-ins” and “Managed identity sign-ins”. I tried Marcin’s amendment but it threw up a number of errors. Thanks!

    1. Hi Greg. I had to modify my script a little bit. Below is the full version:

      #Connect Azure AD [AzureADPreview module is required]
      Connect-AzureAD

      #Get App Registrations only
      #$Apps = Get-AzureADApplication -All:$true

      #Gets all Apps in Azure tenant
      $Apps = Get-AzureADServicePrincipal -All:$true | ? {$_.Tags -eq “WindowsAzureActiveDirectoryIntegratedApp”}

      $SignIns = @()

      foreach($App in $Apps){

      $Obj = New-Object PSObject
      Start-Sleep -Milliseconds 500
      $Log = Get-AzureADAuditSignInLogs -All $true -filter “appid eq ‘$($App.AppID)’ and signInEventTypes/any(t: t eq ‘interactiveUser’ or t eq ‘nonInteractiveUser’ or t eq ‘servicePrincipal’ or t eq ‘managedIdentity’)”

      Add-Member -InputObject $Obj -MemberType NoteProperty -Name “ApplicationName” -Value $App.DisplayName -Force
      Add-Member -InputObject $Obj -MemberType NoteProperty -Name “ApplicationID” -Value $App.AppID -Force
      Add-Member -InputObject $Obj -MemberType NoteProperty -Name “SignIns” -Value $Log.count -Force

      $SignIns += $Obj
      }

      $SignIns | Export-Csv -Path “C:\Users\xxx\Downloads\$((Get-Date).ToString(‘ddMMyyyy’))AppSignInLogs.csv” -NoTypeInformation -Delimiter “;”

  3. Would it be possible to get extend the 30 days to like 90 or 180 days. That will of course require that we somehow collect the entries from our Log Analytics Workspace.

    1. Avatar photo
      Tony Brzoskowski

      Thanks Ashton – Yes, there are ways to grab the data from Log Analytics. I’ll keep this on my to-do and when I get something published I or someone else will link to the article here so you’re notified.

Leave a Comment

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

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