What it observes

What is assigned in a tenant, and whether the usage reports are permitted to name the people who hold it.

Bash
m365-governance collect licensing \
  --tenant-url <tenant>.onmicrosoft.com \
  --period D30 \
  --output ./evidence/licensing/

The address is the same --tenant-url every tenant slice takes. This collector opens no SharePoint session with it: it reads Graph, and the address only says which directory the evidence came from.

ModeLicensing
Needs--tenant-url; --period for any usage read
AcquisitionMicrosoft Graph, not PnP
Produces findingsNo
Consumed byAny viewer. No rule reads it.

A different acquisition surface from every other collector

Every other collector here reads SharePoint through PnP. This reads the directory and the reporting endpoints through Microsoft Graph, with different modules, different permissions and a different sign-in.

Reports.Read.All is the least privileged permission Microsoft documents for the usage reports, delegated or application; Organization.Read.All and User.Read.All are the directory half. A delegated call also requires the caller to hold an appropriate administrator role.

The modules are named by m365-governance doctor, with the acquisition surface each one serves, so a machine that cannot run this says which install is missing rather than reporting an empty tenant.

Four coverage areas, and none of them implies another

This is the whole shape of the collector, and the reason it produces no findings.

AreaWhat it means
assignmentSubscribed SKUs and per-user assignments, read from the directory.
usage_identityWhether the usage reports may name people.
usageA usage report, over a period the caller asked for.
dependencyWhat a capability is required for. Nothing collects this.

assignment = completed does not imply usage = completed, and neither implies dependency = completed. A tenant can have every licence read and no usage report; it can have the usage report and no permission to attribute it; and it has never had dependency evidence, because this product collects none.

Concealed is an answer, not an absence

usage_identity is its own area because the three situations underneath it are three different facts:

  • completed: the setting was read and the reports may name people.
  • partial: the setting was read and says they may not. Microsoft conceals identifiable user information by default, in the admin centre reports, the Microsoft 365 usage reports in Graph and Power BI, and the Teams admin centre reports.
  • missing: nobody read the setting.

A report of ten thousand rows whose principal names are concealed supports no statement about any person in it, so usage_rows and usage_rows_naming_a_principal are two separate counts.

What the evidence carries

FieldWhat it is
populationentra-licensed-user-assignments
acquisition_methodenumerated
populations_not_observedLicence models these reports do not cover, and capabilities granted outside this tenant's subscriptions
subscribed_skusHow many SKUs the tenant holds
skusOne row per SKU, as Microsoft returned it
units_assignedconsumedUnits, summed
report_identifiabilityconcealed or identifiable
usage_window_daysThe periods read, always an array, including when one period was asked for
usage_report_refresh_dateThe report's own statement of when its data was last rebuilt
usage_rows / usage_rows_naming_a_principalRows returned, and rows that name a principal
acquisition_attemptsPer surface: the operation, identity, result, reason, and whose limitation it is
plans_with_one_sourceService plans delivered by exactly one of a person's assignments
plans_with_more_than_one_sourceService plans another assignment also delivers
dependency_evidencepartial. What one assignment uniquely delivers is calculable; what a capability is required for is not

A SKU is a bundle, and a service plan is what somebody has

Counting SKUs does not tell you what anybody can do. A SKU carries service plans, an assignment can disable some of them, not every plan is a user capability, and two different SKUs can deliver the same plan. All four matter, and Microsoft documents each:

FieldWhat it decides
servicePlans[].appliesToUser or Company. Only a plan that applies to a user is a capability a person holds
servicePlans[].provisioningStatusSuccess, Disabled, Error, PendingInput, PendingActivation, PendingProvisioning. A plan that is not provisioned is not a capability
assignedLicenses[].disabledPlansPlans switched off for this person, in this assignment
subscribedSku.appliesToOnly a SKU whose target class is User is assignable at all

So what an assignment actually delivers is:

Text
the SKU's service plans
  minus that assignment's disabled plans
  keeping only appliesTo = User and provisioningStatus = Success

A plan delivered by exactly one of a person's assignments disappears if that assignment is removed. A plan delivered by more than one does not, because something else already carries it. Both are counted.

That is not a recommendation, and the distance between the two is the point. Two SKUs delivering one plan may differ in quota, in support, or in what a contract permits, and none of that is visible in this evidence. This plan also arrives elsewhere and this licence can be removed are different sentences, and only the first is established here.

What a capability is required for is not observable from an assignment or from usage: a policy that depends on it, a role, a compliance obligation, a workload that would stop. That is why dependency_evidence is partial rather than complete, and why no optimisation conclusion is produced.

Every multi-valued field is an array at every cardinality. It is written here because it was not true once: usage_window_days was built by a pipeline whose input was wrapped and whose result was not, so it published a number for one period and an array for two. A consumer parsing it as a number breaks on the second period, and one parsing it as an array breaks on the first. The engine now forces the shape after the whole pipeline and a gate checks zero, one and many by type.

What has been read from a real tenant

Three of the four coverage areas have been acquired from a live directory, and the whole chain was crossed without manual repair between steps: collection, the evidence directory, the collection manifest, the independent reader, the canonical bundle, and a consumer opening it.

