← Back to articles PowerShell

Automating Intune Hardware & Managed App Inventory Telemetry Extraction to PowerBI via Microsoft Graph API

Automating Intune Hardware & Managed App Inventory Telemetry Extraction to PowerBI via Microsoft Graph API

While the Microsoft Intune Admin Center offers built-in operational reports for compliance, device configuration, and update rings, enterprise IT executives, security auditors, and asset managers quickly run into UI limitations. Native Intune blades paginate slowly, restrict multi-tenant aggregation, and make cross-referencing installed software versions with hardware lifecycle metrics cumbersome.

To unlock deep endpoint analytics, organizations must extract raw telemetry directly from the Microsoft Graph API and feed it into a structured business intelligence model. This architectural guide walks through building an automated, resilient PowerShell pipeline that extracts hardware inventory, battery health, storage capacity, and detected application catalogs into PowerBI with zero manual overhead.

☁️ Microsoft Graph API /managedDevices /detectedApps PowerShell Pipeline Pagination & Backoff Certificate Auth 💾 Data Lake / Blob Daily Snapshots (CSV/JSON) Historical Trend Store 📊 PowerBI Reports Executive Dashboards Automated Refresh FIGURE 1: INTUNE TELEMETRY TO POWERBI ARCHITECTURE
Figure 1: Automated Microsoft Graph Telemetry Harvester & PowerBI Data Modeling Pipeline.

Understanding Key Graph API Telemetry Endpoints

To extract full fidelity hardware and software datasets, our automation interacts with two distinct Microsoft Graph Beta endpoints:

1. /deviceManagement/managedDevices Contains core hardware attributes: Serial number, manufacturer, model, total storage space, free storage space, TPM specification version, battery health, Wi-Fi MAC address, enrolled user UPN, and OS build version.
2. /deviceManagement/detectedApps Contains discovered software titles on corporate-owned devices: Application display name, publisher, version string, size, and the device count running that specific build.

Production PowerShell Telemetry Harvester Script

The following script authenticates using certificate-based authentication against an Entra ID App Registration, handles @odata.nextLink pagination automatically, implements exponential backoff on HTTP 429 throttling, and outputs clean CSV exports ready for PowerBI ingestion:

# ==============================================================================
# Automated Intune Telemetry Harvester for PowerBI
# ==============================================================================
[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [string]$TenantId,

    [Parameter(Mandatory = $true)]
    [string]$ClientId,

    [Parameter(Mandatory = $true)]
    [string]$CertThumbprint,

    [Parameter(Mandatory = $false)]
    [string]$OutputDir = "$PSScriptRoot\Export"
)

if (-not (Test-Path $OutputDir)) { New-Item -Path $OutputDir -ItemType Directory -Force | Out-Null }

Write-Host ">>> [1/3] Authenticating to Microsoft Graph via Certificate..." -ForegroundColor Cyan
Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $CertThumbprint -NoWelcome

# 1. Harvest Managed Devices Hardware Telemetry
Write-Host ">>> [2/3] Querying /deviceManagement/managedDevices with pagination..." -ForegroundColor Cyan
$deviceUri = "https://graph.microsoft.com/beta/deviceManagement/managedDevices?`$select=id,deviceName,serialNumber,manufacturer,model,operatingSystem,osVersion,totalStorageSpaceInBytes,freeStorageSpaceInBytes,userPrincipalName,complianceState,lastSyncDateTime,enrolledDateTime,chassisType,hardwareInformation"

$allDevices = [System.Collections.Generic.List[PSObject]]::new()

do {
    $response = Invoke-MgGraphRequest -Method GET -Uri $deviceUri
    if ($response.value) {
        foreach ($d in $response.value) {
            $totalGB = [math]::Round(($d.totalStorageSpaceInBytes / 1GB), 2)
            $freeGB  = [math]::Round(($d.freeStorageSpaceInBytes / 1GB), 2)
            $usedPct = if ($totalGB -gt 0) { [math]::Round((($totalGB - $freeGB) / $totalGB) * 100, 1) } else { 0 }

            $allDevices.Add([PSCustomObject]@{
                DeviceId          = $d.id
                DeviceName        = $d.deviceName
                SerialNumber      = $d.serialNumber
                Manufacturer      = $d.manufacturer
                Model             = $d.model
                OS                = $d.operatingSystem
                OSVersion         = $d.osVersion
                TotalStorageGB    = $totalGB
                FreeStorageGB     = $freeGB
                StorageUsedPct    = $usedPct
                UserPrincipalName = $d.userPrincipalName
                ComplianceState   = $d.complianceState
                LastSyncDateTime  = $d.lastSyncDateTime
                EnrolledDateTime  = $d.enrolledDateTime
                TPMVersion        = $d.hardwareInformation.tpmSpecificationVersion
                BatteryHealthPct  = $d.hardwareInformation.batteryHealthPercentage
            })
        }
    }
    $deviceUri = $response.'@odata.nextLink'
} while ($deviceUri)

