Skip to main content

Schuljahreswechsel ohne Handarbeit: deklarative Synchronisation von Schülerkonten aus der Schulverwaltung (SchILD-NRW, ASV-BW, CSV) ins Active Directory.

Project description

SchulSync

Schuljahreswechsel ohne Handarbeit: Schulverwaltung → Active Directory.

CI PyPI Python Getestet gegen Lizenz

Jeden September dasselbe Ritual: Der neue Export aus der Schulverwaltung liegt vor, und irgendjemand aus der Schul-IT klickt sich durch hunderte Konten – neue Fünftklässler anlegen, alle Klassen eine Stufe weiterschieben, Abgänger deaktivieren, Passwortzettel drucken. Zwei Tage Handarbeit, und ein Tippfehler bedeutet, dass ein Kind am ersten Schultag nicht ins WLAN kommt.

SchulSync macht daraus einen Befehl mit Vorschau:

schulsync plan

Das Prinzip: Terraform für Schülerkonten

Der CSV-Export der Schulverwaltung (SchILD-NRW, ASV-BW oder beliebige Spalten) beschreibt den Soll-Zustand. Das Active Directory ist der Ist-Zustand. SchulSync berechnet die Differenz – und führt sie erst aus, wenn ein Mensch den Plan gesehen und bestätigt hat.

schulsync plan  export.csv        # Was würde passieren? Ändert garantiert nichts.
schulsync apply export.csv        # Plan ausführen – nach Bestätigung.
  • Idempotent: derselbe Export zweimal ⇒ beim zweiten Mal „nichts zu tun". Abgebrochene Läufe repariert der nächste Lauf von selbst.
  • Ein Modell für alles: Schuljahreswechsel, Zuzug im Februar, Namensänderung nach Heirat – immer derselbe Soll/Ist-Vergleich.
  • Nachvollziehbar: jeder Lauf auf Wunsch als HTML-Report für die Ablage (Verfahrensnachweis).

Was SchulSync erledigt

Anlegen Konto in der Klassen-OU, Klassen- & Sammelgruppen, Home-Verzeichnis-Attribute, merkbares Initialpasswort (Tiger-Wolke-47), Passwortwechsel bei erster Anmeldung erzwungen
Klassenwechsel OU-Umzug + Gruppentausch (Klasse-5AKlasse-6A)
Umbenennen Namensänderung ohne neuen Benutzernamen – niemand verliert Profil oder Dateien
Reaktivieren Rückkehrer bekommen ihr altes Konto wieder statt eines Duplikats
Deaktivieren Abgänger: gesperrt, aus allen Gruppen entfernt, in die Abgänger-OU, mit Frist-Stempel
🗑 Löschen schulsync cleanup löscht erst nach Ablauf der Aufbewahrungsfrist – dokumentiertes Löschkonzept statt Karteileichen
Briefe druckfertige Zugangsdaten-Briefe pro Klasse aus einem Befehl

Dazu die Details, die man erst nach ein paar hundert Schülerkonten zu schätzen weiß: Umlaut- und Sonderzeichen-Transliteration (Yılmaz → yilmaz, Đorđević → dordevic), die 20-Zeichen-Grenze von sAMAccountName, Kollisionszähler gegen alle Konten der Domäne (emma.fischer2), automatische Erkennung von Kodierung (UTF-8 ↔ Windows-1252) und Trennzeichen.

Eingebaute Sicherheitsnetze

  • Dry-Run zuerst: plan hat keinerlei Schreibzugriff – Vorschau ist der Normalfall, nicht das Extra.
  • Notbremse: Ein Export, der mehr als die Hälfte der aktiven Konten deaktivieren würde (typisch: halber Export, falsche Berichtsvorlage), wird als Konflikt blockiert – bevor er Schaden anrichtet.
  • Konflikt heißt Stopp: doppelte IDs, Konten ohne Quell-ID → Meldung statt Automatik-Raten. Exit-Code 2, nichts wurde geändert.
  • Nur die eigene OU: Konten außerhalb von OU=SchulSync werden nie angefasst. Das Dienstkonto braucht keine Domain-Admin-Rechte, nur Delegierung auf diese OU.
  • Verschlüsselung erzwungen: LDAPS bzw. STARTTLS – unverschlüsselt verweigert SchulSync den Dienst. Passwörter stehen nie in Dateien, nur in SCHULSYNC_LDAP_PASSWORT.

