Microsoft 365 tenant health audit · Built for MSPs
A tenant audit your client can actually read.
Point me at one client tenant and you get back a branded report and a 0–100 health score, three calendar days later. Read-only throughout — nothing in the tenant changes, and the report goes out under your name, not mine. 8,000 DKK per tenant, fixed.
- 8,000 DKK
- fixed fee, per tenant
- 3 days
- calendar, start to delivered report
- DA / EN
- report language, your choice
PS> .\Invoke-FTTenantHealthScore.ps1 `
-TenantId contoso.onmicrosoft.com
==> Authenticating
[OK] Connected app-only (certificate).
==> Running audits
[OK] Identity and MFA, access policy, applications, external access, licence hygiene.
==> Results
Identity and MFA 27 / 30
Access policy 16 / 20
Applications 14 / 20
External access 12 / 15
Licence hygiene 11 / 15
Tenant health score 80 / 100
[OK] HTML: .\reports\tenant-health-score.html
[OK] CSV: .\reports\tenant-health-score.csv
Actual console output. Figures illustrative.
Read-only. Nothing was changed in the tenant.
The audit
What it is, and what you get back
One client tenant, nominated by you. I run the audit read-only against Microsoft Graph, and three calendar days later you get the report back branded as yours, ready to forward.
-
A tenant health score, 0–100. Five weighted dimensions — identity and MFA, access policy, applications, external access, licence hygiene — with the arithmetic shown. Every deduction carries its own point value and its own explanation, so you can defend the number in front of a sceptical client.
-
The four audit reports the score is built from. Who is signing in with only a password. What your non-human identities can do. Which external guests still have access. What licence spend is going nowhere, in kroner per month and per year.
-
Branded as yours. Your logo, your colours, your company name on every page. Contrast is derived automatically, so a light brand colour still produces a readable report.
-
Self-contained HTML, plus the CSVs. No CDN, no remote fonts, no tracking pixel. Safe to forward, and it opens straight into Word if the client wants a document.
-
Danish or English. Report text, column headings and status values, all of it. Nothing is machine-translated: a string without a proper translation stays in English rather than becoming bad Danish.
-
Honest gaps. Where a signal cannot be read — no Entra ID P1 or P2, a permission not granted — that dimension is marked not assessed and dropped from the calculation rather than guessed at. The report says how much of the score was actually assessable.
- 8,000 DKK per tenant, fixed. Excluding VAT. Not an hourly rate and not an estimate.
- Three calendar days from the day we start.
- Read-only. The audit uses only the read-only scripts. Nothing in the tenant is created, changed or deleted.
- I run three audits at a time, so if the next slot is a week out I will tell you that rather than take the booking and be late.
This is an audit, not a remediation project and not ongoing monitoring. You get the findings and the evidence; fixing them is your team's work, or a separate conversation.
The first three
The first three MSPs are design partners
There are no testimonials on this page because there are no customers yet. Rather than pad it with stock quotes, here is the straight version: the audit is built and tested, and the first three MSPs to take it up get it free or heavily discounted, delivered same day or next day instead of in three.
What I want back is thirty minutes on the phone once you have read the report — which findings mattered to you, which ones your client would care about, and what you would want watched continuously rather than audited once. Being early is the deal, and I think it is a fair one.
Take a design-partner slotThree places. Two still open.
How it works
You grant the access. You can revoke it.
I never ask for an admin account, and I do not create anything in your tenant. You run one setup script yourself, hand me a read-only credential, and take it back whenever you like.
-
01
You run the setup script
One script, run once against the tenant you are nominating, by a Global Administrator on your side. It creates an app registration, adds only the Microsoft Graph permissions the audit reads, and grants consent. Permission names are resolved from your own tenant rather than hardcoded, and there is a switch that leaves out the Exchange administrator role entirely — use it, because the audit does not need it.
-
02
You upload my certificate — there is no credential to send me
I generate the key pair on my own machine and send you nothing but the public half: one certificate file, which is not secret and which cannot authenticate as anything on its own. You pass that file to the setup script when you run it, and it gets uploaded to the app registration. Send me back the tenant and application IDs, which are identifiers rather than secrets, and that is the whole handover.
The private key never leaves my machine, so there is nothing sensitive in either direction and nothing for you to store, escrow or remember you sent. The app registration it belongs to cannot write to your tenant, cannot delete groups and cannot assign directory roles, because none of the audit needs any of that. -
03
You revoke it when we are done
The app registration lives in your tenant, not mine. Delete it and my access is gone the same minute — there is no account of mine to disable, no licence to reclaim and nothing to ask me to return. You do not have to trust that I stopped looking; you can make it so.
Total time on your side: the setup script, and half an hour on the phone at the end to walk through what I found.
You grant it, you can cut it, and I never hold anything you gave me.
Why you can let this near a client tenant
The audit cannot change anything
The audit uses only the read-only scripts. The one script in the library that can write — offboarding — is not part of it and is not run. There is nothing to roll back because nothing is created, changed or deleted.
Least privilege
The app registration requests only what the scripts use — not the broad
Directory.ReadWrite.All that
would cover everything. It cannot delete groups or assign directory
roles, because nothing here needs to.
A number you can defend
Every point deducted is recorded as its own line with its own explanation. Where data could not be collected the dimension is dropped and the weights renormalised, so a partly assessed tenant gets an honest score over a smaller weight rather than a flattering one over a full weight.
You can read what it runs
Plain PowerShell with full comment-based help. Review it before you run it — no compiled binaries, no black box, no phoning home.
The access is yours to cut
The app registration is created in your tenant by you, and it is yours. Delete it and my access stops. I never hold an account in your tenant and there is no standing access to forget about.
Every script was run against real Microsoft 365 tenants, not just syntax-checked. That testing caught defects static analysis missed — including one that produced a CSV successfully while silently failing to write the HTML report, and one where the same CSV was read correctly by Excel on one PowerShell version and mangled on another.
The deliverable
The report is the product
This is what arrives three days later, with your logo on it. Headline figures first, technical detail underneath, and every number traceable to the finding that produced it.
-
Self-contained HTML. No CDN, no remote fonts, no tracking. Safe to email, and it opens straight into Word if a client wants a document.
-
Written for the person paying. Headline figures first, technical detail underneath. Not a CSV dump with a header.
-
Figures it can stand behind. Where data is missing, the report says so and leaves it out of the totals rather than guessing. A number you cannot defend in a meeting is worse than a smaller one.
-
Optional AI summary. Two or three plain-language paragraphs for the front page, written in Danish if the report is. Only aggregate statistics are sent — never names, addresses or IDs.
-
Danish, when the client is Danish. Report text, column headers and status values are translated, set per client. Nothing is machine-translated at run time: a string without a translation stays in English rather than becoming bad Danish.
Frydensberg Tech
Stale User Licence Audit
Contoso A/S — 90 day threshold — 248 accounts
31
Stale
12
Never used
9,240
DKK / month
Read-only report. No changes were made to the tenant.
What the audit looks at
Five audits behind one number
The score is not an opinion. It is built from these audits, run against the tenant you nominate, and each one ships with the report so the client can see the evidence.
-
01
Read only
Stale User Licence Audit
Finds licensed accounts nobody is using and totals what they cost — in kroner, per month and per year. Answers the only licence question a client ever asks.
-
02
Read only
Guest User Cleanup
Every external guest, what they can still reach, and which ones are orphaned — invitations never accepted, access nobody remembers granting.
-
03
Read only
MFA Status Report
Who has MFA, which method, and how it is enforced. Grades SMS as phishable rather than counting it as compliance, and flags any administrator without a strong method first.
-
04
Read only
Conditional Access Documenter
Exports every policy with the GUIDs resolved into real names, and flags the dangerous ones — policies with no break-glass exclusion, or sitting in report-only mode enforcing nothing.
-
05
Read only
App Permission Auditor
Every enterprise app and what it can actually do. Grades permissions by what they enable — an app that can grant itself more permissions is treated as critical, because it is.
How this stays consistent across your whole client list
Multi-tenant runner
One command runs the reports against every tenant in your client list and produces a single roll-up: who failed, who has the most findings, and one click through to the report that explains why. A client that fails doesn't stop the run.
Tenant health score
One 0–100 figure per client, with the arithmetic shown. Every deduction carries its own point value and its own explanation, so you can defend the number to a sceptical client. Some findings cap the score outright, whatever else is tidy.
White-label reports
Your logo, your colours, your name — set up in one file. Contrast is derived automatically, so a light brand colour still produces a readable report. Can be set per client where one insists on their own identity.
Danish output
Reports can be delivered in Danish, set per client — so Danish clients get Danish and everyone else doesn't. Nothing is machine-translated: a string without a translation stays in English rather than becoming bad Danish.
Also in the library: offboarding
Not part of the audit
Not part of the audit. The full leaver process in one command —
block sign-in, revoke sessions, convert the mailbox to shared,
then release the licence, in that order so the mail survives.
It is the only script that writes, it supports
-WhatIf, and it
demands a typed confirmation. Ask me about it separately.
Who this is for
Primary
Managed service providers
You run ten, forty, a hundred tenants and you have to justify the retainer every quarter. These reports are the quarterly review — evidence of waste removed and risk closed, in your client's language.
- →App-only certificate auth, unattended across every tenant
- →One command sets up a new client tenant
Also
Internal IT
One tenant, and a finance director who wants to know why the Microsoft bill keeps climbing. Same reports, pointed inward — spend you can defend and a security posture you can evidence.
- →Licence spend in kroner, not seat counts
- →Audit-ready MFA and Conditional Access evidence
- →Runs on Windows PowerShell 5.1 or PowerShell 7
Price
8,000 DKK per tenant, fixed
Excluding VAT. Not an hourly rate and not an estimate.
- Three calendar days from the day we start.
- Read-only. The audit uses only the read-only scripts. Nothing in the tenant is created, changed or deleted.
- I run three audits at a time, so if the next slot is a week out I will tell you that rather than take the booking and be late.
Questions
What can you actually see, and what can you change? +
Nothing. The audit uses only the read-only scripts, and the app
registration you create for me holds only the Microsoft Graph
permissions those scripts read from. It cannot create, change or
delete anything, it cannot delete groups and it cannot assign
directory roles — not because I promise not to, but because the
permissions to do it were never granted. The one script in the
library that can write to a tenant is the offboarding script, and
it is not part of the audit.
You create that app registration yourself, in your own tenant, by
running one setup script as a Global Administrator. I never
receive an admin account. When we are finished you delete the app
registration and my access ends with it.
What permissions does it need? +
Only what the audit actually uses. A setup script creates the app registration, uploads a certificate and grants consent in one command. It deliberately does not request permission to delete groups or assign directory roles, because nothing in the audit needs it. Each script documents its own required permissions in its help block.
Do I need Entra ID P1 or P2? +
For full results, yes — sign-in activity and Conditional Access are premium features. Without them the scripts do not fail: they report everything still available, mark the rest as unknown, exclude unknowns from the totals, and say so plainly in the report. A tenant running Security Defaults is reported as having a valid baseline, not as unprotected.
Where does the tenant data go? +
The scripts talk to Microsoft Graph and write their output as
files. There is no telemetry, no analytics and nothing that phones
home — the reports are self-contained HTML with no CDN, no remote
fonts and no tracking pixel, which is also why they are safe for
you to forward.
The one exception is opt-in and off by default: if you want the
plain-language executive summary on the front page, aggregate
figures — counts and totals only — are sent to the Claude API.
Names, email addresses and object IDs are actively blocked from
that call. If you would rather it were not used at all, say so and
it will not be.
⚑ SIMON — one sentence is missing here and I will not invent it: where the report files and your credential live on Simon's side while the audit runs, and how long they are kept afterwards.
How much work is this on our side? +
Running one setup script, and half an hour on the phone at the end while I walk you through what I found. The script has to be run by a Global Administrator, because granting consent requires one — that is the only elevated thing about the whole engagement, it happens once, and it happens on your machine rather than mine.
Will the setup itself show up as a finding? +
Yes, and I would rather tell you now than have you find it in the
report.
Signing in to run the setup script consents three write
permissions to Microsoft's own Microsoft Graph Command
Line Tools application, and those grants stay behind
after setup finishes. Nothing in the script can avoid it — it is
how the Graph PowerShell SDK authenticates. My own App Permission
Auditor and health score will then flag that application as
critical.
That is a true finding, not a false positive: whoever can sign in
as that administrator can use those permissions again at any
time. It is also easy to clear once setup is done —
Entra admin centre → Enterprise applications → Microsoft
Graph Command Line Tools → Permissions → Review permissions →
revoke. Doing that will prompt the next admin who runs
Connect-MgGraph in the
tenant to consent again, which is the correct trade.
The report names it where it appears, so nobody has to work out
where it came from.
Which features are shipping and which are roadmap? +
The audit is what I sell today, and everything it produces is built and has been run against real tenants. What I am building toward is a service you would log into rather than a set of reports I hand over — that does not exist yet, there is nothing to sign up for, and the early audits are what shape it. Ask me where it stands before you assume a date.
Can we see how the audit is produced? +
Yes, and you should. It is plain PowerShell delivered through a private Git repository — nothing compiled, nothing obfuscated. Every function carries full comment-based help. You are pointing this at your clients' tenants; verify it rather than trust it.
Do you take on project work? +
Yes — Microsoft 365 and Entra ID projects, tenant migrations, security baselines, and AI automation work. Use the second form below and tell me what you are trying to do.
Get in touch
Primary
Book a tenant health audit
Tell me roughly how many client tenants you run and which one you would point me at first. I will come back with a start date — I run three audits at a time, so I will give you a real date rather than take the booking and be late.
The roadmap
What would you want watched?
The audit is a snapshot. What I am building toward is something that watches continuously — so if there is a thing you check by hand every month and wish you did not, tell me. Good suggestions shape what gets built, with credit.
Also available
Project & freelance work
Microsoft 365 and Entra ID projects, tenant migrations, security baselines, and AI automation. Tell me the problem rather than the solution and I will say whether I am the right person.