Getting compliance data from Intune web interface can be a pain in the ass.
Post from Ola Strom about getting compliance data from Intune using Graph API inspired me to create PowerShell functions for such task.
TL;DR
We will create App registration in Azure, grant it correct permissions, and use it for authentication when calling PowerShell functions New-IntuneAuthHeader, Get-IntuneOverallComplianceStatus and Get-IntuneDeviceComplianceStatus.
Table of Contents
Prerequisite
To programmatically access Intune API (Graph API), you have to create App Registration with correct permissions in your Azure first. I've used this nice tutorial to learn how to do it.
In a nutshell, you have to:
- Create App Registration
- Head to https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps >
New registration > Choose App name and click Register
- Add permission to this App
- Open your newly created App >
API permissions > Add a permission > Add following Application permissions

- Don't forget to
Grant admin consent
- Generate App Secret
- Again in you App settings open
Certificates & secrets > New client secret
Choose validity period and some meaningful description. Don't forget to safely store generated password! We will need it later for requests authentication.
Function New-IntuneAuthHeader is prerequisite for any of my following functions. It is used to create an authentication header that is then used to authenticate every REST api request. It requires your tenant name (contoso.onmicrosoft.com), App ID and App Secret (password created earlier) to generate such header.
Application ID can be found in Overview section of our App
TenantDomainName can be found in Overview section of your Azure Active Directory as Primary domain.
Function definition
function New-IntuneAuthHeader {
<
.SYNOPSIS
Function for generating header that can be used for authentication of Graph API requests.
.DESCRIPTION
Function for generating header that can be used for authentication of Graph API requests.
.PARAMETER credential
Credentials for Graph API authentication (AppID + AppSecret).
.PARAMETER TenantDomainName
Name of your Azure tenant.
<yourtenantdomain>.onmicrosoft.com
.EXAMPLE
$header = New-IntuneAuthHeader -credential $cred
$URI = 'https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/'
$managedDevices = (Invoke-RestMethod -Headers $header -Uri $URI -Method Get).value
.NOTES
https://adamtheautomator.com/powershell-graph-api/
https://thesleepyadmins.com/2020/10/24/connecting-to-microsoft-graphapi-using-powershell/
https://github.com/microsoftgraph/powershell-intune-samples
[CmdletBinding()]
[Alias("Get-IntuneAuthHeader")]
param (
[System.Management.Automation.PSCredential] $credential = (Get-Credential -Message "Enter AppID as UserName and AppSecret as Password"),
[ValidateNotNullOrEmpty()]
$tenantDomainName
)
if (!$credential) { throw "Credential is missing" }
if (!$tenantDomainName) { throw "TenantDomainName is missing" }
Write-Verbose "Getting token"
$body = @{
Grant_Type = "client_credentials"
Scope = "https://graph.microsoft.com/.default"
Client_Id = $credential.username
Client_Secret = $credential.GetNetworkCredential().password
}
$connectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantDomainName/oauth2/v2.0/token" -Method POST -Body $body
$token = $connectGraph.access_token
if ($token) {
return @{ Authorization = "Bearer $($token)" }
} else {
throw "Unable to obtain token"
}
}
Function for getting overall compliance data
Function Get-IntuneOverallComplianceStatus can be used to get compliance data for your whole environment.
This means it will return you a combination of these three Intune pages as one object.
Overall Compliance status:
Specific Compliance policy setting:
Problematic device compliance:

