Releases: ph7x-Systems/m365-governance-as-code
Release list
1.0.0b6
Publishes connection/1.0.0, so a consumer stops parsing an unversioned surface.
connect --format json was declared deliberately unversioned, on the reasoning that a session ends when the process does and so has nothing to persist. That answers the wrong question: persistence is not the test, dependence is. A consumer already parses it to decide whether a collection may start, and a shape somebody depends on is a contract whether or not it is called one.
connection/1.0.0, with its generated model, published in the bundle.- It describes an attempt rather than a resource: no rule reads it, no assessment carries it, it produces no finding.
addressandsessionstay separate blocks, and the schema states thatobserved_tenant_idmay never be filled fromresolved_tenant_id.address.hostis null when nothing was resolved, found by validating an attempt where the collector never ran.
Install
pipx install m365-governance-as-code==1.0.0b61.0.0b5
Two commands' worth of honesty about reaching a tenant, and the release gate
that should have existed before 1.0.0b4 went out.
connect: the other half of doctor
doctor reports whether this installation is sound. Nothing reported whether
the application registration in front of you can reach the tenant in front of
you — that was found out several minutes into a collection, from a failure that
looked like a tenant problem rather than a consent problem.
Four words rather than a boolean: established, refused, unreachable,
cancelled. A tenant that answered and would not have us is a different
sentence from one that never answered, and collapsing them sends a person to
check their network when the answer was consent.
Two questions, and one field would answer neither
Address resolution
<host> owned by <directory id>
Public discovery, and no session was involved.
Authenticated session
identity delegated
observed not established
Which directory owns an address is answerable by anybody, from public
OpenID discovery, without a token. Which directory a session operated in is
answerable only by the session. A GUID the whole world can obtain without
reaching a tenant is not evidence that a collection looked at it, so
resolved_tenant_id and observed_tenant_id are separate and the second is
null.
The address resolves before the sign-in and is reported whatever the sign-in
does: a tenant that refuses you still has an address, and somebody diagnosing a
consent problem is helped by knowing which directory they were actually pointed
at.
Evidence provenance gains neither by shortcut. That is a change to the evidence
contract, with its own decision.
The gate that was missing after the upload
1.0.0b2 shipped an install command that resolved to nothing. 1.0.0b4
shipped one naming 1.0.0b3. Both uploads succeeded, and a release description
is frozen at upload, so neither could be corrected without spending another
version.
A successful upload proves the file arrived. It proves nothing about whether
anybody can install and run it.
tools/post-release-check.sh installs from the public index into a
throwaway environment and refuses unless the program reports the released
version, doctor is sound, packaged evidence evaluates and decides something,
the documented commands run, and the contract bundle is in the wheel.
publish.yml runs it after every publish.
pipx, with ensurepath
pip install is the wrong command for an application, and on Homebrew's
Python, Debian's and Ubuntu's it is not even possible (PEP 668). Skipping
ensurepath installs successfully and still gives command not found, which
reads like a broken package.
The measured surface stopped counting comments as calls
tools/surface.py decided what the collector calls with a regular expression
over its source, so a comment explaining why a cmdlet was not called made the
published document say it was. It parses the syntax tree now, as the read-only
gate always has.
Status, measured
20 rules, a thirteen-mode collector, 9 profiles, 14 commands, 823 tests at 90
per cent coverage.
Install
brew install pipx # macOS. Ubuntu 23.04+/Debian 12+: sudo apt install pipx
pipx ensurepath # then open a new shell
pipx install m365-governance-as-code==1.0.0b51.0.0b4
A collection now reports what it is doing while it does it, and says what it
managed to do when it stops. Both were missing, and the second one is a new
contract.
A collection had no account of itself. The only thing a caller could read
was a process exit code, and a run that reached two hundred of three hundred
sites and then lost its connection had the same value as one that never
authenticated. The first produced evidence worth two hundred sites. That
collapse is made nowhere else here: coverage keeps requested and completed
apart, and a rule answers unknown rather than failing when the gap could
change its answer.
A new contract: collection/1.0.0
A collection-manifest.json is written beside the evidence, on every path
including the failure that produced no evidence at all. It carries the state,
the facts behind it, what was asked for, what was observed, the identity that
looked, the coverage as a union of the artefacts' own, every artefact with a
digest over its bytes, and a digest over itself.
Why a manifest, and not a field in each evidence document. An evidence
document is about one resource and a collection is a batch, so a field in each
would be one truth written once per document — and it could not answer the case
the contract exists for: a collection that stops halfway has to say what it did
not read, and the documents that would carry that sentence are exactly the ones
that were never written.
Full contract: docs/COLLECTION-MANIFEST.md
Consumers re-vendor. A collection state that lived only in a consumer would
be that consumer's opinion about an exit code.
Also in this release
- Four states where there was a boolean:
completed,partial,failed,
cancelled.partialis not a failure andcollectexits0for it.
cancelledis set by the caller and never inferred from an exit code. - The collector's output streams. It was buffered until the process exited,
socollect sitesagainst a large tenant printed nothing for however long it
took and then printed everything, including the line saying how many sites
the identity had enumerated. evaluatestates the bound before the results where a manifest exists,
on stderr. Where none exists it says nothing: evidence collected before this
contract carries no account of its own completeness, and inventing one would
report a gap that was never measured as an absence of gaps.- Two collections into one directory no longer destroy each other's record.
- The canonical form moved to
m365_governance/canonical.py, because two
documents now publish a digest a recipient recomputes.
Defects this found
Three came from printing a manifest the code had just written, and none was
visible in the code: a clean exit that wrote nothing reported completed; a
dry run had a state at all; and the reason an area was not read arrived as a
Python dict repr where a sentence belonged.
One came from adding the schema: the model generator emitted three records
called Versions and two called Coverage into one namespace, all different
shapes. Every file individually correct, the bundle would not have compiled —
which nothing here would have said, because nothing here compiles it. It
refuses now.
Not in this release
An assessment still does not record which collection produced its evidence or
in what state that collection ended. That costs an assessment contract version
and is the next step.
Install
pip install m365-governance-as-code==1.0.0b41.0.0b3
The engine reports the version that built it, and signs its assessments with it.
1.0.0b2 answered 1.0.0b1, because __version__ was a literal in __init__.py beside the one in pyproject.toml and only one of them was bumped.
That value is part of the canonical content an assessment is digested over. A document produced by 1.0.0b2 therefore carried a correct digest over content naming the wrong engine: the digest was honest about what it covered, and what it covered was not honest about what made it. In an engine whose whole claim is that a conclusion traces back to what produced it, that is not a typo.
The version is now read from the installed distribution metadata, so pyproject.toml is the only place it exists. tests/test_version.py compares the two by import and through --version as a user runs it, and refuses to let the not-installed fallback pass as a real version. It failed the moment it was written, against a virtual environment still holding the previous install.
Also in this release:
- The generated contract manifest and the stored example assessment and comparison carry
1.0.0b3. Both were stale for the same reason. CONTRIBUTING.mdnames the whole gate. CI runstools/release-check.sh, nine steps; the document listed four, so a contributor could run everything it asked for and still be refused for formatting.- The repository speaks English. A test file, a comment in
tools/coverage.pyand the same note repeated across seven PowerShell modules were in Portuguese.
1.0.0b1 and 1.0.0b2 stay in the history. Neither is withdrawn and neither number is reused.
Install
pip install m365-governance-as-code==1.0.0b3
1.0.0b2
The release whose project page is right. Nothing in the engine changed.
1.0.0b1 proved the publishing path end to end, and in doing so showed what the path had not checked: the README is what PyPI renders as the project description, and it was written for GitHub.
- The banner used a relative path. GitHub resolves it against the repository; PyPI has none, so the page opened with a broken image where the product's name belongs.
- Eighteen more links pointed at
CONTRIBUTING,LICENSE,CHANGELOGand ten documents underdocs/by relative path. Every one of them was dead on the project page. pip install m365-governance-as-coderesolves to nothing while the only version is a pre-release, and that was the first command anybody was given.- The README claimed
doctorprints where the packaged fixtures live. It does not, so the quick start reachedevaluate --evidence <a file>with no file.
A release description on PyPI is frozen at upload, so correcting the files could not correct the published page. This version is what publishes the corrected one.
Also added: badges for the published version, the Python versions CI runs the suite against, the licence, the state of main, the documentation, the current release and the DCO requirement, each with a line saying what it reports and what it does not. And docs/RELEASING.md, which writes down the publishing procedure and the two things that cannot be fixed after an upload.
Install
pip install m365-governance-as-code==1.0.0b2
The == is not optional: 1.0.0b2 is a pre-release, and pip skips those unless a version is pinned or --pre is given.
1.0.0b1
The first public build. A beta, and the version number says so: this is the release that proves the whole path from a tag to pip install, and everything in it has been run rather than described.
What it is
Microsoft 365 governance checks that show their work. Every rule declares what kind of truth it is, and missing evidence is never a pass.
- 20 rules, each declaring its
basis: a limit Microsoft publishes, guidance Microsoft gives, a convention, or an opinion. A report that mixes those is a report that loses the argument it should win. - 10 collection slices, read only. Nothing under the collector tree calls a mutating cmdlet, and CI proves it by parsing every file in the tree.
- 13 commands, from
collectthroughassesstoverify, so somebody who receives an assessment can check it without this engine and without trusting whoever sent it. unknownis a result, not a pass. Missing evidence is a fact about collection, not about the resource, and the report says so in those words.
Install
pip install m365-governance-as-code==1.0.0b1
The == is not optional yet: 1.0.0b1 is a pre-release, and pip skips those unless asked.
Python 3.11 or later. PowerShell 7 and PnP.PowerShell are needed only to collect from a tenant; the engine, the rules and the tests run offline against packaged fixtures.
Documentation
The manual is at https://ph7x.com/tools/m365-governance-as-code/docs/ and covers installation, reading a report, every collector, the rule model, every command, the architecture and the trust model.
Known boundaries
Stated here because a governance tool that implies coverage it does not have is worse than one that says nothing. This release does not detect SharePoint Add-in authentication, ACS principals, SharePoint 2013 workflows, or InfoPath forms. Those remain manual discovery.