No description
| .env.example | ||
| .gitignore | ||
| config.py | ||
| graph_client.py | ||
| README.md | ||
| requirements.txt | ||
| sync.py | ||
| sync_org_contacts.py | ||
| sync_user_data.py | ||
Microsoft Graph API – M365 Sync
Synchronisiert M365-Daten lokal als JSON-Dateien. Source of Truth ist Microsoft 365.
| Ressource | Graph-Endpunkt | Lokaler Pfad |
|---|---|---|
| Organizational Contacts | /contacts/delta |
objects/global/{id}.json |
| Persönliche Kontakte (alle User) | /users/{uid}/contacts/delta |
objects/user/{uid}/contacts/{id}.json |
| Kalendereinträge (alle User) | /users/{uid}/events/delta |
objects/user/{uid}/calendar/{id}.json |
- Erster Lauf: vollständiger Sync
- Folgeläufe: inkrementell via Graph Delta API
- Gelöschte Einträge werden lokal entfernt
- State wird nach jedem User gespeichert (resilient bei Abbruch)
Voraussetzungen
- Python 3.10+
- Azure App Registration mit folgenden Application Permissions (Admin Consent erforderlich):
| Permission | Zweck |
|---|---|
OrgContact.Read.All |
Organizational Contacts lesen |
User.Read.All |
Alle Tenant-User enumerieren |
Contacts.Read |
Persönliche Kontakte aller User |
Calendars.Read |
Kalender aller User |
Einrichtung
1. Azure App Registration
- Azure Portal → Azure Active Directory → App registrations → New registration
- Name vergeben, Account type: Accounts in this organizational directory only
- Certificates & secrets → New client secret → Secret kopieren
- API permissions → Add a permission → Microsoft Graph → Application permissions
→ Die vier Permissions oben hinzufügen - Grant admin consent for [Tenant] klicken
2. Projekt einrichten
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp .env.example .env
.env ausfüllen:
TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
CLIENT_SECRET=your-client-secret-here
Verwendung
.venv/bin/python sync.py
Der erste Lauf führt einen vollständigen Sync durch. Jeder weitere Lauf synchronisiert nur Änderungen seit dem letzten Lauf.
Für automatische Ausführung per Cron:
# Täglich um 03:00 Uhr
0 3 * * * /pfad/zum/projekt/.venv/bin/python /pfad/zum/projekt/sync.py
Dateistruktur
.
├── sync.py # Einstiegspunkt und Orchestrierung
├── sync_org_contacts.py # Sync: Organizational Contacts
├── sync_user_data.py # Sync: User-Kontakte und Kalender
├── graph_client.py # Graph API Auth + Pagination
├── config.py # Konfiguration (lädt .env)
├── requirements.txt
├── .env # Credentials (nicht committen!)
├── .env.example
├── objects/
│ ├── global/
│ │ └── {id}.json # Organizational Contacts
│ └── user/
│ └── {uid}/
│ ├── contacts/
│ │ └── {id}.json
│ └── calendar/
│ └── {id}.json
└── state/
└── sync_state.json # Delta-Links aller Ressourcen
State-Datei
{
"org_contacts": {
"delta_link": "https://graph.microsoft.com/v1.0/contacts/delta?$deltatoken=..."
},
"users": {
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx": {
"contacts_delta_link": "https://...",
"calendar_delta_link": "https://..."
}
}
}
Hinweise
- User ohne Exchange-Mailbox (keine Lizenz, Service Accounts) werden automatisch übersprungen (HTTP 403/404).
- Die State-Datei wird nach jedem User gespeichert. Ein Abbruch führt beim nächsten Lauf nur zu einem Teilsync der verbleibenden User.
- Alte State-Dateien im v1-Format (
{"delta_link": "..."}) werden automatisch migriert.