I bet I have written this up ten times, I am sick of needing to remember it.
Windows 10:
Windows 11:
DavesTechnology: Having worked in IT for many years I work with lots of customers and different technologies. Day to day it should be easy but there is always strange stuff that happens with both. Not often it just works. Let me tell you why... Dave's Technology DavesTechnology
I bet I have written this up ten times, I am sick of needing to remember it.
Windows 10:
Windows 11:
All Intune apps and who/what has them installed.
This is much better, it is using the REST API so can get rate limited (HTTP 429), but insert a delay and get a coffee. This pulled out a ~150,000 line CSV.
Huge shout out to a mate of a mate, who created https://graphxray.merill.net/ as this allowed me to find the undocumented endpoints and command lines, the traditional PowerShell did not work, but the REST APIs work well.
Thanks Eunice, Dhruv, Clement, Monica & @merill.
## Script starts
#ApplicationProfile
$tenantId = 'xxx'
$appId = 'xxx' # Application (client) ID
$appSecret = 'xxx' #Value
# Obtain an access token using client credentials
$body = @{
grant_type = "client_credentials"
scope = "https://graph.microsoft.com/.default"
client_id = $appId
client_secret = $appSecret
}
$response = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"
$token = $response.access_token
# Retrieve applications
$appUri = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps"
$applications = Invoke-RestMethod -Uri $appUri -Method Get -Headers @{Authorization = "Bearer $token"}
# Prepare output collection
$output = @()
# Loop through each application
foreach ($checkApp in $applications.value) {
$ApplicationId = $checkApp.id
$ApplicationName = $checkApp.displayName
# Request URI and parameters for the report
$uri = "https://graph.microsoft.com/beta/deviceManagement/reports/retrieveDeviceAppInstallationStatusReport"
$params = @{
select = @("DeviceName", "UserPrincipalName", "DeviceId")
skip = 0
top = 5000 ### Max number of users/computers allocation to the app
filter = "(ApplicationId eq '$ApplicationId')"
orderBy = @()
}
# Make the POST request
$response = Invoke-RestMethod -Uri $uri -Method Post -Headers @{Authorization = "Bearer $token"} -Body ($params | ConvertTo-Json) -ContentType "application/json"
# Check if Values contain data
if ($response.Values) {
foreach ($value in $response.Values) {
# Split the concatenated string to extract the relevant fields
$fields = $value -split ' '
# Assuming the format is consistent, map the fields
$outputObject = [PSCustomObject]@{
ApplicationName = $ApplicationName
ComputerName = $fields[1] # Assuming this is DeviceName
UserUPN = $fields[2] # Assuming this is UserPrincipalName
## There are more here, I just didnt need them
}
$output += $outputObject
}
} else {
Write-Host "No installation data found for Application ID $ApplicationId ($ApplicationName)." -ForegroundColor Yellow
}
Start-Sleep -Seconds 6 # Adjust the number of seconds as necessary, this is to stop HTTP 429 rate limiting.
}
# Output to screen
$output | Format-Table -AutoSize
$output.Count
# Export to CSV
$output | Export-Csv -Path "ApplicationInstallStatusReport.csv" -NoTypeInformation