WYRE-AI/msp-claude-plugins/msp-claude-plugins/m365/m365/skills/licensing/SKILL.md
Microsoft 365 Licensing
The M365 subscription → SKU → service-plan model, seat availability versus consumption, assigning and removing licenses through Graph, the audit workflow for finding unused or misallocated seats, common SKU GUIDs, and licensing error causes.
- Source repository stars
- 42
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-28
- Source checked
- 2026-08-28
Decision brief
What it does: where it fits
The M365 subscription → SKU → service-plan model, seat availability versus consumption, assigning and removing licenses through Graph, the audit workflow for finding unused or misallocated seats, common SKU GUIDs, and licensing error causes.
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/WYRE-AI/msp-claude-plugins --skill "msp-claude-plugins/m365/m365/skills/licensing"Inspect the Agent Skill "Microsoft 365 Licensing" from https://github.com/WYRE-AI/msp-claude-plugins/blob/5005f73ba2f52cd299f58aa6bb79f4e70ae87103/msp-claude-plugins/m365/m365/skills/licensing/SKILL.md at commit 5005f73ba2f52cd299f58aa6bb79f4e70ae87103. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
License Audit Workflow
Pull subscribedSkus and calculate: - Total purchased per SKU - Consumed seats - Available seats - SKUs in warning state (near renewal, overallocated)
Total purchased per SKUConsumed seatsAvailable seats - 02
Step 1: Inventory Available SKUs
Pull subscribedSkus and calculate: - Total purchased per SKU - Consumed seats - Available seats - SKUs in warning state (near renewal, overallocated)
Total purchased per SKUConsumed seatsAvailable seats - 03
Step 2: Cross-Reference With Active Users
Find licenses assigned to disabled accounts — these are reclaim candidates:
Find licenses assigned to disabled accounts — these are reclaim candidates: - 04
Step 3: Find Inactive Licensed Users
Users licensed but not signing in (90+ days):
Users licensed but not signing in (90+ days):Filter results where signInActivity.lastSignInDateTime < (today - 90 days). - 05
Step 4: Produce Optimization Report
Review the “Step 4: Produce Optimization Report” section in the pinned source before continuing.
Review and apply the “Step 4: Produce Optimization Report” source section.
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 94/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 42 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- WYRE-AI/msp-claude-plugins
- Skill path
- msp-claude-plugins/m365/m365/skills/licensing/SKILL.md
- Commit
- 5005f73ba2f52cd299f58aa6bb79f4e70ae87103
- License
- Apache-2.0
- Collected
- 2026-08-28
- Default branch
- main
View the original SKILL.md
Microsoft 365 Licensing
Overview
M365 licensing is a top billing concern for MSPs. Licenses are purchased as SKU subscriptions, each containing bundles of service plans (Exchange, Teams, SharePoint, etc.). Efficient license management — finding unused seats, rightsizing SKUs, ensuring all users have what they need — directly impacts both the MSP's margin and the customer's costs.
Anti-triggers
Assigning a seat and buying a seat are different systems, and the word "license" covers both:
- Buying, cancelling, or repricing subscriptions — changing what
the tenant owns happens at the distributor, not in Graph.
subscribedSkusonly reports what was already purchased. Usepax8(pax8-subscriptions),sherweb, orcippfor CSP licences. - License cost, margin, or invoice reconciliation — commercial data
lives with the distributor and in the PSA contract, not in Entra; use
pax8(pax8-invoices) or thefinance-pack. - A licence audit across every customer tenant — use the
cippplugin (cipp-licenses). - Reading seat utilisation without changing assignments — the
vetted query catalogue answers this without composing a
$filter; use themicrosoft-graphplugin'smicrosoft-graph-queryingskill.
Core Concepts
Subscription → SKU → Service Plans
M365 Business Premium (subscription)
└── GUID: cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46
├── Exchange Online (service plan)
├── Microsoft Teams (service plan)
├── SharePoint Online (service plan)
├── Intune (service plan)
└── Entra ID P1 (service plan)
License States
| State | Meaning |
|---|---|
Enabled | Service plan is active and usable |
Disabled | Plan is turned off for this user (license still assigned) |
Error | Assignment failed — usually missing usageLocation |
LockedOut | Tenant billing issue |
PendingInput | Waiting for additional configuration |
Graph API Patterns
Get All SKUs Available in Tenant
GET /v1.0/subscribedSkus?$select=skuPartNumber,skuId,consumedUnits,prepaidUnits,servicePlans
Response:
{
"value": [
{
"skuPartNumber": "SPE_E3",
"skuId": "05e9a617-0261-4cee-bb44-138d3ef5d965",
"consumedUnits": 42,
"prepaidUnits": {
"enabled": 50,
"suspended": 0,
"warning": 0
},
"servicePlans": [...]
}
]
}
Available seats = prepaidUnits.enabled - consumedUnits
Get All Users With Their Assigned Licenses
GET /v1.0/users?$select=id,displayName,userPrincipalName,accountEnabled,assignedLicenses,usageLocation&$top=999
Find Users With a Specific License
Filter by SKU GUID:
GET /v1.0/users?$filter=assignedLicenses/any(x:x/skuId eq cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46)&$select=id,displayName,userPrincipalName,accountEnabled
Find Unlicensed Users
GET /v1.0/users?$filter=assignedLicenses/$count eq 0&$count=true&$select=id,displayName,userPrincipalName,accountEnabled
Requires
ConsistencyLevel: eventualheader and$count=true
Assign a License to a User
POST /v1.0/users/{userId}/assignLicense
Content-Type: application/json
{
"addLicenses": [
{
"skuId": "cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46",
"disabledPlans": []
}
],
"removeLicenses": []
}
usageLocationmust be set on the user before assigning. UsePATCH /v1.0/users/{id}with"usageLocation": "US"first.
Remove a License from a User
POST /v1.0/users/{userId}/assignLicense
Content-Type: application/json
{
"addLicenses": [],
"removeLicenses": ["cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46"]
}
Disable Specific Service Plans (Partial License)
Assign a license but disable specific plans (e.g., give E3 without Teams):
POST /v1.0/users/{userId}/assignLicense
Content-Type: application/json
{
"addLicenses": [
{
"skuId": "05e9a617-0261-4cee-bb44-138d3ef5d965",
"disabledPlans": ["57ff2da0-773e-42df-b2af-ffb7a2317929"]
}
],
"removeLicenses": []
}
License Audit Workflow
Step 1: Inventory Available SKUs
Pull subscribedSkus and calculate:
- Total purchased per SKU
- Consumed seats
- Available seats
- SKUs in
warningstate (near renewal, overallocated)
Step 2: Cross-Reference With Active Users
Find licenses assigned to disabled accounts — these are reclaim candidates:
GET /v1.0/users?$filter=accountEnabled eq false and assignedLicenses/$count ne 0&$count=true&$select=id,displayName,userPrincipalName,assignedLicenses
Step 3: Find Inactive Licensed Users
Users licensed but not signing in (90+ days):
GET /v1.0/users?$filter=accountEnabled eq true&$select=id,displayName,userPrincipalName,assignedLicenses,signInActivity
Filter results where signInActivity.lastSignInDateTime < (today - 90 days).
Step 4: Produce Optimization Report
| Optimization | Estimated Saving |
|---|---|
| Remove licenses from disabled accounts | # disabled × monthly seat cost |
| Downgrade inactive users to lighter SKU | SKU price delta × count |
| Recover unused purchased seats | (purchased - consumed) seats available |
Common SKU GUIDs Reference
| SKU Part Number | GUID | Notes |
|---|---|---|
SPE_E3 | 05e9a617-0261-4cee-bb44-138d3ef5d965 | M365 E3 |
SPE_E5 | 06ebc4ee-1bb5-47dd-8120-11324bc54e06 | M365 E5 |
O365_BUSINESS_PREMIUM | cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46 | M365 Business Premium |
ENTERPRISEPACK | 6fd2c87f-b296-42f0-b197-1e91e994b900 | Office 365 E3 |
AAD_PREMIUM | 078d2b04-f1bd-4111-bbd4-b4b1b354cef4 | Entra ID P1 |
AAD_PREMIUM_P2 | 84a661c4-e949-4bd2-a560-ed7766fcaf2b | Entra ID P2 |
EMS | efccb6f7-5641-4e0e-bd10-b4976e1bf68e | EMS E3 |
Error Handling
| Error | Cause | Resolution |
|---|---|---|
LicenseAssignmentError | No usageLocation on user | Set usageLocation first |
MutuallyExclusiveLicenses | Two conflicting SKUs | Remove old SKU before assigning new |
Request_ResourceNotFound | Invalid SKU GUID | Verify GUID against subscribedSkus |
Authorization_RequestDenied | Missing Directory.ReadWrite.All | Grant admin consent |
Permissions Required
| Task | Microsoft Graph Permission |
|---|---|
| View subscribed SKUs | Directory.Read.All |
| View user licenses | User.Read.All |
| Assign/remove licenses | User.ReadWrite.All or Directory.ReadWrite.All |
| Sign-in activity | AuditLog.Read.All |
Related Skills
- M365 Users - User management and usageLocation
- M365 Security - License impact on security features (P1/P2)
- M365 API Patterns - Advanced filter, count queries
Frequently asked questions
What to verify before installation and use
What does the Microsoft 365 Licensing source document cover?
The M365 subscription → SKU → service-plan model, seat availability versus consumption, assigning and removing licenses through Graph, the audit workflow for finding unused or misallocated seats, common SKU GUIDs, and licensing error causes.
How do I install Microsoft 365 Licensing?
The source record exposes this install command: npx skills add https://github.com/WYRE-AI/msp-claude-plugins --skill "msp-claude-plugins/m365/m365/skills/licensing". Inspect the command and pinned source before running it.
Alternatives
Compare before choosing
alirezarezvani/claude-skills
app-store-optimization
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
brucesongs/kali-claw
insecure-design
Insecure Design (OWASP A06:2025) focuses on security flaws in system architecture and design phases, rather than code implementation-level bugs.
NintendaDev/unikit-ai
unikit-docs
Generate and maintain the project's TECHNICAL documentation from its codebase — scans the project structure, tech stack, and module boundaries, then writes a lean README landing page plus detailed topic pages (architecture, modules, setup, build, APIs), only the docs that are relevant. Use whenever the user wants to create, update, or validate documentation of the CODE or the project itself, e.g. "generate documentation", "create docs", "write the README", "update the project docs", "document th
K-Dense-AI/scientific-agent-skills
dask
Distributed computing for larger-than-RAM pandas/NumPy workflows. Use when you need to scale existing pandas/NumPy code beyond memory or across clusters. Best for parallel file processing, distributed ML, integration with existing pandas code. For out-of-core analytics on single machine use vaex; for in-memory speed use polars.