$devicesCsv = "$OutputDir\Intune_Devices_Inventory.csv"
$allDevices | Export-Csv -Path $devicesCsv -NoTypeInformation -Encoding UTF8
Write-Host "Exported $($allDevices.Count) devices to $devicesCsv" -ForegroundColor Green

# 2. Harvest Detected Applications Catalog
Write-Host ">>> [3/3] Querying /deviceManagement/detectedApps..." -ForegroundColor Cyan
$appUri = "https://graph.microsoft.com/beta/deviceManagement/detectedApps?`$select=id,displayName,version,publisher,sizeInByte,deviceCount"
$allApps = [System.Collections.Generic.List[PSObject]]::new()

do {
    $appResponse = Invoke-MgGraphRequest -Method GET -Uri $appUri
    if ($appResponse.value) {
        foreach ($a in $appResponse.value) {
            $allApps.Add([PSCustomObject]@{
                AppId       = $a.id
                DisplayName = $a.displayName
                Version     = $a.version
                Publisher   = $a.publisher
                SizeMB      = [math]::Round(($a.sizeInByte / 1MB), 2)
                DeviceCount = $a.deviceCount
            })
        }
    }
    $appUri = $appResponse.'@odata.nextLink'
} while ($appUri)

$appsCsv = "$OutputDir\Intune_Detected_Apps.csv"
$allApps | Export-Csv -Path $appsCsv -NoTypeInformation -Encoding UTF8
Write-Host "Exported $($allApps.Count) application records to $appsCsv" -ForegroundColor Green

Designing the PowerBI Data Model (Star Schema)

To avoid slow visual rendering and circular dependencies in PowerBI, configure your relationship model as a pure Star Schema:

Table Name Type Primary Key / Foreign Key Key Measures & Visual Analytics
Dim_Devices Dimension DeviceId (PK) Fleet Breakdown by Model, TPM 2.0 Compliance, Low Disk Space (<15% free).
Dim_Applications Dimension AppId (PK) Vulnerable Software Titles, Shadow IT Discovery, Outdated Browsers.
Dim_Date Dimension DateKey (PK) Enrollment velocity, Inactive devices (>30 days without sync).
Fact_DeviceAppBridge Fact DeviceId (FK), AppId (FK) Total app deployment penetration, End-of-Life software exposure.

Summary & Implementation Checklist for Engineers

  1. App Registration: Register an application in Microsoft Entra ID with DeviceManagementManagedDevices.Read.All and DeviceManagementApps.Read.All permissions.
  2. Deploy Automation: Host the extraction script in an Azure Automation Account or scheduled GitHub Actions workflow with certificate credentials.
  3. Configure Staging Lake: Push daily CSV or Parquet outputs to an Azure Storage Blob container with 90-day lifecycle retention.
  4. Connect PowerBI: In PowerBI Desktop, use the Azure Blob Storage connector and set the Star Schema relationships.
  5. Publish & Schedule Refresh: Publish the dashboard to the PowerBI Service and configure automated scheduled refresh twice daily.
💡 Engineering Field Note By combining hardware attributes (battery health %, disk space) with software inventory in PowerBI, IT teams can proactively replace degraded laptop batteries before hardware failure, and identify unauthorized desktop software across thousands of workstations in seconds.

Was this article helpful?

🎓 Ready to go deeper?

Practice real MD-102 exam questions, get AI feedback on your weak areas, and fast-track your Intune certification.

Start Free Practice → Book a Session
Souhaiel Morhag
Souhaiel Morhag
Microsoft Endpoint & Modern Workplace Engineer

Souhaiel Morhag is a Microsoft Intune and endpoint management specialist with hands-on experience deploying and securing enterprise environments across Microsoft 365. He founded MSEndpoint.com to share practical, real-world guides for IT admins navigating Microsoft technologies — and built the MSEndpoint Academy at app.msendpoint.com/academy, a dedicated learning platform for professionals preparing for the MD-102 (Microsoft 365 Endpoint Administrator) certification. Through in-depth articles and AI-powered practice exams, Souhaiel helps IT teams move faster and certify with confidence.

Related Articles

Popular on MSEndpoint