Function definition
function Get-IntuneOverallComplianceStatus {
<
.SYNOPSIS
Function for getting overall device compliance status from Intune.
.DESCRIPTION
Function for getting overall device compliance status from Intune.
.PARAMETER header
Authentication header.
Can be created via New-IntuneAuthHeader.
.PARAMETER justProblematic
Switch for outputting only non-compliant items.
.EXAMPLE
$header = New-IntuneAuthHeader
Get-IntuneOverallComplianceStatus -header $header
Will return compliance information for all devices in your Intune.
.EXAMPLE
$header = New-IntuneAuthHeader
Get-IntuneOverallComplianceStatus -header $header -justProblematic
Will return just non-compliant information for devices in your Intune.
[CmdletBinding()]
param (
[hashtable] $header
,
[switch] $justProblematic
)
if (!$header) {
$header = New-IntuneAuthHeader -ErrorAction Stop
}
$deviceComplianceData = @{}
$URI = 'https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicySettingStateSummaries'
$complianceSummary = (Invoke-RestMethod -Headers $header -Uri $URI -Method Get).value
$complianceSummary = $complianceSummary | select @{n = 'Name'; e = { ($_.settingName -split "\.")[-1] } }, nonCompliantDeviceCount, errorDeviceCount, conflictDeviceCount, id
if ($justProblematic) {
$complianceSummary = $complianceSummary | ? { $_.nonCompliantDeviceCount -or $_.errorDeviceCount -or $_.conflictDeviceCount }
}
if ($complianceSummary) {
$complianceSummary | % {
$complianceSettingId = $_.id
Write-Verbose $complianceSettingId
Write-Warning "Processing $($_.name)"
switch ($_.name) {
'RequireRemainContact' { Write-Warning "`t- devices that haven't contacted Intune for last 30 days" }
'RequireDeviceCompliancePolicyAssigned' { Write-Warning "`t- devices without any compliance policy assigned" }
'ConfigurationManagerComplianceRequired' { Write-Warning "`t- devices that are not compliant in SCCM" }
}
$URI = "https://graph.microsoft.com/v1.0/deviceManagement/deviceCompliancePolicySettingStateSummaries/$complianceSettingId/deviceComplianceSettingStates?`$filter=NOT(state eq 'compliant')"
$complianceStatus = (Invoke-RestMethod -Headers $header -Uri $URI -Method Get).value
if ($justProblematic) {
$complianceStatus = $complianceStatus | ? { $_.state -ne "compliant" }
}
$deviceDetails = $complianceStatus | % {
$deviceId = $_.deviceId
$deviceName = $_.deviceName
$userPrincipalName = $_.userPrincipalName
Write-Verbose "Processing $deviceName with id: $deviceId and UPN: $userPrincipalName"
if (!($deviceComplianceData.$deviceName)) {
Write-Verbose "Getting compliance data for $deviceName"
$deviceComplianceData.$deviceName = Get-IntuneDeviceComplianceStatus -deviceId $deviceId -justProblematic -header $header
}
if ($deviceComplianceData.$deviceName) {
$errorDescription = $deviceComplianceData.$deviceName | ? { $_.setting -eq $complianceSettingId -and $_.userPrincipalName -eq $userPrincipalName -and $_.errorDescription -ne "No error code" } | select -ExpandProperty errorDescription
}
$_ | select deviceName, userPrincipalName, state, @{n = 'errDetails'; e = { $errorDescription } } | sort state, deviceName
}
[PSCustomObject]@{
Name = $_.name
NonCompliantDeviceCount = $_.nonCompliantDeviceCount
ErrorDeviceCount = $_.errorDeviceCount
ConflictDeviceCount = $_.conflictDeviceCount
DeviceDetails = $deviceDetails
}
}
}
}
Example output
By calling:
$header = New-IntuneAuthHeader
Get-IntuneOverallComplianceStatus -header $header -justProblematic
You will get:
Output is a classic object, so you can format or filter it as you wish...
Function for getting given device compliance data
Function Get-IntuneDeviceComplianceStatus can be used to get specific device(s) compliance data.
Function definition
function Get-IntuneDeviceComplianceStatus {
<
.SYNOPSIS
Function for getting device compliance status from Intune.
.DESCRIPTION
Function for getting device compliance status from Intune.
Devices can be selected by name or id. If omitted, all devices will be processed.
.PARAMETER deviceName
Name of device(s).
Can be combined with deviceId parameter.
.PARAMETER deviceId
Id(s) of device(s).
Can be combined with deviceName parameter.
.PARAMETER header
Authentication header.
Can be created via New-IntuneAuthHeader.
.PARAMETER justProblematic
Switch for outputting only non-compliant items.
.EXAMPLE
$header = New-IntuneAuthHeader
Get-IntuneDeviceComplianceStatus -header $header
Will return compliance information for all devices in your Intune.
.EXAMPLE
$header = New-IntuneAuthHeader
Get-IntuneDeviceComplianceStatus -header $header -deviceName PC-1, PC-2
Will return compliance information for PC-1, PC-2 from Intune.
[CmdletBinding()]
param (
[string[]] $deviceName,
[string[]] $deviceId,
[hashtable] $header,
[switch] $justProblematic
)
$ErrorActionPreference = "Stop"
if (!$header) {
$header = New-IntuneAuthHeader -ErrorAction Stop
}
if (!$deviceName -and !$deviceId) {
Write-Warning "You haven't specified device name or id, all devices will be processed"
$deviceId = (Invoke-RestMethod -Headers $header -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$select=id" -Method Get).value | select -ExpandProperty Id
} elseif ($deviceName) {
$deviceName | % {
$id = (Invoke-RestMethod -Headers $header -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$filter=deviceName eq '$_'" -Method Get).value | select -ExpandProperty Id
if ($id) {
Write-Verbose "$_ was translated to $id"
$deviceId += $id
} else {
Write-Warning "Device $_ wasn't found"
}
}
}
$deviceId = $deviceId | select -Unique
foreach ($devId in $deviceId) {
Write-Verbose "Processing device $devId"
$deviceCompliancePolicy = (Invoke-RestMethod -Headers $header -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$devId')/deviceCompliancePolicyStates" -Method Get).value
if ($deviceCompliancePolicy) {
$deviceCompliancePolicy | % {
$deviceComplianceId = $_.id
$deviceComplianceStatus = (Invoke-RestMethod -Headers $header -Uri "https://graph.microsoft.com/beta/deviceManagement/managedDevices('$devId')/deviceCompliancePolicyStates('$deviceComplianceId')/settingStates" -Method Get).value
if ($justProblematic) {
$deviceComplianceStatus = $deviceComplianceStatus | ? { $_.state -ne "compliant" }
}
$name = (Invoke-RestMethod -Headers $header -Uri "https://graph.microsoft.com/beta/deviceManagement/manageddevices('$devId')?`$select=deviceName" -Method Get).deviceName
$deviceComplianceStatus | select @{n = 'deviceName'; e = { $name } }, state, errorDescription, userPrincipalName , setting, sources
}
} else {
Write-Warning "There are no compliance policies for $devId device"
}
}
}
Example output
By calling:
$header = New-IntuneAuthHeader
Get-IntuneDeviceComplianceStatus -deviceName ni-20-ntb, ng-34-ntb -header $header -justProblematic
You will get:
Output is a classic object, so you can format or filter it as you wish...
Useful TIP
Don't forget that you can get Graph URI for your request just by opening Intune page in your browser and using Developer mode (F12)!

Useful links that inspired me
- https://adamtheautomator.com/powershell-graph-api/#AppIdSecret
- https://thesleepyadmins.com/2020/10/24/connecting-to-microsoft-graphapi-using-powershell/
- https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter
- https://github.com/microsoftgraph/powershell-intune-samples