The commands
| Command | Status |
|---|---|
m365g entra app list | Published, tenant-verified |
m365g entra serviceprincipal list | Published, tenant-verified |
m365g governance inspect app-exposure | Published, tenant-verified |
The inspection reaches no tenant, so no tenant can verify it: it stands on the collections it reads, and its label follows them.
This read has read one directory. That is what the label above stands on, and the Engine's record of the read lists what it did not cover. No credential ended inside the default window, so the ending branch of the inspection has not run against a directory. It was one page, so paging is proved against a stand-in only. No refusal was met, and no registration came back without a credential list, so the missing case is proved against a stand-in only. The Engine's record adds that it was read again through the engine's own sign-in as a person, with a session kept by login, no further prompt and no token supplied from outside. And it was one directory at one moment, which establishes nothing about how another answers.
The Engine's record adds that the branch reporting a credential already ended did run against that directory. Every output on this page is from documents written for it, not from that read.
| Collection | Collected by | Status |
|---|---|---|
applications | m365g entra app list | Published, tenant-verified |
service-principals | m365g entra serviceprincipal list | Published, tenant-verified |
The command that feeds it
m365g entra app list enumerates the application registrations in the directory. Enumeration shows what it prints.
m365g entra app list --tenant-url https://contoso-admin.sharepoint.com --evidence-out ./evidence/applications
m365g governance inspect app-exposure --evidence ./evidenceThe first command reaches a tenant. The second does not.
What is never collected, and why
The read is built around what it leaves out.
- A secret is never in the evidence, and neither is its hint. Microsoft Graph does not return a secret after it is created. It does return the first characters of one, as a hint. The read drops that before anything is written: a fragment of a credential has no use in evidence and one obvious misuse.
- A certificate's bytes are not requested, and its thumbprint is not kept. When the certificate ends is the fact. Which certificate it is, is the operator's to look up by the application's identifier.
- Owners are not read. Who is responsible for an application is a different read, with a different answer about people.
What a credential leaves in the evidence is its kind, when it starts and when it ends.
Asked, not granted
Each row prints two numbers under asks for, and closes the line with (asked, not granted). They count the permissions written on the registration: what the application asks to be allowed to do. Whether an administrator or a user consented to any of it is recorded elsewhere, on the service principal and in the grants, which this does not read. The inspection states it among its relations: application -> what was consented to: NOT JOINED.
So a registration that asks for three application permissions may hold none of them, and these numbers say nothing about what an application can do.
The window is yours
--expires-within 30d is the window after the collection inside which a credential counts as ending. It is the operator's: the engine records the one that was used, in the question and in the parameters of the inspection. Without the option the window is 30 days. A value that is not a number of days is refused, with exit 2.
What it prints
Which application registrations hold a credential that had ended, or ends within 30 days, when the evidence was collected?
Payroll Export application-id-0001
standing ended (1 credentials had ended at collection)
credentials ended 1 · ending 0 · later 1 · earliest end 2026-10-01T00:00:00Z
can sign in here [not-collected]
asks for application 2 · delegated 0 (asked, not granted)
Intranet Sync application-id-0002
standing ending (1 credentials end within 30 days of the collection)
credentials ended 0 · ending 1 · later 0 · earliest end 2026-11-25T00:00:00Z
can sign in here [not-collected]
asks for application 1 · delegated 0 (asked, not granted)
Reporting Connector application-id-0003
standing later (every credential ends after the window)
credentials ended 0 · ending 0 · later 1 · earliest end 2027-06-15T00:00:00Z
can sign in here [not-collected]
asks for application 3 · delegated 1 (asked, not granted)
Kiosk Sign-in application-id-0004
standing no-credentials (the application holds no secret and no certificate)
credentials ended 0 · ending 0 · later 0 · earliest end -
can sign in here [not-collected]
asks for application 0 · delegated 2 (asked, not granted)
Legacy Importer application-id-0005
standing not-established (the credentials of this application were not returned)
credentials ended - · ending - · later - · earliest end -
can sign in here [not-collected]
asks for application 1 · delegated 0 (asked, not granted)
Archive Bot application-id-0006
standing not-established (1 credentials carry no end date)
credentials ended 0 · ending 0 · later 0 · earliest end -
can sign in here [not-collected]
asks for application 0 · delegated 1 (asked, not granted)
6 applications in the evidence:
ended: 1
ending: 1
later: 1
no-credentials: 1
not-established: 2
Not established by this inspection:
- what any application was in fact granted: consent lives on the service principal and in the grants, which were not read
- whether a credential is in use: one that ended may belong to an application nobody runs, and one that ends later may be the only thing keeping a service alive
- who is responsible for an application: owners were not read
- credentials held by service principals rather than by the registration: a different object, not read
From 6 evidence documents, sha256:a84025af8668…, collected 2026-11-10T09:45:00Z to 2026-11-10T09:45:00Z.Where application-id-0001 stands, a run prints the application's own identifier. The counts at the end are the number of rows in each standing, named for the standing they count. They are not added into anything.
--output csv and --output md give one row per application, with the columns name, app_id, standing, ended, ending, later, earliest_end, requested_application_permissions, requested_delegated_permissions, can_sign_in_here, collected_at and because.
With only the applications in the evidence, as here, every row says can sign in here [not-collected]: the read that would answer it is not in the evidence, and nothing is said about signing in.
Whether a registration can sign in here
Published, tenant-verified
This read has read one directory. That is what the label above stands on, and the Engine's record of the read lists what it did not cover. It was one page: the population was below the page size asked for, so paging is proved against a stand-in only. No refusal was met. What any service principal was granted was not read: that needs a scope that has not been decided. And it was one directory at one moment, which establishes nothing about how another answers.
A registration is a definition. What signs in to a directory is its service principal there. When evidence from m365g entra serviceprincipal list is also present, the inspection joins each registration to its service principal on the application identifier both carry, and says whether the application can sign in to this directory.
m365g entra serviceprincipal list --tenant-url https://contoso-admin.sharepoint.com --evidence-out ./evidence/service-principals
m365g governance inspect app-exposure --evidence ./evidenceFour of the six rows, and the block that follows them:
Payroll Export application-id-0001
standing ended (1 credentials had ended at collection)
credentials ended 1 · ending 0 · later 1 · earliest end 2026-10-01T00:00:00Z
can sign in here yes
asks for application 2 · delegated 0 (asked, not granted)
Intranet Sync application-id-0002
standing ending (1 credentials end within 30 days of the collection)
credentials ended 0 · ending 1 · later 0 · earliest end 2026-11-25T00:00:00Z
can sign in here no: its service principal is disabled
asks for application 1 · delegated 0 (asked, not granted)
Reporting Connector application-id-0003
standing later (every credential ends after the window)
credentials ended 0 · ending 0 · later 1 · earliest end 2027-06-15T00:00:00Z
can sign in here no: no service principal in this directory
asks for application 3 · delegated 1 (asked, not granted)
Legacy Importer application-id-0005
standing not-established (the credentials of this application were not returned)
credentials ended - · ending - · later - · earliest end -
can sign in here [missing]
asks for application 1 · delegated 0 (asked, not granted)
8 service principals in the evidence:
with a registration here 5
without one: of another organisation 2 · of this directory 0 · not established 1There are three answers, and each row prints one.
yes: the registration has a service principal in this directory and it is enabled.Payroll Export.no: its service principal is disabled: it has one, and it is switched off.Intranet Sync.no: no service principal in this directory: the registration exists and nothing here can sign in as it.Reporting Connector.
Two more forms say the question was not answered. [not-collected] means no service principal collection is in the evidence, as in the first output on this page. A state in brackets, such as [missing] on Legacy Importer, means the service principal was read and whether it is enabled was not returned.
The answer changes how a standing reads, and does not change the standing. Payroll Export has a credential that ended and can sign in. Reporting Connector has a credential that ends later and cannot sign in here at all. The inspection puts the two facts side by side and concludes nothing from the pair.
The block under the rows counts the service principals: those with a registration here, and those without one, by whose application the service says they are. A service principal with no registration here belongs to somebody else. They are counted and never listed as a finding, because most of a directory's service principals are applications of other organisations.
The five standings
ended: at least one credential had ended when the evidence was collected.Payroll Exportholds two: one had ended, one ends later. An ended credential decides the standing whatever else the application holds.ending: none had ended, and at least one ends inside the window after the collection.Intranet Sync.later: every credential ends after the window.Reporting Connector.no-credentials: the service returned an empty list. The application holds no secret and no certificate, so there is nothing to end.Kiosk Sign-in.not-established: the comparison cannot be made.
Two rows above are not-established, for two reasons, and neither is no-credentials.
Legacy Importer: the credentials were not returned. A list the service did not return ismissing. Only an observed empty list is an application with nothing to expire, and somebody counting expiring secrets must not read a missing list as none.Archive Bot: a credential carries no end date. It cannot be placed before, inside or after the window.
Measured from the collection, never from the clock
Every date is compared with the moment the evidence was collected, which is on every row as collected_at. The clock of the machine running the inspection is never read. ending means inside the window after the collection, not inside the window after the day somebody ran the command.
That is why the same evidence always gives the same answer. The inspection above was run twice over the same six documents, and the two outputs are identical to the byte, digest included. Run a month from the collection, it prints the same rows again, and its question still says when the evidence was collected: somebody reading old evidence is told how old it is, and is not shown a credential as still ending that has since ended.
Only the window changes the answer. The same documents with --expires-within 400d:
Intranet Sync application-id-0002
standing ending (1 credentials end within 400 days of the collection)
credentials ended 0 · ending 1 · later 0 · earliest end 2026-11-25T00:00:00Z
can sign in here [not-collected]
asks for application 1 · delegated 0 (asked, not granted)
Reporting Connector application-id-0003
standing ending (1 credentials end within 400 days of the collection)
credentials ended 0 · ending 1 · later 0 · earliest end 2027-06-15T00:00:00Z
can sign in here [not-collected]
asks for application 3 · delegated 1 (asked, not granted)
6 applications in the evidence:
ended: 1
ending: 2
no-credentials: 1
not-established: 2Reporting Connector was later against 30 days and is ending against 400. Nothing was read again.
What it does not establish
The inspection prints this list itself, on every run.
- What any application was in fact granted. Consent lives on the service principal and in the grants, which were not read.
- Whether a credential is in use. One that ended may belong to an application nobody runs, and one that ends later may be the only thing keeping a service alive.
- Who is responsible for an application. Owners were not read.
- Credentials held by service principals rather than by the registration. A different object, not read.
So ended on a row is a statement about a date on a registration. It is not a statement that something is broken, and later is not a statement that something is safe.