Search Intune for Devices with Application Installed

This script uses the GraphAPI to check all devices in Intune to see if they have a particular application installed.

$Application = "*PuTTY*"
$Username = '[email protected]'

Function Get-AuthToken {
    <#
    .SYNOPSIS
    This function is used to authenticate with the Graph API REST interface
    .DESCRIPTION
    The function authenticate with the Graph API Interface with the tenant name
    .EXAMPLE
    Get-AuthToken
    Authenticates you with the Graph API interface
    .NOTES
    NAME: Get-AuthToken
    #>
    [cmdletbinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$User
    )
    
    $userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User
    $tenant = $userUpn.Host
    
    $AadModule = Get-Module -Name "AzureAD" -ListAvailable
    if(-not $AadModule) {
        $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable
    }
    
    if (-not $AadModule) {
            write-host
        throw("AzureAD Powershell module not installed..`n" +
        "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt")
    }
    
    # Getting path to ActiveDirectory Assemblies
    # If the module count is greater than 1 find the latest version
    if($AadModule.count -gt 1){
        $Latest_Version = ($AadModule | select version | Sort-Object)[-1]
        $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version }
        
        # Checking if there are multiple versions of the same module found
        if($AadModule.count -gt 1){
            $aadModule = $AadModule | select -Unique
        }
    
        $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
        $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
    
    } else {
        $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
        $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
    }
    
    [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
    [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
    $clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
    $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
    $resourceAppIdURI = "https://graph.microsoft.com"
    $authority = "https://login.microsoftonline.com/$Tenant"
    
    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
    $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
    $userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId")
    $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result
    
    # If the accesstoken is valid then create the authentication header
    if($authResult.AccessToken){
        # Creating header for Authorization token
        $authHeader = @{
            'Content-Type'='application/json'
            'Authorization'="Bearer " + $authResult.AccessToken
            'ExpiresOn'=$authResult.ExpiresOn
        }
        return $authHeader
    } else {
        throw "Authorization Access Token is null, please re-run authentication..."
    }
}

Function Get-MsGraphData($Path) {
    $FullUri = "https://graph.microsoft.com/beta/$Path"
    [System.Collections.Generic.List[PSObject]]$Collection = @()
    $NextLink = $FullUri

    do {
        $Result = Invoke-RestMethod -Method Get -Uri $NextLink -Headers $AuthHeader
        if($Result.'@odata.count'){
            $Result.value | ForEach-Object{$Collection.Add($_)}
        } else {
            $Collection.Add($Result)
        }
        $NextLink = $Result.'@odata.nextLink'
    } while ($NextLink)

    return $Collection
}

# Get authentication token
$AuthHeader = Get-AuthToken -User $Username

# Get all devices in Intune
$AllDevices = Get-MsGraphData "deviceManagement/managedDevices"

# Get detected app for each device and check for app name
[System.Collections.Generic.List[PSObject]]$FoundApp = @()
$wp = 1
foreach($Device in $AllDevices) {
    Write-Progress -Activity "Found $($FoundApp.count)" -Status "$wp of $($AllDevices.count)" -PercentComplete $(($wp/$($AllDevices.count))*100) -id 1
    $AppData = Get-MsGraphData "deviceManagement/managedDevices/$($Device.id)?`$expand=detectedApps"
    $DetectedApp = $AppData.detectedApps | ?{$_.displayname -like $Application}
    if($DetectedApp){
        $DetectedApp | Select-Object @{l='DeviceName';e={$Device.DeviceName}}, @{l='Application';e={$_.displayname}}, Version, SizeInByte,
            @{l='LastSyncDateTime';e={$Device.lastSyncDateTime}}, @{l='DeviceId';e={$Device.id}} | Foreach-Object { $FoundApp.Add($_) }
    }
    $wp++
}
Write-Progress -Activity "Done" -Id 1 -Completed

$FoundApp | Sort-Object deviceName | Format-Table