Licence review in one prompt
14 Oct 2026
A Microsoft 365 licence review in one prompt
The licence review used to cost an afternoon: export the active users report, export the sign-ins, export the subscriptions, line them up in Excel by email address, and argue with yourself about what an empty cell means. In the GraphPaaS connector for Claude it is one item in the tools menu, Licence review, and one tenant to point it at.
The prompt is a short brief. It tells Claude which domains to read, in which order, with the 90-day cut already worked out as a date, and it ends by asking for a table of savings and who to check with first. Below is that run against Fabrikam HQ, our demo tenant (synthetic people, real tool calls, data collected on 11 October at 03:07 UTC). Each step shows what the prompt asks, the call it became, and what came back.
Seats you pay for and nobody holds
Per SKU, how many paid seats are assigned to nobody?
get_metrics(domain="licences", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"sku": ["SPE_E3", "EMS", "VISIOCLIENT", "PROJECTPLAN3", "POWER_BI_PRO"]},
aggregate=["sum:free", "sum:total"])
→ sum:free 22 · sum:total 125
22 of 125 paid seats are unassigned: 9 Power BI Pro, 5 EMS E3, 4 Project Plan 3, 3 Microsoft 365 E3 and 1 Visio Plan 2.
Claude read the full SKU list first and left two rows out of the sum on purpose. Power Automate Free shows 10,000 seats with 9 assigned, which would bury everything else and costs nothing. The same rule runs through the rest of the review: someone whose only products are free or trial ones (Office 365 A1, Power Apps for Developer, Fabric Free) is not in the waste list at all, because removing a licence nobody pays for saves nothing.
Ninety days without activity, and a date that lies
Who holds a paid licence with no activity, or none since 13 July?
get_metrics(domain="licence_waste", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"last_activity": null})
→ 5 rows
get_metrics(domain="licence_waste", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"last_activity": {"<": "2026-07-13"}}, order_by="last_activity")
→ 5 rows
get_metrics(domain="licence_waste", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
group_by="first_assigned")
→ 1 group: 2026-07-14 · 42
10 of the 42 people with a paid licence are idle. Ana Dias, João Yilmaz, Sofia Dias, Bernardo Yilmaz and Kelly Dias have no activity Microsoft reports. Clara Fonseca (15 February), Hugo Keller, Olivia Ramos, Jonas Moreira and Vera Yilmaz (15 June) were last active before the cut.
Two things in that data need reading carefully. The last activity is the most recent date across Exchange, OneDrive, SharePoint and Teams, the per-service columns of Microsoft's active user detail report. A null there means no date reported in any of the four, which is not quite the same as "never logged in", as the next step shows.
The third call is the trap. The same report has a License Assign Date per service, and it looks like the answer to "how long has this seat sat idle". Here all 42 people share one date, 14 July, including Clara, whose last activity was five months earlier. She did not get her licence after she stopped working. The date moves when licences are re-assigned or the SKU changes (we measured 14 users jumping together in a real tenant), so Claude reports it and never builds on it. Measure idleness from the activity, never from the assignment.
A second witness: the sign-in
Which licensed accounts has nobody signed in to since 13 July, and which disabled accounts still hold a licence?
get_metrics(domain="dormant_accounts", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"licences": {">": 0}, "last_signin": {"<": "2026-07-13"}})
→ 5 rows
get_metrics(domain="dormant_accounts", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"licences": {">": 0}, "last_signin": null})
→ 4 rows
get_metrics(domain="dormant_accounts", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"enabled": false})
→ 2 rows (1 with licences)
9 licensed accounts have no sign-in since 13 July. Six are also on the activity list: Ana, Clara, Hugo, Olivia, Jonas and Vera. Filipe Moreira is disabled and still holds 2 licences; his last activity was 4 October.
The two lists overlap on six names and disagree on seven, and the
disagreement is the useful part. The sign-in date is
lastSignInDateTime, which Microsoft defines as the last
interactive
sign-in attempt. Henrique Yilmaz, Liam Moreira and Wei Fonseca have none on
record, yet all three show workload activity since 23 September. The likely
reason is apps that refresh the session without prompting, which only the
separate non-interactive date records.
João, Sofia, Bernardo and Kelly are the opposite case. They signed in recently
but show nothing in the four workloads, so ask what they use before touching
their licence.
Copilot seats that may already be gone
Which paid Copilot seats were never used?
get_metrics(domain="copilot_usage", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"last_activity": null})
→ 3 rows of 14
get_metrics(domain="licences", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
search="copilot")
→ 0 rows
Ana Dias, Elena Fonseca and João Yilmaz have 0 prompts. But the tenant has no Copilot SKU today, so there is nothing to reclaim here.
This is why the check against licences matters. Microsoft's
Copilot
usage report lists everyone licensed for Copilot at any point in the past
180 days, even if the licence was later removed. Counting its idle rows as
seats to cancel would count seats that were cancelled already.
What renews before you decide
Which subscriptions are trials, or change state before 10 December?
get_metrics(domain="subscriptions", tenant_id="ce48e256-019f-4f8c-b951-0c52d1d65b8e",
where={"next_lifecycle": {"<": "2026-12-10"}}, order_by="next_lifecycle")
→ 4 rows
Visio Plan 2 (5 seats) is in Warning; its date, 5 October, has passed. Power BI Pro is a 20-seat trial that changes state on 20 October. Microsoft 365 E3 and EMS E3, 45 seats each, both reach their next date on 18 November.
Each subscription's nextLifecycleDateTime is when it moves to its next state if not renewed, and Warning means it already expired and sits in the grace period before suspension. So Visio needs a decision this week, the Power BI trial within ten days, and the 3 spare E3 and 5 spare EMS seats are worth settling before 18 November.
The table it ends with
The prompt closes by asking for savings opportunities, biggest first, and who to check with before removing anything. It also tells Claude not to invent prices: it only puts a money figure on a row when the organisation uploaded its own price catalogue to the GraphPaaS licence advisor, or when you give it the prices. The demo's catalogue is a sample, so the table counts seats.
| What | Seats | Check with |
|---|---|---|
| Paid seats assigned to nobody | 22 | Whoever owns the renewal, before 18 November |
| No activity and no sign-in in 90 days | 6 people | Their managers |
| Disabled account, still licensed | 2 licences | Whoever runs offboarding |
| Power BI Pro trial ending 20 October | 20 | The Power BI report owners |
| Signed in, no workload activity | 4 people | The users themselves; not a removal yet |
- reportRoot: getOffice365ActiveUserDetail — the per-service last activity and licence assign dates behind licence_waste.
- How to detect and investigate inactive user accounts — lastSignInDateTime is interactive only; the non-interactive date is separate.
- Microsoft Copilot usage report — the user table keeps anyone licensed in the past 180 days, even after removal.
- companySubscription resource type — isTrial, status and nextLifecycleDateTime.
- licenseUnitsDetail resource type — Warning means expired and inside the grace period before suspension.
Run the licence review on your own tenant: it is in the connector's tools menu.
Add the connector →