Scripts that read or change Intune through Microsoft Graph usually fail in one of three ways: 401 (no usable token), 403 (a token without the right permission, or an admin without the right Intune role) and 429 (too many requests). The responses look alike, but the fixes don't. In this post I'll decode each one, show how to check what your token really carries, and add the paging and retry logic that keeps long exports alive.
Errors this guide fixes
Application is not authorized to perform this operationGraph response (403 Forbidden)Insufficient privileges to complete the operation.Graph 403 (Authorization_RequestDenied)InvalidAuthenticationTokenGraph response (401 Unauthorized)429 Too Many RequestsGraph response (Retry-After header)503 Service UnavailableGraph response (mobileApps listing)AADSTS65001: The user or administrator has not consented to use the applicationConnect-MgGraph sign-in
Symptoms#
A 403 from an Intune endpoint looks like this (IDs and URL shortened):
{
"error": {
"code": "Forbidden",
"message": "Application is not authorized to perform this operation - Operation ID (for customer support): ... - Activity ID: ... - Url: https://....manage.microsoft.com/..."
}
}Other responses that show up in the same scripts:
401 Unauthorizedwith the codeInvalidAuthenticationToken.403with the error codeAuthorization_RequestDeniedand the messageInsufficient privileges to complete the operation.(two separate fields in the JSON), from the Microsoft Entra parts of a script (users, groups, devices), not from Intune.429 Too Many Requests, usually with aRetry-Afterheader.503 Service Unavailablewhen listing apps underdeviceAppManagement/mobileAppswith large pages.- At sign-in:
AADSTS65001: The user or administrator has not consented to use the application.
Why it happens#
| Response | Usual cause |
|---|---|
401 InvalidAuthenticationToken | No token, an expired token in a long-running job, or a token issued for another resource |
| 403 "Application is not authorized…" | The token lacks the Intune permission for that endpoint: wrong scope, delegated and application permissions mixed up, or admin consent never granted |
| 403 with permissions that look right | A delegated call made by an admin whose Intune role doesn't allow the action, or a script endpoint still using the old permission (see step 3) |
403 Authorization_RequestDenied | Missing directory permission (for example on groups or devices) or Microsoft Entra role |
| 429 | Too many requests from the app or tenant in a short window; Intune has its own limits on top of the global Graph limits, per app and per tenant, with lower limits for writes (POST, PUT, PATCH, DELETE) |
503 on mobileApps | The page of apps is too large |
Microsoft states that all Intune permission scopes require administrator access, so an admin must grant consent, and a delegated script also needs a user with matching Intune role permissions.
How to fix it#
1. Check what the token carries#
Connect-MgGraph -Scopes 'DeviceManagementManagedDevices.Read.All' -NoWelcome
Get-MgContext | Select-Object AuthType, ClientId, Account, ScopesAuthType tells you whether you're Delegated or AppOnly; Scopes lists what was actually granted. If you call REST with your own token, decode it: delegated permissions appear in the scp claim, application permissions in roles.
2. Request the permission the endpoint needs#
Find-MgGraphCommand -Command Get-MgDeviceManagementManagedDevice |
Select-Object -First 1 -ExpandProperty Permissions| Task | Permission |
|---|---|
| Read managed devices | DeviceManagementManagedDevices.Read.All |
| Wipe, retire, remote lock, reboot | DeviceManagementManagedDevices.PrivilegedOperations.All |
| Configuration profiles, compliance policies | DeviceManagementConfiguration.Read.All / .ReadWrite.All |
| Apps, app protection, app configuration | DeviceManagementApps.Read.All / .ReadWrite.All |
| Scripts and Remediations | DeviceManagementScripts.Read.All / .ReadWrite.All |
| Enrollment, Apple tokens, connectors | DeviceManagementServiceConfig.Read.All / .ReadWrite.All |
| Intune roles and assignments | DeviceManagementRBAC.Read.All / .ReadWrite.All |
For an app registration, add these under API permissions › Add a permission › Microsoft Graph as Application permissions for unattended jobs or Delegated permissions for interactive use, then grant admin consent. For interactive Microsoft Graph PowerShell, an admin has to consent to the scopes once; otherwise users hit AADSTS65001 or an approval prompt.
3. Check the scripts permission change#
Since 31 July 2025, Microsoft Graph uses the DeviceManagementScripts permissions instead of DeviceManagementConfiguration for deviceManagementScripts, deviceHealthScripts (Remediations), deviceShellScripts, deviceComplianceScripts and deviceCustomAttributeShellScripts. Automation that read or uploaded scripts before that date can now get 403 until you add and consent to the new permission.
4. Delegated calls: the admin's Intune role still applies#
The Intune admin center itself works through Microsoft Graph, so a delegated script can only do what the signed-in admin could do in the portal. A role without the action returns 403, and scope tags limit which objects come back, so a short device list can be RBAC rather than a bug (see Intune RBAC and scope tags). Application permissions have no signed-in admin; Intune role assignments target users and groups, so the Graph permission you grant is the whole boundary. Grant the least you need.
5. Page through results and back off on 429#
Intune list endpoints return results in pages. Follow @odata.nextLink (or use -All on SDK cmdlets), and use $select to keep pages small:
$uri = 'v1.0/deviceManagement/managedDevices?$select=id,deviceName,complianceState,lastSyncDateTime'
$devices = @(while ($uri) {
$page = Invoke-MgGraphRequest -Method GET -Uri $uri
$page.value
$uri = $page.'@odata.nextLink'
})
"Devices retrieved: $($devices.Count)"The Microsoft Graph SDKs already retry throttled requests using Retry-After or exponential backoff, but requests inside a JSON batch aren't retried automatically. If you call REST directly, honour the header yourself (PowerShell 7):
function Invoke-GraphWithRetry ($Uri, $Headers, $MaxAttempts = 5) {
for ($i = 1; $i -le $MaxAttempts; $i++) {
try { return Invoke-RestMethod -Uri $Uri -Headers $Headers -ErrorAction Stop }
catch {
$resp = $_.Exception.Response
if (-not $resp -or [int]$resp.StatusCode -ne 429 -or $i -eq $MaxAttempts) { throw }
$wait = $resp.Headers.RetryAfter.Delta.TotalSeconds
if (-not $wait) { $wait = [math]::Pow(2, $i) * 5 } # no header: back off
Start-Sleep -Seconds $wait
}
}
}Microsoft's guidance is to fall back to exponential backoff whenever a 429 arrives without a Retry-After header, hence the fallback. For app listings that return 503, Microsoft recommends a smaller page size, such as $top=20.
6. Choose beta or v1.0 deliberately#
Many Intune endpoints and properties exist only in beta (Microsoft.Graph.Beta, cmdlets named Get-MgBeta…). Beta can change without notice, so pin module versions and re-test after updates. To find the exact call behind a portal blade, watch the browser's developer tools network tab while you use the Intune admin center.
Verify the fix#
Get-MgContextshows the expectedAuthTypeand the new scope.- The same request returns 200, and record counts match what the Intune admin center shows for that admin.
- A full export completes with no unhandled 429; log how often your retry waited.
- For app-only jobs, the service principal sign-ins in the Microsoft Entra sign-in logs show successful sign-ins for the app.
Prevent it next time#
- Use one app registration per automation, least-privilege permissions and certificate credentials, as in Automating Intune device reports with Microsoft Graph PowerShell.
- Avoid per-device GET loops; filter on the server, select only needed properties and run writes sequentially.
- Watch Microsoft's permission changes (like the scripts change) and the Intune limits on the throttling limits page.
References#
- How to use Microsoft Entra ID to access the Intune APIs in Microsoft Graph (Microsoft Learn)
- Microsoft Graph throttling guidance (Microsoft Learn)
- Microsoft Graph service-specific throttling limits (Microsoft Learn)

