Zum Inhalt springen
SharePoint Entwicklung

Die SharePoint Migration API in der Praxis: Was Microsoft nicht dokumentiert

AES-256-Verschlüsselung, Manifest-XMLs, Blob-Snapshots und Service-Bugs: Praxiserfahrungen und Code aus über 50 Reverse-Engineering-Iterationen mit der SharePoint Online Migration API.

Wer eine große SharePoint-Migration nicht mit Standard-Tools abbilden kann (warum, steht in Teil 2), landet früher oder später bei der SharePoint Online Migration API — derselben Schnittstelle, die auch ShareGate und SPMT unter der Haube verwenden. Sie ist der einzige Weg, Inhalte mit Versionshistorie, Autoren und Zeitstempeln performant nach SharePoint Online zu importieren, ohne Item für Item über CSOM/REST zu tröpfeln.

Die offizielle Dokumentation deckt aber nur die halbe Wahrheit ab. Dieser Beitrag konsolidiert, was wir in über 50 Reverse-Engineering-Iterationen gegen Live-SharePoint-Online gelernt haben — jeder Punkt durch erfolgreiche End-to-End-Importe verifiziert.

Wie die Migration API grundsätzlich funktioniert

  1. Container anfordern: ProvisionMigrationContainers liefert zwei Azure-Blob-Container (Content + Manifeste) samt SAS-URLs und einem AES-256-Schlüssel.
  2. Paket bauen: Die Dateien plus 8 Manifest-XMLs (u. a. Manifest.xml, UserGroup.xml, SystemData.xml) beschreiben, was wohin importiert wird — inklusive Feldwerten, Versionen, Autoren.
  3. Verschlüsselt hochladen: Jeder Blob wird AES-256-CBC-verschlüsselt hochgeladen.
  4. Job starten: CreateMigrationJobEncrypted übergibt Container + Schlüssel; SharePoint importiert serverseitig.
  5. Fortschritt lesen: Eine Azure Queue liefert JobProgress/JobEnd-Events.

Klingt einfach. Die Fallen stecken in den Details.

Falle 1: Die Verschlüsselung — IV niemals in den Content

Die häufigste Sackgasse (auch in Microsoft-Q&A-Threads): den Initialisierungsvektor vor den Ciphertext zu schreiben. Falsch. Der IV gehört in die Blob-Metadata, der Blob enthält nur den Ciphertext:

internal static (byte[] ciphertext, byte[] iv) EncryptAesCbcPureCiphertext(
    byte[] plainBytes, byte[] key)
{
    using var aes = Aes.Create();
    aes.Mode      = CipherMode.CBC;
    aes.Padding   = PaddingMode.PKCS7;
    aes.KeySize   = 256;
    aes.Key       = key;          // Key aus ProvisionMigrationContainers
    aes.GenerateIV();             // frischer IV pro Blob!
    using var encryptor = aes.CreateEncryptor();
    var cipher = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
    return (cipher, aes.IV);
}

Der Upload-Ablauf pro Blob — und jeder Schritt ist Pflicht:

1. PUT <blob>                    Body = NUR Ciphertext
                                 Content-MD5 = MD5 über den CIPHERTEXT (nicht Plaintext!)
2. PUT <blob>?comp=metadata      x-ms-meta-IV = Base64(IV)
3. PUT <blob>?comp=snapshot      kein Body

Zwei Konsequenzen, die uns Stunden gekostet haben:

  • MD5 über den Ciphertext, nicht über den Klartext — sonst lehnt Azure den PUT mit 400 Content-MD5 mismatch ab.
  • Der Snapshot ist kein Nice-to-have. Ohne Snapshot liest der Migration-Worker den Blob gar nicht erst — der Job läuft kommentarlos in den Timeout. Und der Snapshot muss nach dem Metadata-PUT erfolgen, sonst fehlt dem Worker der IV.

Das Fehlerbild Data at the root level is invalid. Line 1, position 1. bedeutet übrigens fast immer: IV im Body statt in den Metadaten — der Worker entschlüsselt Müll und versucht, ihn als XML zu parsen.

Falle 2: Manifest.xml — der strenge Deserializer