AreaState
assignmentacquired
usage_identityacquired, and the tenant read was concealed
usageacquired, with its window and refresh date
dependencycollected by nothing

The report returned rows and named nobody. That is recorded as usage completed with usage_identity unavailable, never as usage unavailable: the usage exists and cannot be attributed to a directory identity by this read.

No rule reads any of this, so evaluating it produces no finding. That is the expected outcome of an observation-only family and not a failed run.

There is no total number of licences

units_purchased existed and was removed. It summed prepaidUnits.enabled across SKUs and returned a seven-figure total on a tenant holding a few dozen assigned seats: arithmetically correct and meaningless, because the count means something different on a paid seat SKU and on a free or effectively unlimited one.

It was not replaced by a better total. An aggregate somebody hand-filtered is the same failure with more steps: it decides what a tenant holds by how the figures look.

consumedUnits is still summed, because an assignment means the same thing on every SKU: somebody holds it.

What each subscription is, which is the question that comes first

The classification the paragraph above said this engine did not have now exists, and its answer is that nothing on this surface may be added to anything else on it. Every observed subscription carries two values and the documented rule that produced them.

capacity_kind, which is what the units are:

ValueEstablished by
user-assignableappliesTo is User, the only target class Microsoft documents as assignable
organisationalappliesTo is Company. These units were never seats anybody holds
not-establisheda target class outside the documented pair. Not an error, and not a licence to assume

aggregation_eligibility, which is whether they may be counted with another subscription's:

ValueMeaning
comparablean established interpretation permits adding these units to another subscription's
not-comparablethe number is valid and represents something else
not-establishednot enough authority to decide

No subscription is comparable today, and that is the result rather than a gap. Two things this surface does not carry decide it. Microsoft documents self-service sign-up as provisioning services without asking you to take action on their behalf, so a subscription being present in a tenant is not evidence the organisation bought it. And prepaidUnits.enabled is documented as the units enabled for the active subscription, which is a different quantity from the seats a subscription carries.

A subscription is never excluded because its number looks wrong. A tenant holding a million units of one product and five hundred of another has them classified identically, because from this surface they are identical. A threshold would decide what a tenant holds by the size of its figures, which is the error the removed total made, with a filter in front of it.

The four prepaid counters

prepaidUnits is a licenseUnitsDetail, and Microsoft documents four counters on it. This collector read the first and called it prepaid_units, which flattens a subscription's lifecycle into one integer.

CounterWhat it counts
enabledunits enabled for the active subscription
warningthe grace period after the subscription expired, before cancellation
suspendedafter cancellation. Cannot be assigned, can still be reactivated
lockedOutafter the customer cancelled

A tenant whose subscription has expired has its units in warning, and a single integer cannot say so: it reads zero, which looks like a subscription holding nothing rather than one about to be lost.

Where the number everybody wants actually lives

Not here. companySubscription carries isTrial, documented as whether the subscription is a free trial or purchased, and totalLicenses, documented as the number of seats included in this subscription. It is at /directory/subscriptions and subscribedSku.subscriptionIds is the join, which this collector now captures.

That resource is in the beta endpoint only. The commercially authoritative surface is not in v1.0, and that is a result about Microsoft Graph rather than about this engine. Until an authorised read of it exists, comparable_capacity is recorded as absent and names this measurement, never estimated.

Whose limitation an absence is

Every acquisition surface records its attempt, and an absent one records who owns the limitation:

  • implementation: this version does not collect it yet. Our work.
  • tenant-or-identity: the directory refused. A fact about the environment.
  • microsoft: not exposed in this cloud. A fact about the platform.
  • caller: not asked for, such as a usage read with no period.

Only the first is unfinished product. The others are evidence.

What this does not do

It produces no recommendation, no saving, no unused-licence count and no score. Concluding that a licence can be removed needs evidence of use and of dependency; this reads the first at best and the second not at all, and the absence is recorded as a fact so that a usage figure cannot be read as an answer.

What the Engine publishes

Generated from the Engine's capability manifest at1.0.0b8@2d4f5db76f21. No value on this panel is maintained by this site.

Answers
what is assigned in one tenant, and whether the usage reports are permitted to name the people who hold it
Collector
PnP PowerShell · mode Licensing
Reads
  • Get-MgSubscribedSku
  • Get-MgUser -Property assignedLicenses
  • GET /beta/admin/reportSettings
  • GET /v1.0/reports/getOffice365ActiveUserDetail
Least privilege
  • Organization.Read.All
  • User.Read.All
  • Reports.Read.All
  • ReportSettings.Read.All
Produces
microsoft-365 ·tenant
Proven against a tenant
partially live-validated, assignment, report identifiability and one usage report were observed against a real directory; the report returned rows and named nobody, and dependency evidence is collected by nothing

Rules that decide from it

No rule reads this evidence, and that is a recorded decision rather than a gap: the licensing evidence in a report, and any viewer is what consumes it. A threshold invented for a surface Microsoft publishes no position on would make a pass mean nothing.

Contracts it produces

Every collection writes evidence and an account of itself. The manifest publishes no narrower per-capability list, so this states what it does establish rather than guessing at more.