20 PowerShell Scripts Every Intune Admin Should Have (Graph API Edition) · CloudEngineerHub
PowerShell11 min read12 views
20 PowerShell Scripts Every Intune Admin Should Have (Graph API Edition)
Production-tested Microsoft Graph PowerShell patterns for Intune: device inventory, compliance drift, bulk retire and sync actions, throttling, and least-privilege auth.
C
CloudEngineerHub Team
Published: August 26, 2026· Last Updated: October 15, 2026·11 min read
20 PowerShell Scripts for Intune Admins: Graph API Edition
Almost every repetitive Intune task — inventory exports, compliance drift reports, bulk device actions, policy audits — is a ten-line PowerShell script against the Microsoft Graph API. The Microsoft Graph PowerShell SDK is the supported surface for all of it, and it replaces the deprecated MSOnline and Intune PowerShell previews. This collection covers the patterns that survive production: least-privilege connections, correct paging, throttling-aware loops, and reports you will actually email to management.
Visual Walkthrough
Consent, paging and Retry-After: the difference between a script and a liability.
C
CloudEngineerHub Team
Author
The CloudEngineerHub editorial team — Microsoft endpoint management and cloud engineers writing hands-on guides on Intune, Azure, Entra ID, Autopilot, Defender and PowerShell. Every article is reviewed against current Microsoft documentation.
429 + Retry-After honored — backoff, never hammer.
6
Act + log outcomes
Changes applied, results exported for the runbook.
Graph API Call — Token to Throttle-Safe Script. 1. App registered + consented: Application or delegated permission approved (least privilege). — 2. Token acquired: MSAL fetches access token (cached, refreshed silently). — 3. Call Graph endpoint: HTTPS GET/POST/PATCH with $filter, $select, $top. — 4. Page through results: @odata.nextLink loop for large datasets. — 5. Respect throttling: 429 + Retry-After honored — backoff, never hammer. — 6. Act + log outcomes: Changes applied, results exported for the runbook.
Prerequisites
PowerShell 7.x (recommended) or Windows PowerShell 5.1 with the Microsoft Graph SDK installed.
An account with the appropriate Intune role: Intune Administrator for actions, Intune Reader or read-only built-ins for reporting.
Consent for the exact Microsoft Graph scopes your script needs — no more.
For unattended runs: an Entra app registration with a certificate, or a managed identity — not a client secret pasted into a script.
PowerShell
1
2
3
4
5
6
# One-time module install (user scope)
Install-Module Microsoft.Graph -Scope CurrentUser -AllowClobber# Expected: NuGet provider prompt, then module download progress
Connect-MgGraph -Scopes"DeviceManagementManagedDevices.Read.All"# Expected: "Welcome to Microsoft Graph!" plus the account and tenant
How Intune Automation Works Over Graph
Intune's entire surface is a Graph resource tree rooted at /deviceManagement: managed devices, configuration policies, compliance policies, apps, update rings. The SDK cmdlets are thin wrappers over REST — Get-MgDeviceManagementManagedDevice is a GET to /v1.0/deviceManagement/managedDevices. Two practical consequences: collections page at roughly 100 objects per call (use -All to iterate), and anything not exposed as a cmdlet is still reachable with Invoke-MgGraphRequest against the raw URI, including beta-only endpoints. The SDK defaults to v1.0 endpoints; call beta explicitly when a feature lives there, and remember beta shapes can change — pin critical automation to v1.0 whenever the property exists.
Step-by-Step: Your First Session
Connect with only the scopes the script needs (below).
Verify context — the connected account, tenant, and scopes actually granted.
Pull one page first, eyeball the properties, then run with -All.
Export to CSV or JSON before you act on anything.
For actions (retire, wipe, sync), filter to exact devices and confirm counts before the loop.
PowerShell
1
2
3
Connect-MgGraph -Scopes"DeviceManagementManagedDevices.Read.All","DeviceManagementConfiguration.Read.All"
Get-MgContext | Select-Object Account, TenantId, Scopes
# Expected: Account, TenantId, and the consented scope list
The Core Scripts
1. Full device inventory export
PowerShell
1
2
3
4
5
Get-MgDeviceManagementManagedDevice -All |
Select-Object DeviceName, OperatingSystem, OsVersion, UserPrincipalName,
ComplianceState, LastSyncDateTime, EnrolledDateTime |
Export-Csv .\intune-device-inventory.csv -NoTypeInformation# Expected: one row per device; a 4,000-device tenant takes a few minutes
2. Non-compliant devices with grace-period expiry
PowerShell
1
2
3
4
5
Get-MgDeviceManagementManagedDevice -All -Filter"complianceState eq 'noncompliant'" |
Select-Object DeviceName, UserPrincipalName, OsVersion, ComplianceGracePeriodExpirationDateTime |
Sort-Object ComplianceGracePeriodExpirationDateTime |
Format-Table -AutoSize# Expected: devices ordered by who loses access next
3. Bulk retire devices stale for 90+ days
PowerShell
1
2
3
4
5
6
7
8
$cutoff = (Get-Date).AddDays(-90)
$stale = Get-MgDeviceManagementManagedDevice -All |
Where-Object { $_.LastSyncDateTime -lt$cutoff }
"Stale devices found: $($stale.Count)" # review BEFORE continuingforeach ($din$stale) {
Invoke-MgGraphRequest -Method POST -Uri"https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$($d.Id)/retire"
}
# Expected: 204 No Content per call; company data removed, personal devices keep user data
4. Devices with no primary user
PowerShell
1
2
3
4
Get-MgDeviceManagementManagedDevice -All -Filter"userPrincipalName eq null" |
Select-Object DeviceName, OperatingSystem, EnrolledDateTime |
Format-Table -AutoSize# Expected: shared/kiosk devices and forgotten test hardware
5. OS version distribution
PowerShell
1
2
3
4
5
Get-MgDeviceManagementManagedDevice -All -Filter"operatingSystem eq 'Windows'" |
Group-Object OsVersion |
Sort-Object Count -Descending |
Select-Object Count, Name
# Expected: Count/Name table — the raw material for your Windows 11 migration plan
$dev = Get-MgDeviceManagementManagedDevice -Filter"deviceName eq 'LAPTOP-4F12'"
Invoke-MgGraphRequest -Method POST -Uri"https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$($dev.Id)/syncNow"# Expected: 204 No Content; device checks in within a minute or two
8. Audit configuration policies by platform
PowerShell
1
2
3
4
Get-MgDeviceManagementDeviceConfiguration -All |
Select-Object DisplayName, '@odata.type', LastModifiedDateTime |
Sort-Object LastModifiedDateTime -Descending# Expected: every classic profile, newest first — duplicates stand out immediately
Configuration Deep Dive: Auth and Least Privilege
Interactive scripts should request only the scopes they use: reporting needs DeviceManagementManagedDevices.Read.All; wiping a device needs the privileged operations scope, which is exactly why it should be rare, PIM-elevated, and logged. For scheduled automation, register an Entra app, upload a certificate, and connect with Connect-MgGraph -ClientId ... -TenantId ... -CertificateThumbprint .... Keep the app's scope list minimal, name it for the runbook that owns it, and review its consent annually. Anything that destroys data (wipe, retire) deserves a dedicated app identity so audit logs tell you which runbook acted.
Real-World Enterprise Scenario
A 6,000-device manufacturer needed a monthly "compliance drift" email: count of non-compliant devices by policy, plus devices within 7 days of grace-period expiry. The whole solution was one scheduled script: pull non-compliant devices (script 2), group by UserPrincipalName for ownership, join policy failures from the compliance-policy device-status endpoint, and export to CSV where Power Automate turns it into an email. What used to be a morning of console clicking became 40 lines and a Task Scheduler entry — and the team found that 60 percent of drift came from a single outdated certificate profile.
Visual Walkthrough
Every reliable detection script follows this exact structure — copy it.
Decision TreeDetection Script Anatomy — The Exit-Code Contract5/5
Decision gate
Is the state healthy?
One question, one exit code — keep detection dumb and honest.
Healthy → exit 0
1
Test the condition
Single cmdlet or property read; no side effects, no output noise.
2
Exit 0, no output
Silence is the success contract — Intune treats it as compliant.
Unhealthy → exit 1
1
Emit one evidence line
Write the observed value — your future self reads the report.
2
Exit 1
Signals "needs remediation"; the paired fix script may now run.
3
Remediate idempotently
Fix script is safe to run repeatedly; no reboots without consent.
4
Exit 0 after fix
Next detection run proves remediation worked.
Detection Script Anatomy — The Exit-Code Contract. Gate: Is the state healthy? — Healthy → exit 0: Test the condition — Healthy → exit 0: Exit 0, no output — Unhealthy → exit 1: Emit one evidence line — Unhealthy → exit 1: Exit 1 — Unhealthy → exit 1: Remediate idempotently — Unhealthy → exit 1: Exit 0 after fix
Troubleshooting Matrix
Symptom
Cause
Fix
403 Authorization_RequestDenied
Scope missing or not consented
Reconnect with the needed scope; consent as admin
Results silently truncated
Forgot -All; got one page
Add -All or loop paging manually
429 Too Many Requests
Loop hammering Graph
Add Start-Sleep backoff; honor Retry-After; use server-side filters
400 invalid filter
Filtering a non-filterable property
Filter client-side, or use -ConsistencyLevel eventual with advanced queries
CmdletNotFoundException
Module missing or outdated
Update-Module Microsoft.Graph
Empty CSV
Property name typo in Select-Object
Dump one raw object first and inspect real property names
Common Errors
Authorization_RequestDenied — the token works but lacks the scope; the error's message even names the missing one. Re-consent, do not widen roles.
429 throttling — batch loops must expect it; a polite script sleeps and retries with the Retry-After header, not a hard-coded constant.
Advanced query not supported — some filters need the eventual-consistency headers; add -ConsistencyLevel eventual or move the filter client-side.
Null property exports — Graph returns sparse objects; guard with $_.Property ?? 'n/a' style defaults in PowerShell 7.
Best Practices
Filter server-side with -Filter and -All; never pull the tenant into memory to Where-Object it.
Always export a report before any bulk action, and print counts before destructive loops.
Wrap action loops in try/catch and log Graph error details per device, so one failure does not abort a 500-device run.
Test bulk actions against one pilot device by name before the loop.
Pin scripts to v1.0 endpoints; use beta deliberately and re-verify after SDK updates.
Security Considerations
Retire and wipe are privileged operations — request DeviceManagementManagedDevices.PrivilegedOperations.All only where needed and elevate it through PIM.
Never embed client secrets in scripts or pipelines; use certificates or managed identities, and rotate certificates on a schedule.
Device exports contain usernames, serials, and compliance state — treat the CSVs as confidential data and store them accordingly.
Log every admin-initiated device action with timestamp and operator; Graph audit logs plus your runbook log are your forensics pair.
FAQ
Do I need Global Admin or full Graph scopes?
No. Intune Administrator plus the specific resource scopes covers almost everything here; report scripts can run with read-only roles.
Why do results differ between Get-Mg... and the portal?
The portal frequently reads beta endpoints, while the SDK defaults to v1.0. Check the beta resource when a property looks missing.
How do I handle 429s in bulk loops?
Honor the Retry-After header, add jittered backoff, and reduce page pressure with server-side filters.
Can these run unattended?
Yes — with certificate-based app-only auth. Never interactive credentials in a scheduler.
Graph or the Intune console for day-to-day work?
Console for one-off inspection; scripts for anything repeated, audited, or scheduled.
Conclusion
The Graph PowerShell SDK turns Intune administration from clicking into engineering: repeatable reports, audited actions, and automation that scales with the fleet instead of your patience. Start with the inventory and compliance scripts, adopt the least-privilege connection pattern on day one, and build the bulk-action habits — export, count, confirm — that keep one bad loop from becoming a very loud afternoon.
Comments
0No comments yet — be the first to share your thoughts.