How I Automated Intune Compliance Reports Using PowerShell and Microsoft Graph
Every Monday morning our team needed a full compliance report from Intune. I got tired of doing it manually, so I automated the whole thing with PowerShell and Microsoft Graph API. Here is exactly how I did it.
Every Monday morning at 9am, my manager would ask the same question: "How many devices are non-compliant right now?"
For the first few weeks, I would log into the Intune admin centre, run a report, export it to Excel, clean it up, and email it across. It took about 25 minutes each time.
After doing this six times, I had enough. I automated the whole thing with PowerShell and Microsoft Graph API. Now it runs on a schedule, exports a clean report, and emails it automatically — without me touching anything.
This guide walks you through exactly how I built it.
What We Are Building
By the end of this guide, you will have a PowerShell script that:
- Connects to Microsoft Graph API using an app registration (no interactive login)
- Pulls all managed devices from Intune
- Filters for non-compliant devices
- Exports the results to a clean CSV file
- Can be scheduled via Windows Task Scheduler or Azure Automation
You need the Microsoft.Graph PowerShell module installed. If you have not installed it yet, run: Install-Module Microsoft.Graph -Scope CurrentUser
How the Script Works: The Big Picture
Before writing a single line of code, it helps to understand the flow of what we are doing.
Step 1: Create an App Registration in Entra ID
The script needs permission to read Intune data. We do this through an App Registration in Microsoft Entra ID (Azure AD). This gives us a Client ID and Client Secret — like a username and password for the script.
Go to Entra ID in the Azure Portal
Open portal.azure.com, search for Entra ID, and click App registrations in the left menu.
Create a new registration
Click New registration. Give it a name like "Intune-Compliance-Reporter". Leave the default settings and click Register.
Note down your IDs
After creation, copy the Application (client) ID and the Directory (tenant) ID from the Overview page. You will need both in the script.
Create a client secret
Go to Certificates and secrets → New client secret. Set an expiry (12 months is fine). Copy the secret value immediately — it is only shown once.
Add API permissions
Go to API permissions → Add a permission → Microsoft Graph → Application permissions. Search for and add: DeviceManagementManagedDevices.Read.All. Then click Grant admin consent.
Store your Client Secret somewhere secure — like Azure Key Vault or a password manager. Never hardcode it directly in a script that gets shared or committed to GitHub.
Step 2: The PowerShell Script
Here is the full script. I will break it down section by section below.
# ── Configuration ──────────────────────────────────────────────
$TenantId = "your-tenant-id-here"
$ClientId = "your-client-id-here"
$ClientSecret = "your-client-secret-here"
$OutputPath = "C:\Reports\IntuneComplianceReport.csv"
# ── Connect to Microsoft Graph ─────────────────────────────────
$SecureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($ClientId, $SecureSecret)
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $Credential
Write-Host "Connected to Microsoft Graph" -ForegroundColor Green
# ── Get all managed devices ────────────────────────────────────
Write-Host "Fetching devices from Intune..." -ForegroundColor Cyan
$AllDevices = Get-MgDeviceManagementManagedDevice -All -Property `
DeviceName, ComplianceState, UserPrincipalName, OperatingSystem,
OsVersion, LastSyncDateTime, EnrolledDateTime, Manufacturer, Model
Write-Host "Total devices found: $($AllDevices.Count)" -ForegroundColor Cyan
# ── Filter non-compliant devices ───────────────────────────────
$NonCompliant = $AllDevices | Where-Object {
$_.ComplianceState -eq "noncompliant"
}
Write-Host "Non-compliant devices: $($NonCompliant.Count)" -ForegroundColor Yellow
# ── Build the report ───────────────────────────────────────────
$Report = $NonCompliant | Select-Object @(
@{ Name = "Device Name"; Expression = { $_.DeviceName } },
@{ Name = "User"; Expression = { $_.UserPrincipalName } },
@{ Name = "OS"; Expression = { $_.OperatingSystem } },
@{ Name = "OS Version"; Expression = { $_.OsVersion } },
@{ Name = "Manufacturer"; Expression = { $_.Manufacturer } },
@{ Name = "Model"; Expression = { $_.Model } },
@{ Name = "Last Sync"; Expression = { $_.LastSyncDateTime } },
@{ Name = "Enrolled On"; Expression = { $_.EnrolledDateTime } },
@{ Name = "Compliance State"; Expression = { $_.ComplianceState } }
)
# ── Export to CSV ──────────────────────────────────────────────
$Report | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Report saved to: $OutputPath" -ForegroundColor Green
# ── Disconnect ─────────────────────────────────────────────────
Disconnect-MgGraphBreaking Down the Key Parts
Connecting Without a Pop-Up Login
The standard way to connect to Graph is Connect-MgGraph — but that opens a browser window for you to sign in. That does not work for scheduled scripts.
Instead, we use Client Secret credentials. The script authenticates silently using the App Registration we created in Step 1. No human interaction required.
$SecureSecret = ConvertTo-SecureString $ClientSecret -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($ClientId, $SecureSecret)
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $CredentialFetching Devices Efficiently
The -All flag on Get-MgDeviceManagementManagedDevice is important. Without it, Graph API only returns the first 1,000 devices. In a large organisation, this will silently give you incomplete data.
The -Property parameter tells Graph to return only the fields you need. This makes the API call faster and reduces the data transferred.
Filtering for Non-Compliant Devices
$NonCompliant = $AllDevices | Where-Object {
$_.ComplianceState -eq "noncompliant"
}The possible values for ComplianceState are:
| Value | Meaning |
|---|---|
| compliant | Device meets all compliance policies |
| noncompliant | Device fails one or more policies |
| inGracePeriod | Device is non-compliant but still in the grace period |
| unknown | Device has not yet been evaluated |
| notApplicable | No compliance policy assigned to this device |
You can add more filters — for example, to report only on Windows devices:
$NonCompliant = $AllDevices | Where-Object {
$_.ComplianceState -eq "noncompliant" -and
$_.OperatingSystem -eq "Windows"
}Step 3: Schedule It to Run Automatically
Option A — Windows Task Scheduler (simplest)
Create a .ps1 file with the script, then set up a scheduled task to run it:
# Run this once to create the scheduled task
$Action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-NonInteractive -File C:\Scripts\IntuneReport.ps1"
$Trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Monday -At 8am
Register-ScheduledTask -TaskName "IntuneComplianceReport" `
-Action $Action -Trigger $Trigger -RunLevel HighestOption B — Azure Automation (recommended for production)
Azure Automation is better for production because:
- It runs in the cloud — no dependency on a local machine being on
- Credentials are stored in secure Azure Key Vault
- You get run logs and failure alerts built in
Upload the script as a Runbook, store your credentials as Automation Variables, and set a schedule from the Automation Account.
For Azure Automation, use a Managed Identity instead of a Client Secret. Managed Identities do not expire, cannot be leaked, and require no credential management. Assign the DeviceManagementManagedDevices.Read.All permission to the Managed Identity in Entra ID.
What the Output Looks Like
The CSV report includes these columns for every non-compliant device:
| Device Name | User | OS | OS Version | Manufacturer | Model | Last Sync | Compliance State |
|---|---|---|---|---|---|---|---|
| LAPTOP-001 | john@company.com | Windows | 11.0.26100 | Dell | Latitude 5540 | 2026-05-06 | noncompliant |
| IPHONE-042 | sara@company.com | iOS | 17.4.1 | Apple | iPhone 14 | 2026-05-05 | noncompliant |
This file can go straight into an email, be opened in Excel, or be loaded into Power BI for a compliance dashboard.
Taking It Further
Once the basic script is working, here are some useful extensions:
- Email the report automatically — use
Send-MgUserMailfrom the Graph module to email the CSV to your team every Monday morning - Push data to Power BI — write the data to a Power BI streaming dataset for a live compliance dashboard
- Trigger remediation — if a device has been non-compliant for more than 7 days, automatically send a notification to the user via Teams using the Graph API
- Compare week over week — save each week's report and compare to see whether compliance is improving or getting worse
Summary
Automating Intune compliance reports with PowerShell and Microsoft Graph removes a repetitive manual task and gives your team accurate, consistent data every time.
The three things that made this work reliably:
- App registration with the right Graph permissions — no interactive login needed
- The
-Allflag — ensures you get every device, not just the first 1,000 - Scheduled execution — Task Scheduler or Azure Automation removes any human dependency
The script above is production-ready. Drop in your tenant ID, client ID, and client secret, and it will run straight away.
Written by
Chetan Yamger
Cloud Engineer · AI Automation Architect · Blogger
Cloud Engineer and AI Automation Architect with deep expertise in Azure, Intune, PowerShell, and AI-driven workflows. I use ChatGPT, Gemini, and prompt engineering to build intelligent automation that improves productivity and decision-making in real IT environments.
Stay in the loop.
New articles, straight to you.
Deep-dive technical articles on Intune, PowerShell, and AI — no noise, no spam.
Discussion
Share your thoughts — your email stays private
Leave a comment