schulsync apply

In 5 Minuten ausprobieren – mit echtem AD

Das Repo bringt ein Docker-Lab mit: ein wegwerfbarer Samba Active Directory Domain Controller (Domäne SCHULE.LOCAL) plus fiktive Beispieldaten für zwei Schuljahre.

git clone https://github.com/haydarkozat/schulsync && cd schulsync
python3 -m venv .venv && source .venv/bin/activate
pip install -e .

# 1) Lab-AD hochfahren (einmalig ~1 Minute)
docker compose -f lab/docker-compose.yml up -d --build --wait
export SCHULSYNC_LDAP_PASSWORT="Lab-Kennwort-2026"

# 2) Schuljahr 2025/26 einspielen: 149 Schüler:innen + 5 Lehrkräfte
schulsync apply examples/schild-export-2025.csv \
    --lehrkraefte examples/lehrkraefte.csv --ja \
    --config lab/schulsync.lab.yaml

# 3) Der Schuljahreswechsel: erst ansehen …
schulsync plan examples/schild-export-2026.csv \
    --lehrkraefte examples/lehrkraefte.csv \
    --report report.html --config lab/schulsync.lab.yaml

# … dann ausführen: 24 anlegen, 125 aufrücken, 24 Abgänger, 2 Umbenennungen
schulsync apply examples/schild-export-2026.csv \
    --lehrkraefte examples/lehrkraefte.csv --ja \
    --config lab/schulsync.lab.yaml

# 4) Zugangsdaten-Briefe für die neuen 5er drucken
schulsync briefe zugangsdaten-*.csv --loeschen --config lab/schulsync.lab.yaml

Danach lohnt ein Blick in den HTML-Report (Beispiel):

HTML-Report

Und weil SchulSync idempotent ist, sagt derselbe Befehl direkt danach schlicht die Wahrheit:

idempotenter zweiter Lauf

Im echten Schulnetz

pip install schulsync                               # frisch von PyPI (Python 3.11+)
cp examples/schulsync.example.yaml schulsync.yaml   # anpassen: Server, Basis-DN, Schema
export SCHULSYNC_LDAP_PASSWORT='…'                  # Bind-Passwort des Dienstkontos
schulsync check                                     # Verbindung & Konfiguration prüfen
schulsync validate export.csv                       # Export prüfen (Kodierung, Dubletten)
schulsync plan export.csv --report plan.html        # Vorschau für die Ablage
schulsync apply export.csv                          # mit Rückfrage ausführen

Funktioniert gegen Windows Server AD und Samba AD (ab Werk wird gegen einen echten Samba-DC integrationsgetestet, s. u.). Für die Drift-Überwachung: schulsync plan export.csv --check liefert Exit-Code 3, sobald AD und Schulverwaltung auseinanderlaufen – fertig ist der Nightly-Check im Monitoring.

Kommando Zweck
schulsync check Konfiguration & AD-Verbindung prüfen (nur lesend)
schulsync validate <csv> Export prüfen: Kodierung, Spalten, Dubletten
schulsync plan <csv> Soll/Ist-Vergleich als Vorschau (Dry-Run)
schulsync apply <csv> Plan ausführen (mit Bestätigung bzw. --ja)
schulsync briefe <csv> Zugangsdaten-Briefe je Klasse als druckfertiges HTML
schulsync cleanup Abgänger nach Ablauf der Aufbewahrungsfrist endgültig löschen

