M365-Kontakte auf dem Mac nach dem EWS-Aus

Plötzlich leer

Anfang Oktober waren auf meinem MacBook und meinem Mac Studio die Kontakte aus Microsoft 365 weg. Auf dem iPhone? Alles da. Der Grund ist schnell gefunden: Microsoft schaltet seit dem 1. Oktober 2026 Exchange Web Services (EWS) in Exchange Online ab, Tenant für Tenant. Am 1. April 2027 ist dann endgültig Schluss, ohne Hintertür.

Das Problem: Mail, Kalender und Kontakte auf dem Mac sprechen mit Exchange Online immer noch über genau dieses EWS. Angekündigt war das Ende seit Jahren. Apple hat die Umstellung auf Microsoft Graph für «ein künftiges Update von macOS 27» versprochen, ein Datum gibt es nicht. iPhone und iPad sind nicht betroffen, sie verwenden Exchange ActiveSync.

Ich wollte meine Kontakte aber jetzt auf dem Mac haben, nicht irgendwann. M365 bleibt dabei der Master, der Mac soll nur lesen.

Die naheliegenden Auswege – und warum ich sie nicht wollte

VarianteWas sie machtHaken
EWS im Tenant wieder erlaubenEWSEnabled + Allow-List für die Apple-AppLäuft am 1. April 2027 ab, egal was Apple bis dahin liefert
iPhone als BrückeOutlook für iOS schreibt die Kontakte nach iCloud, iCloud bringt sie auf den MacEin Handy als Sync-Server, Firmenkontakte in der privaten Cloud
Outlook für Mac oder OWAKontakte direkt in OutlookKein Zugriff aus Kontakte, Nachrichten, Telefon oder der Autovervollständigung

Alles Bastellösungen mit Ablaufdatum. Was ich wollte, ist simpel: Die Kontakte-App auf dem Mac spricht CardDAV, und das kann sie seit Jahren zuverlässig. Es fehlt nur jemand, der M365 in CardDAV übersetzt.

Die Lösung: m365-carddav

Also habe ich mir den Übersetzer gebaut: einen einzigen Docker-Container, der auf dem NAS läuft. Er holt die Kontakte per Microsoft Graph und stellt sie über einen eingebauten CardDAV-Server bereit.

Architektur: Microsoft 365 liefert per Graph an den Sync-Loop im Docker-Container, dieser schreibt vCards in Radicale, die Macs lesen per CardDAV über HTTPS
Architektur: Microsoft 365 liefert per Graph an den Sync-Loop im Docker-Container, dieser schreibt vCards in Radicale, die Macs lesen per CardDAV über HTTPS

Der Sync-Loop fragt alle 15 Minuten nur die Änderungen ab (Graph Delta Query) und schreibt sie als vCards in Radicale. Als Schlüssel dient die unveränderliche ID des Kontakts aus Exchange. Zieht jemand um, wird die ganze vCard neu geschrieben, M365 gewinnt immer. Einmal pro Nacht läuft zur Sicherheit ein vollständiger Abgleich.

Die Kontakte-App auf dem Mac merkt davon nichts. Für sie ist das ein gewöhnliches CardDAV-Konto, auch offline verfügbar dank lokaler Kopie.

Sicherheit: so wenig Rechte wie möglich

Eine App, die Kontakte aus Postfächern liest, ist ein heikles Ding. Darum ist das Berechtigungsmodell strikt:

  • Keine Berechtigung in Entra. Die App bekommt dort kein Contacts.Read und keinen Admin-Consent. Das würde sie für alle Postfächer im Tenant freischalten.
  • Exchange Online RBAC for Applications. Die Rolle Application Contacts.Read gilt nur für einen Management Scope: ein einzelnes Postfach oder die direkten Mitglieder einer mail-aktivierten Sicherheitsgruppe.
  • Zertifikat statt Client Secret. Der private Schlüssel verlässt den Docker-Host nie. Kein Refresh-Token, das bei MFA oder Passwortwechsel kaputtgeht.
  • Nur lesen. Auf dem Mac ist das Adressbuch schreibgeschützt, Radicale lehnt Änderungen ab. Jeder Benutzer sieht ausschliesslich sein eigenes Adressbuch.
  • Eigenes CardDAV-Passwort. Es ist nicht das M365-Passwort und existiert nur als bcrypt-Hash im Container. Der Benutzer setzt es über einen Einmal-Link selbst, der Admin sieht es nie.

