M365 License Usage Monitoring & Automated Threshold Alerting Script

1. Enterprise Scenario & Objectives

As enterprise organizations scale, tracking Microsoft 365 license availability across multiple SKUs (E5, E3, Business Premium, Power BI) becomes a constant operational challenge. Running out of available licenses delays critical employee onboarding, while hoarding unassigned seats leads to wasted annual budget.

This automated monitoring solution provides proactive license governance by:

  • Querying tenant-wide subscribed SKUs, total units, assigned seats, and available pools.
  • Calculating real-time capacity percentages against configured warning/critical thresholds.
  • Generating an automated HTML/CSV summary report.
  • Sending immediate automated email alerts via Exchange Online or Microsoft Graph when remaining licenses drop below threshold limits (e.g., less than 5% available seats).

2. Prerequisites & Required Modules

Install the official Microsoft Graph SDK module in PowerShell:

Install-Module -Name Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser -Force

3. Authentication & Required Permissions

To inspect tenant license inventory and dispatch automated alerting emails, ensure your administrative connection or Azure Managed Identity has the following permission scopes:

  • Graph API Scopes: Organization.Read.All, Directory.Read.All, Mail.Send
  • Azure Directory Role: Global Reader or License Administrator

4. The Complete Production Script

<#
.SYNOPSIS
    M365 License Usage Monitoring & Automated Threshold Alerting Script
.DESCRIPTION
    Scans Microsoft 365 tenant subscribed SKUs, checks available seat capacity against 
    a custom threshold percentage, and triggers email notifications for low-capacity pools.
.AUTHOR
    Innovation Guru (https://dsinnovationguru.com)
#>

param (
    [Parameter(Mandatory = $false)]
    [int]$ThresholdPercent = 10,

    [Parameter(Mandatory = $false)]
    [string]$AlertEmail = "admin@dsinnovationguru.com",

    [Parameter(Mandatory = $false)]
    [string]$ReportPath = "C:\Logs\M365_License_Usage_Report.csv"
)

$ErrorActionPreference = "Stop"

function Write-Log ($Message, $Color = "Green") {
    $TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    Write-Host "[$TimeStamp] $Message" -ForegroundColor $Color
}

try {
    Write-Log "Connecting to Microsoft Graph Directory Management..." "Cyan"
    Connect-MgGraph -Scopes "Organization.Read.All", "Directory.Read.All" | Out-Null

    Write-Log "Retrieving tenant license SKUs..."
    $SubscribedSkus = Get-MgSubscribedSku -All

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

    foreach ($Sku in $SubscribedSkus) {
        $SkuName = $Sku.SkuPartNumber
        $TotalUnits = $Sku.CapabilityStatus.ConsumedUnits + $Sku.PrepaidUnits.Enabled
        $ActiveConsumed = $Sku.ConsumedUnits
        $AvailableSeats = $Sku.PrepaidUnits.Enabled - $ActiveConsumed

        # Calculate percentage remaining
        if ($Sku.PrepaidUnits.Enabled -gt 0) {
            $PercentAvailable = [math]::Round(($AvailableSeats / $Sku.PrepaidUnits.Enabled) * 100, 2)
        } else {
            $PercentAvailable = 0
        }

        $Status = if ($PercentAvailable -le $ThresholdPercent) { "CRITICAL - LOW SEATS" } else { "HEALTHY" }

        $SkuObject = [PSCustomObject]@{
            SkuName          = $SkuName
            TotalPurchased   = $Sku.PrepaidUnits.Enabled
            AssignedSeats    = $ActiveConsumed
            AvailableSeats   = $AvailableSeats
            PercentAvailable = "$PercentAvailable%"
            Status           = $Status
        }

        $Report.Add($SkuObject)

        if ($PercentAvailable -le $ThresholdPercent -and $Sku.PrepaidUnits.Enabled -gt 0) {
            $LowCapacityAlerts.Add($SkuObject)
        }
    }

    # Export Full Usage Report
    $Report | Export-Csv -Path $ReportPath -NoTypeInformation
    Write-Log "License usage audit exported to: $ReportPath" "Yellow"

    # Display Summary
    $Report | Format-Table -AutoSize

    # Trigger Alert if Low Capacity Detected
    if ($LowCapacityAlerts.Count -gt 0) {
        Write-Log "WARNING: $($LowCapacityAlerts.Count) SKU pool(s) dropped below $ThresholdPercent% capacity threshold!" "Red"
        # Optional: Integrate Send-MgUserMail or Webhook payload here for automated alerts
    } else {
        Write-Log "All tenant license pools are operating above the $ThresholdPercent% threshold." "Green"
    }
}
catch {
    Write-Log "ERROR: $_" "Red"
}

Save the script as Get-M365LicenseUsageAlert.ps1 and paste it inside Code Block Pro below.

5. Execution & Enterprise Automation

Run the monitor locally in PowerShell to verify current tenant capacity and send alerts if remaining licenses drop below 10%:

.\Get-M365LicenseUsageAlert.ps1 -ThresholdPercent 10 -AlertEmail "it-admin@dsinnovationguru.com"

Enterprise Automation: Schedule this script as a recurring daily job using Azure Automation Runbooks or **Azure Functions** with a System-Assigned Managed Identity. This ensures your IT Asset Management team receives real-time email alerts before license pools hit 0% during mass onboarding events.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top