Warum nicht …?

  • … Microsoft School Data Sync? Synct in die Cloud (Entra ID/Teams), nicht ins on-prem AD, und setzt M365-Schulverträge voraus. SchulSync läuft dort, wo viele Schulträger ihre Konten wirklich verwalten: im lokalen Active Directory – ohne dass Schülerdaten das Haus verlassen.
  • … UCS@school? Stark, aber ein kompletter Plattformwechsel samt eigenem Ökosystem. SchulSync ist ein Werkzeug, keine Plattform: es arbeitet mit dem AD, das schon da ist.
  • … das gewachsene PowerShell-Skript? Kennt jede Schul-IT. Meist ohne Dry-Run, ohne Idempotenz, ohne Notbremse, ohne Löschkonzept – und der Autor ist nicht mehr an der Schule. Genau diese Lücken schließt SchulSync, mit Tests statt Hoffnung.

Qualität

  • 60+ Tests, darunter eine Integrationssuite, die in der CI einen echten Samba-AD-DC im Docker-Container hochfährt und den kompletten Lebenszyklus durchspielt: Erstbefüllung → Idempotenz → Schuljahreswechsel → Rückkehrer-Reaktivierung → Notbremse → Fristablauf & Löschung.
  • Kein Mock spielt AD – was hier grün ist, lief gegen LDAP, TLS und unicodePwd-Realität.
  • docs/funktionsweise.md erklärt die Designentscheidungen (warum CN = Benutzername, warum employeeNumber als Schlüssel, warum diese Ausführungsreihenfolge).
  • docs/dsgvo.md: Datenminimierung, Löschkonzept, TLS-Zwang – Datenschutz als Designgrundlage, nicht als Fußnote.

Hinweis zu den Beispieldaten: Alle Namen in examples/ sind fiktiv und werden von scripts/erzeuge_beispieldaten.py deterministisch erzeugt. Es sind keine echten Schülerdaten – und es sollten auch nie welche ins Repo gelangen (.gitignore hilft nach).

Über dieses Projekt

Ich habe 16 Jahre lang Schul-IT betrieben – zuletzt als IT-Systemadministrator im türkischen FATİH-Programm (dem Pendant zum DigitalPakt Schule) mit rund 5.000 Nutzerkonten. Den Schuljahreswechsel habe ich oft genug von Hand gemacht, um zu wissen, welche Fehler um 3 Uhr nachts passieren. SchulSync ist das Werkzeug, das ich mir damals gewünscht hätte.

Haydar Kozat · GitHub · LinkedIn

Lizenz: MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

schulsync-0.1.1.tar.gz (250.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

schulsync-0.1.1-py3-none-any.whl (36.3 kB view details)

Uploaded Python 3

File details

Details for the file schulsync-0.1.1.tar.gz.

File metadata

  • Download URL: schulsync-0.1.1.tar.gz
  • Upload date:
  • Size: 250.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for schulsync-0.1.1.tar.gz
Algorithm Hash digest
SHA256 c8f05d3a577b4eae1f72a6dfeedf85c7808ad54d9cccf8083f1da3fb6d014afa
MD5 a273a947bfdc854a0cbafbae4905007e
BLAKE2b-256 f0727b8b2f930ead7dbe8985625e5ccc17e00555748d0e4028c11d5966420fda

See more details on using hashes here.

Provenance

The following attestation bundles were made for schulsync-0.1.1.tar.gz:

Publisher: release.yml on haydarkozat/schulsync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file schulsync-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: schulsync-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 36.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for schulsync-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fb9e8793a9ac4c59f6106a23ac933d7b50bed21b6e52f7903e7a4b3c06172454
MD5 c0ff45aaff2072fb8e2cbedc92aae54d
BLAKE2b-256 80e2e91fc79d7c4e7f32094ba9703519ff86ea3e159027ec7f9b41bd37e6e479

See more details on using hashes here.

Provenance

The following attestation bundles were made for schulsync-0.1.1-py3-none-any.whl:

Publisher: release.yml on haydarkozat/schulsync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page