Ein neuer Benutzer ist damit eine Gruppenmitgliedschaft in Exchange und ein Eintrag in der Admin-Oberfläche. Die App selbst fasst du nie wieder an.

Einrichtung in sieben Schritten

Die vollständige Anleitung mit allen Befehlen steht im README auf GitHub. Hier der grobe Ablauf, damit du siehst, worauf du dich einlässt:

  1. Image bauen auf einem Host, der rund um die Uhr läuft. Bei mir ist das eine Synology.
  2. Graph-Zertifikat erzeugen mit docker run … gen-cert.
  3. Entra-App registrieren mit 1-Register-EntraApp.ps1. Das Skript lädt das Zertifikat hoch und vergibt keine API-Berechtigung.
  4. Mailbox-Zugriff erteilen mit 2-Grant-MailboxAccess.ps1 -Group carddav-users@…. Dafür braucht es einen Exchange-Admin.
  5. Container starten mit ADMIN_UI=true. Das initiale Admin-Passwort landet nicht im Log, sondern einmalig in einer Datei.
  6. Benutzer anlegen in der Admin-Oberfläche auf Port 5233. Du bekommst einen Einmal-Link samt Text zum Verschicken.
  7. Mac einrichten: Der Benutzer öffnet den Link, wählt sein Passwort und folgt der Anleitung auf der Seite.
Admin-Oberfläche von m365-carddav mit zwei Benutzern, Sync-Status und offener Einladung
Die Admin-Oberfläche auf Port 5233: wer eingerichtet ist, wer noch nicht, und wie der letzte Sync lief.
Erstellte Einladung mit erfolgreichem Graph-Zugriffstest, Einmal-Link und Text zum Verschicken
Beim Anlegen prüft der Container sofort den Graph-Zugriff und liefert Einmal-Link und Text gleich mit.
Einladungsseite «M365-Kontakte einrichten» mit Formular für das neue Passwort
Der Benutzer wählt sein Passwort selbst – der Admin sieht es nie.
Bestätigungsseite «Erledigt» mit der Anleitung für die Einrichtung auf dem Mac
Danach zeigt die Seite die Anleitung für den Mac, mit Benutzername und Server-URL zum Kopieren.

Wer kein eigenes Zertifikat hat, bekommt vom Container ein selbstsigniertes. Mit einer internen CA geht es eleganter: tls-cert.pem und tls-key.pem nach data/config/ legen, fertig, keine Warnungen mehr.

Drei Stolpersteine auf dem Mac

«Manuell», nicht «Erweitert». Beim CardDAV-Account bietet macOS zwei Accounttypen an. Mit «Erweitert» (Server, Pfad und Port in getrennten Feldern) und auch mit einem Konfigurationsprofil meldet macOS 26 nur «Accountname oder Passwort kann nicht überprüft werden». Und zwar ohne je eine Verbindung aufzubauen, das habe ich mit tcpdump nachgeprüft. Mit «Manuell» und der vollständigen URL klappt es sofort:

https://nas.example.lan:5232/alice/
macOS-Dialog «Kontakte-Account hinzufügen» mit Accounttyp Manuell und vollständiger Server-URL
Accounttyp «Manuell» und die vollständige URL mit Benutzerpfad. «Erweitert» verbindet nie.

Konto umbenennen. macOS nennt das Konto nach der Server-URL, und genau so steht es dann in Kontakte. In Systemeinstellungen → Internetaccounts das Konto auswählen und die Beschreibung auf «M365» ändern. Ein Terminal-Befehl dafür existiert leider nicht.

Zertifikat. Bei einem selbstsignierten Zertifikat fragt macOS einmal nach: «Zertifikat einblenden» → «Immer vertrauen». Mit einer eigenen CA, deren Root auf den Macs installiert ist, entfällt das.

Fazit

Bei uns laufen jetzt fünf Postfächer über einen einzigen Container. Der erste Abgleich von 345 Kontakten dauerte ein paar Minuten, danach kommen Änderungen innert 15 Minuten auf den Mac. Und wenn Apple irgendwann Graph nachliefert: Container löschen, App entfernen, fertig. Es bleibt nichts zurück.

Code, Anleitung und Skripte findest du auf GitHub, unter MIT-Lizenz: github.com/ElFerminador/m365-carddav. Rückmeldungen und Pull Requests sind willkommen.