All scripts
Microsoft 365 800

Microsoft 365 Apps Update Channel Report

Reports the Office update channel and version for every device with Microsoft 365 Apps installed, using the Microsoft Graph reports API, so you can spot devices stuck on the wrong channel or an old build before it turns into a support ticket.

Get-M365AppsUpdateChannelReport.ps1
<#
.SYNOPSIS
    Reports the Microsoft 365 Apps update channel and version in use across your tenant's devices.

.DESCRIPTION
    Calls the Microsoft Graph reports API to pull the Microsoft 365 Apps user detail report,
    which lists each user's device along with the Office update channel (Current Channel,
    Monthly Enterprise Channel, Semi-Annual Enterprise Channel, and so on) and the installed
    Office version. Useful for finding devices stuck on an old build or a channel your
    organization no longer wants to support, before it turns into a support ticket or a
    compatibility problem.

.AUTHOR
    Shehryar Hassan

.EXAMPLE
    .\Get-M365AppsUpdateChannelReport.ps1 -OutputPath "C:\Reports\M365AppsChannels.csv"

    Connects to Microsoft Graph, pulls the current report, and saves it to the given path.

.EXAMPLE
    .\Get-M365AppsUpdateChannelReport.ps1 -ShowOffChannel

    Runs the report and prints only devices that are not on the Current or Monthly Enterprise channel.
#>

[CmdletBinding()]
param(
    [string]$OutputPath = ".\M365AppsUpdateChannelReport.csv",
    [switch]$ShowOffChannel
)

$requiredModule = "Microsoft.Graph.Authentication"
if (-not (Get-Module -ListAvailable -Name $requiredModule)) {
    Write-Host "Installing $requiredModule..." -ForegroundColor Yellow
    Install-Module -Name $requiredModule -Scope CurrentUser -Force
}

Import-Module Microsoft.Graph.Authentication

Write-Host "Connecting to Microsoft Graph..." -ForegroundColor Cyan
Connect-MgGraph -Scopes "Reports.Read.All" -NoWelcome

$uri = "https://graph.microsoft.com/beta/deviceManagement/reports/getM365AppsUserDetail"
$body = @{
    select = @(
        "UserPrincipalName",
        "DeviceId",
        "Product",
        "Platform",
        "Channel",
        "Version",
        "LastActivateDate"
    )
} | ConvertTo-Json

Write-Host "Requesting Microsoft 365 Apps user detail report..." -ForegroundColor Cyan
Invoke-MgGraphRequest -Method POST -Uri $uri -Body $body -OutputFilePath $OutputPath

$rows = Import-Csv -Path $OutputPath

if ($ShowOffChannel) {
    $acceptedChannels = @("Current Channel", "Monthly Enterprise Channel")
    $offChannel = $rows | Where-Object { $_.Channel -and ($acceptedChannels -notcontains $_.Channel) }

    if ($offChannel) {
        Write-Host "`nDevices not on an accepted update channel:" -ForegroundColor Yellow
        $offChannel | Select-Object UserPrincipalName, DeviceId, Channel, Version, LastActivateDate | Format-Table -AutoSize
    } else {
        Write-Host "`nEvery device is on an accepted update channel." -ForegroundColor Green
    }
}

$channelSummary = $rows | Group-Object Channel | Select-Object Name, Count | Sort-Object Count -Descending

Write-Host "`nChannel breakdown across the tenant:" -ForegroundColor Cyan
$channelSummary | Format-Table -AutoSize

Write-Host "`nFull report saved to $OutputPath" -ForegroundColor Green

Disconnect-MgGraph | Out-Null

Read it before you run it, and test in a safe tenant first.