Skip to content

License usage report: SKU inventory, assigned counts, and per-user breakdown.

Exports a comprehensive report useful for audits — combines inventory, unlicensed users, and per-SKU user lists into a single view.

Permissions

Requires delegated permission User.Read.All, Organization.Read.All.

Reference

View source

from office365.graph_client import GraphClient
from tests.settings import client_id, client_secret, tenant

client = GraphClient(tenant=tenant).with_client_secret(client_id, client_secret)

# 1. SKU inventory
skus = client.subscribed_skus.get().execute_query()
print("=" * 80)
print("LICENSE INVENTORY")
print("=" * 80)
print(f"{'SKU':35s}  {'Enabled':>8s}  {'Consumed':>8s}  {'Status':12s}")
print("-" * 80)
for s in sorted(skus, key=lambda x: x.sku_part_number or ""):
    enabled = s.prepaid_units.enabled if s.prepaid_units else 0
    consumed = s.consumed_units or 0
    status = s.capability_status or ""
    print(f"{s.sku_part_number or '':35s}  {enabled:8d}  {consumed:8d}  {status:12s}")

# 2. All users with their licenses
all_users = (
    client.users.select(["displayName", "userPrincipalName", "assignedLicenses"])
    .order_by("userPrincipalName")
    .get()
    .execute_query()
)

sku_map = {s.sku_id: s.sku_part_number for s in skus}

print()
print("=" * 80)
print("USER LICENSE ASSIGNMENTS")
print("=" * 80)
for u in all_users:
    assigned = u.assigned_licenses or []
    if assigned:
        names = [str(sku_map.get(lic.skuId, lic.skuId) or "")[:8] for lic in assigned]
        print(f"  {u.user_principal_name:45s}  {', '.join(names)}")

# 3. Unlicensed users
unlicensed = [u for u in all_users if not u.assigned_licenses]
print()
print("=" * 80)
print(f"UNLICENSED USERS ({len(unlicensed)})")
print("=" * 80)
for u in unlicensed:
    print(f"  {u.user_principal_name:45s}  {u.display_name or ''}")

← Back to Licensing