Building a Microsoft 365 License Audit Report with PowerShell and Microsoft Graph

Introduction

Managing Microsoft 365 licensing at scale is not just about assigning licenses. It is also about visibility, governance, audit readiness, and operational control.

In many organizations, users may receive licenses in different ways:

  • Directly assigned to the user
  • Assigned through group-based licensing
  • Assigned through legacy processes
  • Assigned during migration or onboarding activities

Over time, this can make it difficult to answer a simple question:

Who has which Microsoft 365 license, and how was it assigned?

To make this easier, I created a PowerShell script called M365 License Audit Report.

The script uses Microsoft Graph PowerShell to collect user and license assignment information and exports the result into a timestamped CSV report. Microsoft Graph PowerShell supports retrieving users through Get-MgUser, including selected user properties when requested. Microsoft also documents Microsoft Graph PowerShell examples for group-based licensing scenarios, including license assignment and SKU lookup patterns.

Why I Built This

Microsoft 365 admin portals are useful, but they are not always ideal when you need repeatable reporting across a large tenant.

For example, IT and governance teams often need to know:

  • Which users have E3, E5, F3, or other configured licenses?
  • Are those licenses assigned directly or through a group?
  • Are contractors and employees licensed correctly?
  • Are direct license assignments being used where group-based licensing should be preferred?
  • Can we produce CSV evidence for audit or compliance reviews?
  • Are license assignments consistent after migration or cleanup work?

Checking this manually user by user is slow and hard to repeat.

This script helps convert license visibility into a repeatable report.

What the Script Does

The script:

  • Connects to Microsoft Graph interactively
  • Retrieves Microsoft 365 user details
  • Checks configured license groups
  • Identifies whether a user has those configured licenses
  • Shows whether the assignment is direct or group-based
  • Optionally resolves group names
  • Exports the output to a CSV file
  • Shows progress and estimated time while running

The output file is saved under the reports folder using this format:

m365_license_audit_report_yyyyMMdd_HHmmss.csv

Configuration

The script includes a simple $CONFIG block.

This allows the report to be customized without changing the core logic.

$CONFIG = @{
    EmployeeTypes      = @("Contractor","Employee")

    LicenseGroups = [ordered]@{
        F3 = @("SPE_F1", "M365_F1")
        E3 = @("SPE_E3", "ENTERPRISEPACK")
        E5 = @("SPE_E5", "ENTERPRISEPREMIUM")
    }

    IncludeAllUsers    = $true
    ResolveGroupNames  = $false
    OutputFolder       = Join-Path $PSScriptRoot "reports"
}

License Groups Are Fully Customizable

Although the example configuration includes F3, E3, and E5, the script is not limited to those licenses.

You can update the LicenseGroups section to track any license SKU that matters to your environment.

Example:

LicenseGroups = [ordered]@{
    F3 = @("SPE_F1", "M365_F1")
    E3 = @("SPE_E3", "ENTERPRISEPACK")
    E5 = @("SPE_E5", "ENTERPRISEPREMIUM")
}

The friendly names such as F3, E3, and E5 are just labels.

Each label can map to one or more SKU part numbers.

This is useful because different tenants may have different licensing combinations, product bundles, or historical SKU names.

Key Output Columns

The CSV report includes the following fields:

ObjectId
UserPrincipalName
DisplayName
Mail
EmployeeType
Department
JobTitle
UsageLocation
AccountEnabled
HasConfiguredLicense
LicenseSummary
LicenseAssignmentPaths

These columns are useful for operational review, audit evidence, and license governance.

For example:

  • HasConfiguredLicense shows whether the user has any license from the configured license groups.
  • LicenseSummary shows which configured license was found.
  • LicenseAssignmentPaths shows whether the license was assigned directly or through a group.

Why Assignment Path Matters

Knowing that a user has a license is useful. But knowing how the license was assigned is even more important. For large environments, group-based licensing is usually easier to govern than direct assignment. Microsoft documents group-based licensing as a way to assign one or more product licenses to a group so that members of that group receive those licenses.

This report helps identify:

  • Users with direct license assignments
  • Users licensed through groups
  • Users with unexpected assignment paths
  • Licensing drift after onboarding, migration, or cleanup activities
  • Assignment inconsistencies across employee types

This visibility helps teams move from assumptions to evidence.

Example Use Cases

1. Monthly License Audit

Generate a monthly CSV report to validate who has configured Microsoft 365 licenses and how they were assigned. This can support recurring governance and operational reviews.

2. Direct Assignment Cleanup

If your organization prefers group-based licensing, this report can help identify users who still have direct license assignments. These users can then be reviewed and cleaned up where appropriate.

3. Contractor and Employee License Review

The script can use EmployeeType filtering when IncludeAllUsers is set to $false. This helps organizations review license allocation across employees, contractors, students, or other worker classifications.

4. Migration Validation

If users are moved from one license type to another, such as F3 to E3, this report can help validate the final state. It gives teams a timestamped CSV record of the license position after migration.

5. Audit and Compliance Evidence

The report can be used as supporting evidence for internal reviews. It helps show:

  • Which users were included
  • Which configured licenses were detected
  • Whether accounts were enabled
  • What employee type was recorded
  • Whether the license came from a direct or group-based assignment

Performance Considerations

The script includes a setting called:

ResolveGroupNames = $false

When this is set to $false, the report runs faster because it does not perform extra lookups to resolve group display names.

When set to $true, the report becomes easier to read because group IDs are resolved to group names, but this may take longer in large tenants. For large environments, I recommend starting with $false. Then enable group name resolution only when needed.

Required Permissions

The script connects to Microsoft Graph using the following scopes:

User.Read.All
Directory.Read.All

These permissions are needed to read user and directory license information. Depending on your tenant settings, admin consent may be required.

How to Run the Script

From the repository root, run:

pwsh .\M365LicenseAuditReport.ps1

Or with Windows PowerShell:

powershell -ExecutionPolicy Bypass -File .\M365LicenseAuditReport.ps1

The script will prompt for interactive Microsoft Graph sign-in. After completion, the CSV file will be created in the reports folder.

Security Guidance

The exported CSV may contain sensitive identity and licensing information.

Recommended practices:

  • Store the CSV in an approved location
  • Limit access to the report folder
  • Do not share externally without review
  • Remove or mask user data if needed
  • Follow your organization’s data retention policies

License reports may look operational, but they can still contain personal and organizational data.

Final Thoughts

The M365 License Audit Report is a practical PowerShell tool for improving Microsoft 365 license visibility.

It helps teams understand:

  • Who has configured licenses
  • Which license groups are present
  • Whether licenses are assigned directly or through groups
  • Where cleanup may be needed
  • How to produce repeatable audit evidence

This is not intended to replace a full license management platform. Instead, it provides a lightweight and flexible way to generate useful license assignment evidence using PowerShell and Microsoft Graph.

If you manage Microsoft 365 licensing at scale, especially in an environment using group-based licensing, this type of report can save time, improve governance, and support better audit readiness.

You can find the script here:

PowerShell-Automation/Licensing/M365LicenseAuditReport.ps1 at main · emadcodes/PowerShell-Automation
Contribute to emadcodes/PowerShell-Automation development by creating an account on GitHub.

Subscribe to Emad Afaq Khan

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe