How to Get All Installed Apps on Devices from Intune

Table of content

Learn how to pull a complete list of installed apps from Intune—step by step, with Intune console, PowerShell and Graph API.

Prerequisites

Before you start, make sure you have:

  • An Intune Administrator account with access to Apps > Monitor > Discovered Apps Microsoft Learn.

  • PowerShell 7+ and the Microsoft.Graph.Intune module (for automation) Microsoft Learn.

  • (Optional) An Azure AD app registration with the right Graph permissions if you plan to call the Graph API directly Microsoft Learn.

Method 1: Quick CSV Export (Intune Portal)

  1. Sign in to the Microsoft Endpoint Manager admin center.

  2. Navigate to Apps > Monitor > Discovered apps.

  3. Click Export to download a .csv report containing:

    • Application name

    • Platform

    • Version

    • Publisher

    • Device count

Wow, That’s it—a one-click inventory, perfect for quick audits. Also, if you need the result for specific application, just search with App name.

Method 2: Automate with PowerShell

Automating your export means you’ll never click the portal again. This script will connect to Microsoft Intune using Graph API and fetch all the installed application on all devices and will export to C:\ drive.

    # Define Azure AD and App Details
$ClientID     = "d86a2d7c-25c4-1111-9521-1bfe52cde8f6"
$ClientSecret = 'wB38Q~zqG1MuK.IEehNp.DnzPxr8iXVRnAfFNdnd'
$TenantID     = "e869957b-68cc-4373-1111-ff5d9b56ab9c"

$RequestTimeout = 120 # Request timeout in seconds

# Start local log transcript
$LogFilePath = Join-Path $PSScriptRoot "C:\Test.log"
Start-Transcript -Path $LogFilePath

# Get Access Token
$TokenUrl = "https://login.microsoftonline.com/$TenantID/oauth2/v2.0/token"
$TokenBody = @{
    Client_Id     = $ClientID
    Scope         = "https://graph.microsoft.com/.default"
    Client_Secret = $ClientSecret
    Grant_Type    = "client_credentials"
}
$TokenResponse = Invoke-RestMethod -Uri $TokenUrl -Method Post -Body $TokenBody -TimeoutSec $RequestTimeout
$AccessToken = $TokenResponse.Access_Token

# Define Headers for Graph API
$Headers = @{
    Authorization = "Bearer $AccessToken"
}

# Query Microsoft Graph API for Managed Devices
$BaseUrl = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices"
$Filter = "`$filter=operatingSystem+eq+'Windows'"
$ApiUrl = $BaseUrl + '?' + $Filter

Function Invoke-WithRetry {
    Param (
        [ScriptBlock]$ScriptBlock,
        [Int]$MaxRetries = 5,
        [Int]$TimeoutSec = 60
    )

    $RetryCount = 0
    Do {
        Try {
            Return & $ScriptBlock
        }
        Catch {
            $IsThrottled = $_.Exception.Response.StatusCode -eq 'TooManyRequests' -or $_.Exception.Response.StatusCode -eq '429'
            $IsConnectionClosed = $_.Exception.Message -like "*unexpected EOF or 0 bytes from the transport stream*"
            If ($IsThrottled -or $IsConnectionClosed) {
                $RetryCount++
                $BaseWaitTime = If ($_.Exception.Response.Headers['Retry-After']) { 
                    $_.Exception.Response.Headers['Retry-After']
                }
                Else { [Math]::Pow(2, $RetryCount) }
                # Adding randomness to the wait time
                $WaitTime = $BaseWaitTime + (Get-Random -Minimum 1 -Maximum 5)

                Write-Host "Request throttled or connection closed. Retrying in $WaitTime seconds..."
                Start-Sleep -Seconds $WaitTime
            }
            Else {
                Throw $_
            }
        }
    } While ($RetryCount -lt $MaxRetries)

    Throw "Maximum number of retries reached."
}

# Fetch the devices (Filter for Windows Devices)
$ManagedDevicesResponse = @()
$Top = "`$top=100"
$NextPageUrl = $ApiUrl + '&' + $Top

Do {
    Write-Host "Querying URL: $NextPageUrl"

    $Response = Invoke-WithRetry -ScriptBlock {
        Invoke-RestMethod -Uri $NextPageUrl -Headers $Headers -TimeoutSec $RequestTimeout
    } -TimeoutSec $RequestTimeout

    $ManagedDevicesResponse += $Response.Value

    $NextPageUrl = $Response."@odata.nextLink"
} While ($NextPageUrl)

# Loop Through Devices and Retrieve Information
$ReportData = @()
$TotalDevices = $ManagedDevicesResponse.Count
$Counter = 0

ForEach ($Device in $ManagedDevicesResponse) {
    try {
        $Counter++
        Write-Progress -Activity "Processing Devices" -Status "$Counter of $TotalDevices" -PercentComplete (($Counter / $TotalDevices) * 100)
        $DeviceId = $Device.Id

        # Define the URL for fetching detected apps for this device
        $DetectedAppsUrl = "https://graph.microsoft.com/beta/deviceManagement/managedDevices/$DeviceId/detectedApps"

        Write-Host "Fetching apps for DeviceId $DeviceId."

        # Fetch all detected apps for this device
        $InstalledApps = @()
        Do {
            $Response = Invoke-WithRetry -ScriptBlock {
                Invoke-RestMethod -Uri $DetectedAppsUrl -Headers $Headers -TimeoutSec 60
            }
            If ($Response.Value) {
                $InstalledApps += $Response.Value
            }
            $DetectedAppsUrl = $Response.'@odata.nextLink'
        } While ($DetectedAppsUrl)

        Write-Host "Apps detected on device $DeviceId : $($InstalledApps.Count)"

        # Filter Apps Containing "powerbi" and Add to Report Data
        $MatchingApps = $InstalledApps | Where-Object { $_.DisplayName -like "**" }
        Write-Host "Apps containing 'powerbi' on device $DeviceId : $($MatchingApps.Count)"
        ForEach ($App in $MatchingApps) {
            $ReportData += [PSCustomObject]@{
                UserPrincipalName = $Device.UserPrincipalName
                DeviceName        = $Device.DeviceName
                Manufacturer      = $Device.Manufacturer
                Model             = $Device.Model
                OperatingSystem   = $Device.OperatingSystem
                AppName           = $App.DisplayName
                AppVersion        = $App.Version
            }
        }
    }
    catch {
        Write-Host "An error occurred processing device $DeviceId. Error: $($_.Exception.Message)"
        continue
    }
}

# Sort the Report Data
$SortedReportData = $ReportData | Sort-Object DeviceName, UserPrincipalName, AppName, AppVersion, OperatingSystem

# Export Sorted Report Data to CSV
$CurrentDate = Get-Date -Format "MM-dd-yyyy"
$CsvPath = "C:\AllIntuneDiscoveredApps-$CurrentDate.csv"
$SortedReportData | Export-Csv -Path $CsvPath -NoTypeInformation   
            
                    
        

After this Powershell Graph API script is ran, All the applications installed on devices will be exported to the CSV, you can use this script in Power BI too if you need the live dashboard. You can run this script as Windows task scheduler job to get daily email to your inbox.

IntuneDiscoveredApp

Wrapping up

Well, that’s your complete toolkit to get all installed apps on devices from Intune—portal, PowerShell, or Graph API. Here’s what I think: pick the method that fits your workflow, automate it, and never miss a single app again.

For more hands-on Intune guides, swing by techeuc.com and subscribe for updates. If you need any help or facing any issues, Please reach out to me or contact me.