Die Schema-Dokumentation zeigt viele Attribute, die der Import-Worker schlicht ignoriert oder an denen er scheitert. Erkenntnisse aus der Praxis:

  • SPFile.Id darf nicht gleich SPListItem.Id sein — sonst kollabieren Datei und Listitem zu einem Eintrag.
  • SystemData.xml braucht ObjectsProcessed, sonst startet der Job nicht sauber.
  • Ordnerhierarchien müssen parents-first im Manifest stehen.
  • Feld-Werte haben exakte Wire-Formate: User-Felder als id;#login (und der User muss in UserGroup.xml stehen), Lookups als id;#text, Taxonomie mit echten Term-GUIDs.

Falle 3: Der Zwei-Versionen-Bug

Unser Lieblings-Service-Bug: Eine Datei mit genau zwei Versionen verliert beim Import ihre Historie — beide werden zu v1.0. Bei drei und mehr Versionen funktioniert das <Versions>-Pattern korrekt. Wer, wie wir, Versionsketten dedupliziert (Teil 5), erzeugt genau solche Zwei-Versionen-Ketten ständig — und braucht eine bewusste Strategie dafür.

Ebenfalls dokumentationswürdig:

  • Autoren-Erhalt ist best-effort: Ist ein historischer Benutzer nicht mehr auflösbar, fällt der Worker still auf den System-Account zurück und meldet nur eine Warning.
  • JobEnd-Zähler kommen teils als Strings: Wer FilesCreated naiv als Zahl parst, liest 0 und hält erfolgreiche Jobs für leer. Erst tolerantes Parsen macht die Job-Telemetrie belastbar.
  • Nur historische Versionen ohne aktuelle im <Versions>-Block → Worker verwirft die komplette Historie.

Falle 4: Grün heißt nicht fertig

Die wichtigste strategische Lektion: Ein erfolgreicher Migration-Job beweist nicht, dass alles angekommen ist. Teilfehler, verworfene Versionen, stille Fallbacks — all das steckt bestenfalls in Warnings. Wir haben deshalb ein eigenes Abnahme-Gate gebaut: Nach jedem Projekt läuft ein Parity-Check, der jede Quelldatei gegen das Ziel abgleicht (missing=0, SizeMismatch=0), mit SHA256-Stichproben bei Verdacht. Erst dieser Report — nicht der Job-Status — ist unser Abnahme-Artefakt.

Fehler-Lexikon (Auszug)

SymptomTatsächliche Ursache
Data at the root level is invalidIV im Blob-Body statt in x-ms-meta-IV
Job läuft in Timeout, keine FehlermeldungBlob-Snapshot fehlt
400 Content-MD5 mismatch beim UploadMD5 über Plaintext statt Ciphertext
Versionshistorie fehlt am ZielZwei-Versionen-Bug oder <Versions> ohne aktuelle Version
Alle Dokumente gehören dem System-AccountBenutzer nicht in UserGroup.xml / nicht mehr auflösbar
FilesCreated=0 trotz sichtbarer DateienString-codierte Zähler im JobEnd-Event

Fazit

Die Migration API ist mächtig — Versionen, Autoren, Zeitstempel, hoher Durchsatz — aber sie ist eine Schnittstelle für Werkzeugbauer, keine Endanwender-API. Wer sie direkt nutzt, übernimmt die Verantwortung für Verschlüsselung, Manifest-Korrektheit und vor allem für die eigene Verifikation des Ergebnisses.


Sie evaluieren die Migration API für Ihr Projekt oder stecken in einem der beschriebenen Fehlerbilder fest? Wir haben diese Lernkurve bereits hinter uns und unterstützen gerne — von der Architekturberatung bis zur fertigen Pipeline. Kontakt aufnehmen.

Alle Teile der Serie:

  1. Warum keine 1:1-Migration
  2. 25 TB SharePoint-Migration: Warum Standard-Tools nicht reichen
  3. Die SharePoint Migration API in der Praxis (dieser Beitrag)
  4. Term Store am Limit: Taxonomie-Migration im geteilten Tenant
  5. Versionshistorie intelligent migrieren
  6. Provisioning im Großformat: PnP, Throttling, Berechtigungen
  • Migration API
  • SharePoint Online
  • Azure Blob Storage
  • C#
  • .NET
  • Verschlüsselung

Fragen zu diesem Thema?

Wir unterstützen Sie gerne bei der Umsetzung in Ihrer Umgebung.