Webapplicatie (sherlog.nl) die Microsoft Intune Management Extension (IME) logbestanden analyseert en het resultaat als HTML-timelinerapport in de browser toont.
De homepage biedt het Diagnostics Package (/diagnostics) en de
CMTrace Viewer (/cmtrace). Je uploadt een diagnostics-.zip (→ device
health + analyse) of losse .log-bestanden (→ CMTrace-viewer), bijvoorbeeld uit
C:\ProgramData\Microsoft\IntuneManagementExtension\Logs of een Intune
"Collect Diagnostics"-export. De timeline-analyse is geen losse tool meer —
die draait automatisch op de IME-logs binnen een diagnostics-pakket: de
server draait het analysescript headless en toont het rapport bij
/result/<id>/timeline.
Eén container, twee lagen:
- Weblaag — Python 3 + FastAPI + uvicorn op poort
8080 - Analyse-engine — PowerShell Core (
pwsh) die het scriptGet-IntuneManagementExtensionDiagnostics.ps1aanroept
De state staat volledig op het bestandssysteem (/data): geen database, geen
Redis.
Boven het timeline-rapport toont de resultpagina een inklapbaar
samenvattingspaneel: aantallen geslaagde/mislukte installaties per type
(Win32App, PowerShell-script, remediation), mislukte items, herkende
foutcodes met uitleg en downloadstatistieken per app. De samenvatting wordt
na de analyse uit het rapport afgeleid (summary.json in de jobmap).
Naast het timeline-rapport biedt de app een CMTrace-logviewer: bekijk de ruwe
geüploade .log-bestanden in een gekleurde tabel (warnings geel, errors rood) met
tekst- en componentfilter — een web-equivalent van het Windows-only CMTrace.exe.
Bereikbaar als eigen tool via de uploadpagina /cmtrace (geen analyse nodig) én
via "Raw logs (CMTrace)" op de rapportpagina. De (untrusted) loginhoud wordt in
een sandboxed iframe geserveerd.
De Diagnostics Package-tool neemt de zip die
Collect-IntuneDiagnostics.ps1 op een device produceert (IntuneDiag-*.zip)
en biedt drie dingen in één resultaatpagina:
- Diagnose-dashboard — health checks uit het pakket: Entra join- en
PRT-status (
dsregcmd), MDM-enrollment-URL, IME-servicestatus, bereikbaarheid van de Intune/Entra-endpoints en verlopen machinecertificaten. Ontbreekt een bronbestand, dan toont de check "unknown" (grijs) in plaats van een fout. - Automatische timeline-analyse — op de IME-logs in het pakket
(
Apps-IME\Logs) draait de timeline-analyse (alleen hier beschikbaar); het rapport en het samenvattingspaneel verschijnen zodra de analyse klaar is. - File browser — alle bestanden in het pakket zijn direct te bekijken:
.login de CMTrace-viewer, tekstbestanden (.txt,.reg,.xml, …) met UTF-16-detectie,.htmlin een sandboxed frame en.evtxin een eventviewer (tijd, event-ID, level, provider; gecapt opEVTX_MAX_EVENTS)..cab-archieven (o.a. DefenderMpSupportFiles.cab) worden metcabextractuitgepakt en de inhoud is per type te bekijken; binaire.etl-bestanden worden niet uitgepakt maar wel (grijs) in de bestandsboom getoond.
Recente uploads worden alleen in je eigen browser bewaard (localStorage) —
niet op de server, geen cookies of login. De lijst staat op de homepage en de
uploadpagina's; jobs die de server heeft opgeruimd (na JOB_RETENTION_HOURS)
verdwijnen er automatisch uit.
De homepage heeft een drag-&-drop dropzone (een .zip → Diagnostics, losse
.log-bestanden → CMTrace-viewer) en icon-tegels naar de tools. De
geanonimiseerde voorbeeldlogs uit testdata/ worden door de tests gebruikt.
Het analysescript Get-IntuneManagementExtensionDiagnostics.ps1 is gemaakt door
Petri Paavola en is hier integraal opgenomen:
https://github.com/petripaavola/Get-IntuneManagementExtensionDiagnostics
Het script is origineel voor Windows geschreven. Voor headless gebruik op PowerShell Core / Linux zijn minimale compatibiliteitspatches aangebracht. Elke wijziging is gedocumenteerd in PATCHES.md, zodat upstream-updates later opnieuw gemerged kunnen worden. Het analysegedrag en het rapportformaat zijn ongewijzigd.
Vereist: Docker met Compose.
docker compose up --buildOpen daarna http://localhost:8080. De homepage laat je een diagnostics-pakket of losse logs uploaden (Diagnostics / CMTrace Viewer). De app draait standaard als publieke tool zonder login: iedereen kan logs uploaden en het rapport bekijken. Optioneel kun je er basic auth voor zetten (zie hieronder).
Alle configuratie loopt via environment variables met veilige defaults:
| Variabele | Default | Betekenis |
|---|---|---|
MAX_UPLOAD_MB |
100 |
Maximale totale uploadgrootte per analyse (MB). Wordt streaming afgedwongen. |
JOB_RETENTION_HOURS |
24 |
Jobmappen (logs + rapport) ouder dan dit worden automatisch verwijderd. |
SCRIPT_TIMEOUT_SECONDS |
300 |
Timeout voor het analyse-subprocess. Bij overschrijding wordt de job als failed gemarkeerd. |
JOB_CONCURRENCY |
2 |
Maximum aantal analyses dat tegelijk draait. Extra jobs wachten in de wachtrij. |
CMTRACE_MAX_LINES |
50000 |
Maximum aantal regels dat de CMTrace-logviewer per bestand rendert. |
MAX_LOCAL_JOBS |
200 |
Globale rem op het aantal interactieve (web-form) jobs op schijf; daarboven 429. Begrenst de ongeauthenticeerde uploadroutes zodat de schijf niet volloopt (drop-off heeft zijn eigen cap). |
EVTX_MAX_EVENTS |
2000 |
Maximum aantal events dat de eventviewer per .evtx-bestand parst en rendert. |
LONG_SCRIPT_THRESHOLD_SECONDS |
180 |
PowerShell-scripts die langer draaien dan dit worden in de timeline als waarschuwing gemarkeerd. |
APP_USER |
(leeg) | Optionele gebruikersnaam voor basic auth. |
APP_PASSWORD |
(leeg) | Optioneel wachtwoord voor basic auth. |
GRAPH_TENANT_ID |
(leeg) | Zet samen met GRAPH_CLIENT_ID/GRAPH_CLIENT_SECRET aan: verrijkt de RSOP-settingtabel met de vriendelijke Intune-settingnaam uit de Microsoft Graph settings-catalog. |
GRAPH_CLIENT_ID |
(leeg) | App-registratie client-id (scope DeviceManagementConfiguration.Read.All, app-permission). |
GRAPH_CLIENT_SECRET |
(leeg) | Client secret van bovenstaande app-registratie. |
CSP_NAMES_CACHE |
<JOBS_DIR>/../csp-names.json |
Cachebestand voor de (tenant-onafhankelijke) catalog. Mag ook vooraf gegenereerd worden. |
CSP_NAMES_TTL_HOURS |
720 |
Maximale leeftijd van de cache voordat de catalog opnieuw wordt opgehaald. |
ENABLE_UPLOAD_API |
(uit) | Zet de device drop-off API (/api/diagnostics) + /inbox aan. Default uit. Drop-off packages gebruiken dezelfde JOBS_DIR en JOB_RETENTION_HOURS als alle andere logs. |
UPLOAD_TOKEN_MIN_LEN |
24 |
Minimale lengte van een (zelfgekozen) upload-token. |
UPLOAD_API_MAX_JOBS |
2000 |
Globale rem op het aantal drop-off-jobs (tegen disk-misbruik); daarboven 429. |
UPLOAD_API_MAX_JOBS_PER_TOKEN |
200 |
Per-inbox rem op het aantal drop-off-jobs per token; daarboven 429. Voorkomt dat één token de globale cap vult. |
De Graph-verrijking is optioneel en uit by default: zonder de drie GRAPH_*
vars doet de app geen externe call en blijft de RSOP-tabel zoals hij is (OMA-URI +
Learn-link). De catalog bevat alleen globale Microsoft-metadata (geen logdata) en
wordt één keer bij het starten opgehaald en gecached.
De app is standaard zonder login (publiek). Basic auth is optioneel: zet
beide APP_USER en APP_PASSWORD om de hele app achter een wachtwoord te
zetten. Zijn ze (allebei) leeg — de default — dan is de app open en logt hij
één waarschuwing bij het starten. /health valt altijd buiten auth.
Een Intune-beheerder kan de collector via Intune op een device draaien en de
logs automatisch naar Sherlog laten uploaden, om ze daarna in een inbox op de
site door te nemen. Zet hiervoor ENABLE_UPLOAD_API=1.
Self-service tokens. Het token is de namespace: genereer er één op
/inbox (knop Generate token) en bewaar het. Wie het token kent kan ermee
uploaden én alle bijbehorende packages bekijken: voer het in op /inbox (het
wordt in de request-body verstuurd, niet in de URL). Sherlog bewaart alleen
de sha256-hash van het token op elke job — nooit het token zelf — en houdt
geen token-register bij.
Token = secret. Het token reist nooit in de URL-query — niet bij upload (
X-Upload-Token-header) en niet bij het openen van de inbox (POST-body), dus het komt niet in access-logs, browsergeschiedenis of deRefererterecht. Behandel het toch als een wachtwoord: deel het niet en genereer een nieuw token als je vermoedt dat het is uitgelekt (oude uploads verlopen vanzelf na de retentie).
Uitrollen (aanbevolen: Remediation on-demand):
- Genereer een token op
<sherlog>/inbox. - Open
Remediate-CollectToSherlog.ps1, vul$SherlogBaseen$UploadTokenin. - Intune-admincenter → Devices → Scripts and remediations → custom script
package, Run in 64-bit PowerShell: Yes, logged-on credentials: No.
Plak
Remediate-CollectToSherlog.ps1als detection-script — Intune vereist een detection-script; dit ene script doet de collectie, dus een remediation-script is niet nodig (leeg laten). - Wijs toe aan een device-groep (de detection draait op schema), of selecteer
een device → Run remediation (on-demand). Draait als SYSTEM, verzamelt
het slimme
-Remote-profiel en POST't de zip. - Open
<sherlog>/inbox, voer je token in en klik de device-upload open.
Het detection-script staat ook kant-en-klaar (met je token al ingevuld) op de
/inbox-pagina nadat je een token genereert.
Direct vanaf de commandline kan ook:
.\Collect-IntuneDiagnostics.ps1 -Remote `
-UploadUrl 'https://sherlog.nl/api/diagnostics' -UploadToken '<token>'Anonimiseren (best-effort). Voeg -Anonymize toe (of zet de toggle aan op
/inbox) om tenant- en company-gegevens te redigeren: tenant-id/naam, domein(en),
UPN/e-mail, device- en username worden in alle tekstbestanden vervangen door
placeholders, en de zip-naam + upload-X-Device-Name worden geanonimiseerd. Dit
is best-effort, geen garantie: binaries (event logs .evtx, Defender .cab,
de geneste mdmdiag-zip) worden niet gescrubd en kunnen nog identifiers
bevatten — controleer het pakket vóór delen.
.\Collect-IntuneDiagnostics.ps1 -Remote -AnonymizeOpslag & retentie. Drop-off packages worden net als alle andere logs in
JOBS_DIR opgeslagen en na JOB_RETENTION_HOURS (default 24u) opgeruimd. Wil je
ze langer bewaren én over redeploys behouden: mount JOBS_DIR op een persistent
Coolify-volume en zet JOB_RETENTION_HOURS hoger (bijv. 720 voor 30 dagen).
Maprechten. De container draait als niet-root (uid 10001). De entrypoint
(scripts/docker-entrypoint.sh) chownt JOBS_DIR
bij het starten en dropt daarna naar die user, dus een root-owned Coolify-volume
werkt zonder handmatige chown. Wel nodig: het volume moet schrijfbaar zijn
voor root bij het starten (standaard zo).
Security & privacy. Diagnostics-packages bevatten vertrouwelijke gegevens
(IME-logs, identity, certificaten). Voor vertrouwelijke logs heeft een
self-hosted Sherlog de voorkeur boven het publieke sherlog.nl. Het token
is een device-secret (in het remediation-script leesbaar voor wie de policy kan
inzien). Jobs worden na JOB_RETENTION_HOURS (default 24 u) opgeruimd. Microsoft
adviseert geen persoonsgegevens via scripts te verzamelen — beoordeel zelf wat je
ophaalt.
Stap voor stap:
- Nieuwe Application aanmaken. Maak in Coolify een nieuwe Application aan en koppel deze repository als Git-source.
- Build Pack: Dockerfile. Kies build pack Dockerfile (de
Dockerfilestaat in de repo-root). Zet de exposed port op8080. - Persistent volume. Mount een persistent volume op
/data. Daar staan de jobmappen (/data/jobs/<uuid>/) met geüploade logs en gegenereerde rapporten. Zonder dit volume gaat de state verloren bij elke redeploy. - Environment variables. De app is publiek zonder login; laat
APP_USER/APP_PASSWORDleeg. Optioneel afstellen:MAX_UPLOAD_MB,JOB_RETENTION_HOURS,SCRIPT_TIMEOUT_SECONDS,JOB_CONCURRENCY. Wil je toch een wachtwoord, zet dan beide auth-vars. - Domein + HTTPS. Wijs het domein
sherlog.nltoe; de Coolify-proxy (Traefik) regelt automatisch HTTPS via Let's Encrypt. Forceer HTTPS-redirect. - Healthcheck. Configureer het healthcheck-pad op
/health(poort8080, geen auth). Dit endpoint geeft200terug en controleert ofpwshbeschikbaar is; ontbreektpwsh, dan503. - Resource limits. Aanbevolen: 1 CPU / 1–2 GB RAM. Het parsen van grote logbestanden is geheugenintensief; te krap zetten leidt tot OOM-kills tijdens de analyse.
De app is bedoeld als open, login-vrije tool. Wie de URL heeft kan logs uploaden en het rapport bekijken. Dat is een bewuste keuze — houd er wel rekening mee:
- Privacy-afweging. IME-logs bevatten gevoelige data (device-/gebruikers-
namen, app-GUID's, soms script-output). Zonder login vertrouw je op de
onraadbaarheid van de job-URL en op korte retentie. Wil je toch een drempel,
zet dan
APP_USER/APP_PASSWORD(basic auth over de hele app). - HTTPS afdwingen. Laat de Coolify-proxy HTTPS regelen en forceer een redirect van HTTP.
- Job-URL's = capability. Een job-id is een 128-bits
uuid4(niet te raden of op te sommen). Wie de link heeft, ziet het rapport — deel hem dus bewust. - Korte retentie. Houd
JOB_RETENTION_HOURSlaag; rapporten en geüploade logs worden na die periode automatisch verwijderd.
Beveiligingen die al in de code zitten (geen config nodig):
- Rapport-isolatie (XSS). Het rapport wordt opgebouwd uit loginhoud en is
dus niet te vertrouwen. De app serveert het in een
sandbox-iframe (/result/<id>/report) met eenContent-Security-Policy: sandbox-header, zodat kwaadaardige scripts in een geüploade log géén toegang krijgen tot de app-origin. Diezelfde sandbox-respons (en elke.htmluit een diagnostics- pakket) krijgt bovendiendefault-src 'none', zodat een kwaadaardig script de inhoud ook niet naar buiten kan exfiltreren. De app-pagina's sturen restrictieve security-headers (CSP,X-Content-Type-Options,Referrer-Policy,X-Frame-Options). - Concurrency-limiet.
JOB_CONCURRENCY(default 2) begrenst hoeveel analyses tegelijk draaien, zodat veel gelijktijdige uploads de container niet uitputten. Stem af op de toegewezen CPU/RAM. - Upload-validatie. Alleen
.log/.zip, harde groottelimiet (streaming), zip-slip- en zip-bom-bescherming. - Disk-limiet.
MAX_LOCAL_JOBS(default 200) begrenst hoeveel interactieve upload-jobs er tegelijk op schijf staan; daarboven krijgen nieuwe uploads429tot oude jobs verlopen. De drop-off API heeft zijn eigen caps (UPLOAD_API_MAX_JOBS,UPLOAD_API_MAX_JOBS_PER_TOKEN). Samen met korte retentie voorkomt dit dat anonieme uploads de schijf vullen.
- Geen LogViewerUI. De
-ShowLogViewerUI-modus van het script gebruiktOut-GridView(Windows-only) en wordt nooit aangeroepen. - Geen
-Online/ Graph in v1. De online-modus vereist Graph API-credentials en is bewust uitgeschakeld in deze versie. - Uploadgrootte. Maximaal
MAX_UPLOAD_MB(default 100 MB) per analyse; zips worden bovendien tegen zip-bombs en path-traversal (zip-slip) beschermd. - Retentie. Jobmappen worden na
JOB_RETENTION_HOURS(default 24 uur) automatisch verwijderd. Rapporten zijn dus tijdelijk; download wat je wilt bewaren. - Eenvoudige concurrency.
JOB_CONCURRENCYbegrenst het aantal parallelle analyses (extra jobs wachten), maar er is nog geen volwaardige, persistente job-queue — bij een herstart gaan wachtende/lopende jobs verloren.
- Job-queue voor gecontroleerde gelijktijdigheid in plaats van ongelimiteerd parallelle subprocessen.
- Rapporthistorie — een overzicht van eerdere analyses in plaats van losse job-URL's.
-Online-ondersteuning via een Entra app registration (Graph API), zodat app- en toewijzingsnamen verrijkt worden in het rapport.