Security
Microsoft 365 audit-checklist: voor je een inbox-agent live zet
Voor we een agent koppelen aan een tenant draaien we vier uur audit: twaalf service principals, dertig shared mailboxes, één Graph-tokenrotatie, AVG-spoor intact.

Het is 09:00 op een dinsdag en de operations lead van een Nederlands logistiek bedrijf met 180 man hangt op een Teams-call met de vraag wanneer we de inbox-triage-agent kunnen uitrollen. Het contract is getekend. De mailboxes zijn in kaart. Het shared service-account is aangemaakt. En wij zeggen: nog niet. We hebben eerst vier uur met de tenant nodig.
Elke Microsoft 365-retrofit die we offreren begint met dezelfde audit. Hij is niet optioneel en geen verkoopstap. Het is de enige manier om te weten wat de agent straks daadwerkelijk mag, wat hij achterlaat in de audit log, en wat er stilletjes breekt zodra een delegated token roteert. Hieronder de versie van de checklist die we medio 2026 draaien, na veertien agents in productie en één bijna-incident bij een tenant met eenendertig shared mailboxes waar audit logging stilletjes uit stond.
Twaalf service principals, gescoord op blast radius
Elke tenant heeft honderden service principals. De meeste zijn slapende first-party apps die Microsoft tijdens provisioning heeft geïnstalleerd. De twaalf die ertoe doen zijn die met actieve sign-ins in de laatste dertig dagen en minstens één tenant-brede Graph-permissie. We rangschikken ze op blast radius: als het client secret van deze app op zaterdag om 02:00 uitlekt, wat kan hij dan lezen of versturen voor iemand het maandag merkt?
Connect-MgGraph -Scopes "Application.Read.All","AuditLog.Read.All"
Get-MgServicePrincipal -All -Filter "servicePrincipalType eq 'Application'" |
Where-Object { $_.SignInActivity.LastSignInDateTime -gt (Get-Date).AddDays(-30) } |
ForEach-Object {
$assignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $_.Id
[PSCustomObject]@{
DisplayName = $_.DisplayName
AppId = $_.AppId
Roles = ($assignments.AppRoleId -join ',')
LastSignIn = $_.SignInActivity.LastSignInDateTime
}
} | Sort-Object LastSignIn -Descending | Select-Object -First 12 |
Export-Csv ./sp-top12.csv -NoTypeInformation
De scoring is simpel. Een 1 betekent dat de app Mail.Read of User.Read.All heeft: vervelend, maar afgebakend. Een 3 betekent Mail.ReadWrite of Calendars.ReadWrite.All. Een 5 betekent dat de app mail kan sturen namens gebruikers of de hele directory kan schrijven: Mail.Send, Group.ReadWrite.All, Directory.ReadWrite.All. Alles met een 4 of hoger zonder gekoppeld conditional access-policy gaat in het rood. In acht van de laatste tien audits scoorden minstens twee van de twaalf een 4 of hoger zonder enig policy erop.
Mailbox-audit-retentie op de top dertig shared inboxes
Shared mailboxes zijn waar de agent gaat wonen. Het is ook waar de Microsoft-defaults je in de steek laten. Mailbox audit logging staat sinds 2019 standaard aan voor user mailboxes, maar shared mailboxes erven andere defaults, en tenants die zijn gemigreerd vanuit on-prem Exchange of oudere E1-plannen hebben het vaak uit staan. De Microsoft-documentatie over mailbox audit logging is duidelijk genoeg, maar de defaults gaan je niet redden.
We trekken de top dertig shared mailboxes op item count en checken drie flags: AuditEnabled, AuditLogAgeLimit, en welke operations worden vastgelegd voor het Delegate-logontype. De agent draait als Delegate. Als MailItemsAccessed niet in de audit set staat voor dat logontype, kun je achteraf niet reconstrueren wat de agent heeft gelezen. Onder de AVG is dat het verschil tussen een uitlegbare disclosure en een melding aan de Autoriteit Persoonsgegevens.
Get-EXOMailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited |
ForEach-Object {
$stats = Get-EXOMailboxStatistics -Identity $_.UserPrincipalName
[PSCustomObject]@{
Mailbox = $_.UserPrincipalName
ItemCount = $stats.ItemCount
AuditEnabled = $_.AuditEnabled
AuditLogAgeLimit = $_.AuditLogAgeLimit
DelegateOps = ($_.AuditDelegate -join ',')
}
} | Sort-Object ItemCount -Descending | Select-Object -First 30 |
Export-Csv ./mailbox-audit.csv -NoTypeInformation
De standaard AuditLogAgeLimit is 90 dagen. We zetten 'm op 365 voor elke mailbox die de agent aanraakt, langer als de klant een fiscale bewaarplicht heeft. Onder de Algemene wet inzake rijksbelastingen betekent dat zeven jaar voor alles wat een factuur raakt.
Conditional access-gaten die specifiek agents pakken
Conditional access voor workload identities werd in 2022 algemeen beschikbaar, maar vier jaar later zit de adoptie binnen ons MKB-portfolio nog steeds onder de helft. Het meest voorkomende gat is een tenant met een fraaie CA-stack voor mensen (MFA, compliant device, named locations) en helemaal niets gericht op service principals. Het secret van de agent is dan gewoon een string in een key vault die elke gecompromitteerde admin-sessie kan exfiltreren. De Microsoft-referentie over conditional access voor workload identities is het juiste startpunt als je tenant er geen heeft.
Drie policies zijn verplicht voor we live gaan. Eén: blokkeer de app van de agent voor elk IP buiten onze deployment-ranges en de kantoorranges van de klant; de agent heeft niets te zoeken bij Graph vanuit een koffiebar. Twee: vereis token protection op elke sessie met Mail.ReadWrite, zodat een gestolen refresh token niet vanaf een ander apparaat te replayen is. Drie: blokkeer legacy authentication expliciet op de service principal. Het tenant-brede policy dat legacy auth blokkeert voor mensen cascadeert niet naar workload identities. Die twee worden los gescoped, en dat heeft ons in vier tenants op rij verrast.
De drill voor het roteren van het delegated Graph-token
Dit is de test die bepaalt of een agent op schema live gaat of twee weken slipt. We kiezen drie afdelingen, meestal Sales, Finance en Operations, en stellen de vraag: als we het delegated Graph-token van het service-account van de agent op dinsdag om 09:00 roteren, welk afdelings-mailspoor breekt?
Het antwoord moet "geen" zijn. Het werkelijke antwoord, in de eerste audit die we dit jaar deden, was: "Finance, omdat de invoice-chase-macro die iemand in 2019 aan Outlook had vastgeschroefd ook een delegated token onder dezelfde UPN vasthield, en al acht maanden stilletjes faalde om naar de shared journal te schrijven zonder dat iemand het merkte."
De drill zelf: roteer het token eerst in een dev-tenant-clone, loop dan per afdeling de bekende automation-surface langs en bevestig dat elke write naar het audit-relevante spoor nog steeds aankomt. Korte versie van onze runbook: niets wat klant-correspondentie raakt mag afhangen van één token waarvan geen mens het rotatieschema beheert.
AVG en het spoor van klant-correspondentie
De Algemene Verordening Gegevensbescherming is niet subtiel over audit trails voor klant-correspondentie. Als jouw agent een mail van een klant leest, classificeert of beantwoordt, moet je kunnen reconstrueren wat hij deed en waarom, en de betrokkene kan je om die reconstructie vragen. Onder artikel 30 heb je een register van verwerkingsactiviteiten nodig; onder artikel 32 de bijbehorende technische en organisatorische maatregelen. De Autoriteit Persoonsgegevens is consistent geweest dat "we zijn de log kwijt" geen verweer is.
Concreet: als de agent een klantmail leest en besluit niet te escaleren, is die beslissing een verwerking. Wat we minimaal vastleggen: timestamp, mailbox, message ID, beslissing van de agent, model-versie en de policy-versie die de beslissing produceerde. Dat schrijven we naar een append-only log buiten de M365-tenant. Een Postgres-tabel met een write-once-rol werkt. Een S3 bucket met object lock werkt beter.
Als het enige audit-spoor van wat je agent deed in dezelfde tenant zit waar de agent schrijfrechten heeft, heb je geen audit-spoor. Je hebt een suggestie.
De daadwerkelijke vier-uurs-checklist
Hier is de checklist in de volgorde waarin we hem draaien. Elk punt levert een CSV- of JSON-artefact op dat in de engagement-map gaat; niets wordt afgevinkt voor het artefact bestaat.
- Tenant-inventaris: actieve licenties, MFA-handhaving, named global admins, break-glass accounts bevestigd en getest.
- Service principal-telling: top twaalf op recente sign-in, gescoord 1 tot 5 op blast radius.
- Conditional access-gap-rapport specifiek voor workload identities.
- Audit-posture van de top dertig shared mailboxes, zoals de CSV hierboven.
- Tokenrotatie-droogloop in een dev-tenant-clone voor drie afdelingen.
- Concept-entry in het verwerkingsregister voor de betreffende agent.
- Append-only beslissingslog geprovisioneerd en end-to-end getest.
- Schriftelijk akkoord van de FG van de klant, of bij ontbreken, van de operations lead.
Het kost vier uur senior consultant-tijd als de admin van de klant Global Reader en Security Reader vooraf afgeeft. Twee dagen kalendertijd als toegang nog ingepland moet worden. Zonder dat offreren we geen agent-werk.
Toen we vorig kwartaal de inbox-triage-agent bouwden voor een Nederlandse groothandel met 90 man, liepen we vast op punt vijf: de rotatiedrill brak hun invoice-journal in Finance op een manier waar sinds 2020 niemand meer aan had gezeten. We hebben dat journal herbouwd als kleine service die zijn eigen credentials beheert en via een geteste interface schrijft, en de agent ging een week later live in plaats van drie maanden later, wanneer het journal anders in productie was geklapt. Dat soort pre-flight is grotendeels waar een AI-agent van leeft als hij contact maakt met een echte tenant.
Het kleinste dat je vandaag kunt doen: draai de one-liner voor de mailbox-audit tegen je eigen tenant en tel hoeveel van je top dertig shared mailboxes AuditEnabled op true hebben staan. Zit dat aantal onder vijfentwintig, dan heb je een project.
Kern
Voor een inbox-agent een tenant aanraakt: scoor twaalf service principals, audit dertig shared mailboxes en doe een droogloop van een Graph-tokenrotatie.
FAQ
Moet ik deze audit draaien als mijn Microsoft 365-tenant gloednieuw is?
Ja. Standaardinstellingen op nieuwe tenants zijn afgestemd op gebruiksvriendelijkheid, niet op het hosten van een autonome agent die klantmail leest. De eerste audit op een verse tenant levert meestal drie tot vijf gaten op.
Hoe lang duurt de checklist end-to-end?
Vier uur senior consultant-tijd als de admin van de klant Global Reader en Security Reader vooraf afgeeft. Twee dagen kalendertijd als toegang nog ingepland moet worden. Zonder dat offreren we geen agent-werk.
Kan de agent zelf de audit draaien?
Nee. De audit heeft leestoegang nodig tot tenant-brede directory- en security-data die je nooit aan een automation-account zou moeten geven. Een menselijke admin draait hem, in een sessie die wordt ingetrokken zodra de audit klaar is.
Wat als de M365-admin van de klant een externe MSP is?
Komt vaak voor in deze grootte. We trekken de MSP de engagement in en vragen lees-Global Reader plus Security Reader voor het audit-window. Weigeren ze, dan is dat zelf al een bevinding en gaat het in het rapport.