Browse all topics
Microsoft 365 essentials

Microsoft 365 reporting via PowerShell

PowerShell patterns for extracting reporting data from Microsoft 365 — licensing, usage, security posture, mailbox stats.

Most Microsoft 365 reporting questions can be answered with PowerShell — Microsoft Graph PowerShell SDK plus the service-specific modules (Exchange Online, Teams, SharePoint Online, Microsoft 365 admin). Knowing the right patterns saves hours of manual admin centre clicking.

The modules you'll use

  • Microsoft.Graph — the modern unified module. Most user, group, and licensing data.
  • ExchangeOnlineManagement — mailbox stats, mail flow, transport rules, EOP / Defender for Office.
  • MicrosoftTeams — Teams-specific configuration and policies.
  • PnP.PowerShell — SharePoint deep work.
  • AzureAD (deprecated) — being replaced by Microsoft.Graph. Avoid for new scripts.

Install as needed; Microsoft.Graph has sub-modules so you don't have to install the whole thing.

Common reporting patterns

Licence assignments

Connect-MgGraph -Scopes "User.Read.All,Organization.Read.All"

# Active licences in the tenant
Get-MgSubscribedSku | 
  Select-Object SkuPartNumber, PrepaidUnits, ConsumedUnits

# Users with a specific licence
$skuId = (Get-MgSubscribedSku -Filter "SkuPartNumber eq 'SPE_E5'").SkuId
Get-MgUser -All -Filter "assignedLicenses/any(x:x/skuId eq $skuId)" |
  Select-Object DisplayName, UserPrincipalName

Inactive users (no sign-in in 90 days)

$cutoff = (Get-Date).AddDays(-90).ToString("o")
Get-MgUser -All -Property UserPrincipalName,SignInActivity |
  Where-Object { $_.SignInActivity.LastSignInDateTime -lt $cutoff } |
  Select-Object UserPrincipalName, @{N="LastSignIn";E={$_.SignInActivity.LastSignInDateTime}}

Useful for licence reclamation — inactive licensed users cost money.

Mailboxes over quota

Connect-ExchangeOnline
Get-EXOMailbox -ResultSize Unlimited |
  Get-EXOMailboxStatistics |
  Where-Object { $_.TotalItemSize -gt 49GB } |
  Select-Object DisplayName, TotalItemSize

Identify mailboxes approaching the 50 GB limit (or 100 GB depending on plan) so you can enable archive mailboxes before users hit the wall.

Teams meeting recording locations

For tenants that need to audit where meeting recordings live:

# Channel meeting recordings - in the team's SharePoint site
# Non-channel meeting recordings - in the organiser's OneDrive
# Query via Graph for files matching meeting-recording pattern

SharePoint site storage by site

Connect-SPOService -Url https://yourtenant-admin.sharepoint.com
Get-SPOSite -Limit All |
  Sort-Object StorageUsageCurrent -Descending |
  Select-Object Url, @{N="StorageMB";E={$_.StorageUsageCurrent}}, Owner |
  Export-Csv site-storage.csv -NoTypeInformation

Useful for identifying which sites are consuming the tenant's SharePoint storage pool.

Conditional Access policy assignments

Connect-MgGraph -Scopes "Policy.Read.All"
Get-MgIdentityConditionalAccessPolicy |
  Select-Object DisplayName, State, 
    @{N="UsersIncluded";E={($_.Conditions.Users.IncludeUsers + $_.Conditions.Users.IncludeGroups) -join ","}},
    @{N="UsersExcluded";E={($_.Conditions.Users.ExcludeUsers + $_.Conditions.Users.ExcludeGroups) -join ","}},
    @{N="Apps";E={$_.Conditions.Applications.IncludeApplications -join ","}}

Inventory all CA policies in one report.

Defender for Endpoint device inventory

Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All"
Get-MgDeviceManagementManagedDevice -All |
  Select-Object DeviceName, OperatingSystem, ComplianceState, LastSyncDateTime

Devices and their compliance state.

Authentication patterns for production scripts

For one-off scripts, interactive sign-in works:

Connect-MgGraph -Scopes "User.Read.All"

For scheduled scripts, use certificate-based authentication:

Connect-MgGraph -ClientId "app-id" -TenantId "tenant-id" -CertificateThumbprint "thumbprint"

This requires an Entra ID app registration with the right Graph permissions and a certificate uploaded. Better than password-based auth for unattended scripts.

For Azure-hosted automation, managed identities are ideal — no credentials to manage.

Output destinations

Reports are most useful when delivered automatically:

  • Email to relevant stakeholders.
  • SharePoint list for ongoing dashboards.
  • Power BI for richer visualisation.
  • Teams channel for visibility to the team.
  • Slack / external chat for cross-platform distribution.

A simple pattern: scheduled Azure Function or Azure Automation runbook executes the PowerShell, writes results to a SharePoint list, Power BI refreshes from the list.

For organisations with ongoing Microsoft 365 reporting needs, this kind of structured PowerShell reporting is dramatically more efficient than manual admin centre work. Build a library; share with the team.