Files
Start-SPMigration/README.md
T
Torsten Brendgen 90d17be932 Enhance Start-SPMigration GUI for SharePoint SSOM compatibility and update mapping schema
- Added version checks to ensure the script runs in 64-bit Windows PowerShell Desktop Edition.
- Implemented checks for STA thread requirement and SharePoint environment validation.
- Updated application version to 1.2.0 and supported mapping schema version to 2.
- Introduced a new function to retrieve SharePoint product support information based on build version.
- Modified mapping table structure to include ToolVersion and additional metadata fields.
- Updated sample mapping JSON to reflect new schema version and added new fields for metadata column mappings.
2026-08-26 21:39:39 +02:00

17 KiB

Start-SPMigration

Version 1.2.0

PowerShell-Werkzeug fuer die Migration von SharePoint-On-Premises-Inhalten ueber das serverseitige Objektmodell (SSOM). Unterstuetzt werden SharePoint Server 2016 und SharePoint Server 2019.

Das Skript kann:

  • Dokumentbibliotheken exportieren
  • Dateien inklusive Metadaten exportieren
  • normale SharePoint-Listen und deren Eintraege exportieren
  • exportierte Inhalte wieder in ein Ziel-Web importieren
  • beim Export automatisch eine JSON-MappingTable fuer Feld-, Listen- und Bibliotheks-Mappings erzeugen
  • beim Import dieselbe MappingTable fuer Container- und Metadaten-Mappings verwenden
  • grosse Dokumente streambasiert exportieren und importieren, ohne die komplette Datei in den Arbeitsspeicher zu laden
  • Fehler einzelner Dateien in ImportErrors-yyyyMMdd-HHmmss.csv protokollieren und mit der naechsten Datei fortfahren

Voraussetzungen

  • SharePoint Server 2016 oder SharePoint Server 2019
  • lokale Ausfuehrung auf einem SharePoint-Farmserver; SSOM kann keine entfernte Farm adressieren
  • 64-Bit Windows PowerShell (Desktop Edition), nicht PowerShell 7
  • Hauptskript: Windows PowerShell 4.0 oder neuer
  • GUI: Windows PowerShell 5.0 oder neuer im STA-Modus; Windows PowerShell 5.1 wird empfohlen
  • Farm- und Web-Berechtigungen fuer die ausgefuehrten Export-/Importaktionen

Das Werkzeug versucht zuerst das SharePoint-Modul und anschliessend den auf klassischen Farmen ueblichen Snap-in-Fallback zu laden:

Import-Module SharePointServer
Add-PSSnapin Microsoft.SharePoint.PowerShell

Die lokale Farmversion wird anhand von SPFarm.Local.BuildVersion erkannt. Andere Versionen, einschliesslich Subscription Edition, werden bewusst abgelehnt. Die Umgebung kann vor einer Migration separat geprueft werden:

.\Start-SPMigration.ps1 -ValidateEnvironment

Migration zwischen zwei Farmen

Eine Migration von SharePoint 2016 nach SharePoint 2019 erfolgt mit SSOM zwingend zweistufig:

  1. Export lokal auf einem Server der SharePoint-2016-Quellfarm ausfuehren.
  2. Den kompletten Exportordner auf einen Server der SharePoint-2019-Zielfarm kopieren.
  3. Import dort lokal gegen das Ziel-Web ausfuehren.

Ein kombinierter Export und Import in demselben Prozess ist nur sinnvoll, wenn Quelle und Ziel in derselben lokalen Farm liegen. Ein Import von 2019 nach 2016 ist ein nicht garantierter Downgrade und erzeugt eine Warnung.

Dateien im Projekt

  • Start-SPMigration.ps1: Hauptskript fuer Export und optionalen Import
  • Start-SPMigrationGUI.ps1: grafische Oberflaeche; die aktuelle Programmversion wird im Fenstertitel angezeigt
  • mapping.sample.json: Beispiel fuer die aktuelle MappingTable
  • column-mapping-defaults.sample.json: Beispiel fuer wiederverwendbare Column-Mapping-Defaults

Exportierte Struktur

Beim Export entsteht unter -OutputPath folgende Struktur:

OutputPath
|-- Files
|   |-- <Bibliothek>
|   |   |-- Unterordner
|   |   |   |-- Dokument.pdf
|   |   |   |-- Dokument.pdf.properties.json
|-- Lists
|   |-- ListeA.json
|-- MappingTable.json
|-- manifest.csv

Dateien

  • Die Originaldatei wird in Files\<Bibliothek>\... gespeichert.
  • Die Metadaten liegen direkt daneben als Sidecar-Datei: Dateiname.ext.properties.json
  • manifest.csv enthaelt ausserdem Container-Eintraege fuer Listen und Dokumentbibliotheken

Listen

  • Pro Liste wird genau eine JSON-Datei unter Lists\ erzeugt.
  • Die Eintraege enthalten insbesondere:
    • ContentTypeId
    • FieldValues
    • FieldTextValues
    • Fields

Wichtige Metadaten

Fuer den Import sind vor allem diese Informationen relevant:

  • FieldValues
  • FieldTextValues
  • ContentTypeId
  • interne Feldnamen (InternalName)

Der Inhaltstyp wird zuerst ueber eine exakt passende ID beziehungsweise einen gleichnamigen Site-Content-Type aufgeloest. Ein beliebiger Basis-Content-Type wie Item oder Document wird nicht mehr still als Ersatz fuer einen fehlenden eigenen Content-Type verwendet.

MappingTable.json

Beim Export wird automatisch eine MappingTable.json erzeugt. Diese Datei enthaelt:

  • LibraryMappings: Mapping von Quellbibliothek auf Zielbibliothek
  • ListMappings: Mapping von Quellliste auf Zielliste
  • MetadataColumnMappings.SystemColumns: erkannte Systemspalten
  • MetadataColumnMappings.CustomColumns: erkannte eigene Fachspalten

Beispiel:

{
  "SchemaVersion": 2,
  "ToolVersion": "1.2.0",
  "GeneratedAtUtc": "2026-04-15T08:00:00.0000000Z",
  "SourceWebUrl": "http://sharepoint/sites/Quelle",
  "SourceSharePointProduct": "SharePoint Server 2016",
  "SourceSharePointBuild": "16.0.5508.1000",
  "LibraryMappings": [
    {
      "ObjectType": "DocumentLibrary",
      "SourceTitle": "Documents",
      "TargetTitle": "Dokumente",
      "BaseType": "DocumentLibrary",
      "BaseTemplate": 101,
      "RootFolderUrl": "/Documents",
      "Hidden": false
    }
  ],
  "ListMappings": [
    {
      "ObjectType": "List",
      "SourceTitle": "TestListe",
      "TargetTitle": "ZielTestListe",
      "BaseType": "GenericList",
      "BaseTemplate": 100,
      "RootFolderUrl": "/Lists/TestListe",
      "Hidden": false
    }
  ],
  "MetadataColumnMappings": {
    "SystemColumns": [
      {
        "ObjectType": "List",
        "ContainerSourceTitle": "Kalender",
        "SourceInternalName": "EventDate",
        "SourceCanonicalInternalName": "EventDate",
        "SourceSupportingInternalNames": [],
        "TargetInternalName": "EventDate",
        "DisplayName": "Beginnt",
        "TypeAsString": "DateTime",
        "Hidden": false,
        "ReadOnly": false,
        "Sealed": true,
        "IsSystemColumn": true,
        "ImportSupported": true
      }
    ],
    "CustomColumns": [
      {
        "ObjectType": "DocumentLibrary",
        "ContainerSourceTitle": "Documents",
        "SourceInternalName": "SecurityClearance",
        "SourceCanonicalInternalName": "SecurityClearance",
        "SourceSupportingInternalNames": [
          "SecurityClearance_0"
        ],
        "TargetInternalName": "GMNSecurityClearance",
        "DisplayName": "Security Clearance",
        "TypeAsString": "TaxonomyFieldType",
        "Hidden": false,
        "ReadOnly": false,
        "Sealed": false,
        "IsSystemColumn": false,
        "ImportSupported": true
      }
    ]
  }
}

Fuer das Mapping ist vor allem relevant:

  • SchemaVersion: aktuell Version 2; Version 1 wird beim Laden auf Version 2 normalisiert, neuere unbekannte Versionen werden abgelehnt
  • ToolVersion, SourceSharePointProduct und SourceSharePointBuild: dokumentieren die erzeugende Tool- und Quellfarm-Version
  • TargetTitle: Zielname von Liste oder Bibliothek
  • TargetInternalName: Zielfeld fuer die exportierte Metadatenspalte
  • SystemColumns: SharePoint-Standardfelder wie EventDate, EndDate, Title oder Created
  • CustomColumns: eigene Fachspalten, die im Ziel oft umbenannt oder anders aufgebaut sind
  • SourceSupportingInternalNames: technische Begleitspalten wie _0 werden nur noch informativ aufgefuehrt und nicht separat gemappt
  • ImportSupported: steuert, ob eine Spalte beim Import ueberhaupt beruecksichtigt wird
  • Taxonomy-Felder (Einfach- und Mehrfachwerte) werden beim Import ueber den exportierten TermGuid gesetzt; dabei wird angenommen, dass dieselben Terme im Ziel bereits mit identischer GUID existieren
  • Die exportierte WssId wird bewusst nicht uebernommen: Sie ist nur innerhalb der Quell-SiteCollection gueltig. SharePoint loest beim Setzen des Feldwerts die zielseitige WssId auf und legt den Eintrag in der TaxonomyHiddenList bei Bedarf selbst an. Die Hidden List soll deshalb nicht separat migriert oder vorab befuellt werden.
  • Klassische Lookup- und LookupMulti-Spalten werden derzeit explizit als nicht importierbar markiert. Quellseitige Lookup-IDs sind in einer anderen Farm nicht portabel; ein korrekter Ausbau benoetigt ein fachliches Schluessel-Mapping und einen zweiphasigen Import.

Wenn eine Ziel-Liste oder Ziel-Bibliothek nicht existiert, gibt das Skript eine Warnung aus, dass dieser Container manuell angelegt werden soll.

Column Mapping Defaults

Wenn Bibliotheks- und Listen-Mappings je Migration unterschiedlich sind, die Spalten-Mappings aber gleich bleiben, kann die GUI eine separate Column-Defaults-Datei verwenden.

  • Die MappingTable.json bleibt migrationsspezifisch und enthaelt weiterhin LibraryMappings, ListMappings und die konkret angewendeten MetadataColumnMappings.
  • Die Defaults-Datei enthaelt nur wiederverwendbare Spaltenregeln unter Rules.
  • In der GUI kann ein Defaults-Pfad im Feld ColumnDefaults gepflegt werden. Nach einem Export werden vorhandene Defaults automatisch auf die frisch geladene MappingTable.json angewendet und gespeichert.
  • Mit Anwenden koennen Defaults erneut auf die aktuelle MappingTable gelegt werden. Danach bleiben manuelle Anpassungen pro Migration weiterhin moeglich.
  • Mit Speichern erzeugt die GUI aus den aktuellen Column-Mappings ein Defaults-Template. Gleichartige Spalten werden dabei auf ContainerSourceTitle = "*" generalisiert; widerspruechliche Regeln bleiben container-spezifisch.

Regeln werden von spezifisch nach allgemein angewendet:

  • ObjectType + ContainerSourceTitle + SourceInternalName
  • ObjectType + * + SourceInternalName
  • * + ContainerSourceTitle + SourceInternalName
  • * + * + SourceInternalName

Beispiel:

{
  "SchemaVersion": 1,
  "TemplateType": "ColumnMappings",
  "Rules": [
    {
      "ObjectType": "*",
      "ContainerSourceTitle": "*",
      "SourceInternalName": "SecurityClearance",
      "TargetInternalName": "GMNSecurityClearance",
      "ImportSupported": true
    }
  ]
}

Parameter

Web- und Pfadparameter

  • SourceUrl: Quell-Web; beim Export erforderlich
  • TargetUrl: Ziel-Web; beim Import erforderlich

OutputPath ist optional. Standard ist .\SPMigrationOutput im aktuellen Verzeichnis.

Umgebungspruefung

  • ValidateEnvironment: prueft Windows PowerShell, x64-Prozess, SharePoint-Cmdlets, Taxonomy-Assembly und die lokale SharePoint-2016/2019-Farm, ohne eine Migration zu starten

Exportparameter

  • Export: fuehrt den Export aus
  • IncludeHiddenLibraries: exportiert auch versteckte Bibliotheken
  • IncludeHiddenLists: exportiert auch versteckte Listen

Importparameter

  • Import: fuehrt den Import aus einem vorhandenen Exportordner aus
  • MappingTable: JSON-Datei mit Listen-, Bibliotheks- und Spalten-Mappings
  • ImportFiles: importiert nur Dateien/Bibliotheken
  • ImportLists: importiert nur Listen
  • Overwrite: ueberschreibt vorhandene Dateien beim Import

Hinweis:

  • Wenn weder ImportFiles noch ImportLists gesetzt sind, werden beim Import beide Bereiche verarbeitet.
  • Wird MappingTable nicht angegeben, verwendet das Skript standardmaessig OutputPath\MappingTable.json.
  • Die alte CSV-Variante wird fuer reines Feldmapping weiterhin als Fallback gelesen.
  • Werden keine Schalter gesetzt, leitet das Skript den Modus weiterhin aus SourceUrl und TargetUrl ab.
  • Wenn Overwrite nicht gesetzt ist und eine Datei bereits existiert, wird bei aktiver Versionierung eine neue Version geschrieben; ohne Versionierung wird die Datei uebersprungen.
  • Nicht importierbare Systemfelder wie Attachments, Created, Modified, Author oder Editor werden automatisch aus der MappingTable herausgehalten bzw. beim Import uebersprungen.
  • Vor dem Speichern prueft das Skript fehlende Pflichtfelder. Wenn ein einzelnes Listen- oder Datei-Metadatenobjekt trotzdem nicht gespeichert werden kann, wird es mit Kontext-Warnung uebersprungen und der Import laeuft weiter.
  • Fehler bei einzelnen Dateien stoppen den restlichen Dateiimport nicht. Sie werden im Exportordner in einer Datei ImportErrors-yyyyMMdd-HHmmss.csv mit Importphase, Datei, Bibliothek, Ziel-Web und Fehlerursache protokolliert.
  • Beim Listenimport legt das Skript bei Bedarf ein verstecktes Textfeld StartSPMigrationSourceUniqueId in der Zielliste an. Wiederholte Imports aktualisieren damit vorhandene Elemente anhand der exportierten Source-UniqueId statt sie erneut anzulegen.

Beispiele

SSOM-Umgebung pruefen

.\Start-SPMigration.ps1 -ValidateEnvironment

Nur Export

.\Start-SPMigration.ps1 `
  -SourceUrl "http://sharepoint/sites/Quelle" `
  -Export `
  -IncludeHiddenLists `
  -IncludeHiddenLibraries

Nur Import

.\Start-SPMigration.ps1 `
  -TargetUrl "http://sharepoint/sites/Ziel" `
  -Import `
  -MappingTable "C:\Temp\SP-Export\MappingTable.json"

SharePoint 2016 exportieren, MappingTable anpassen und auf SharePoint 2019 importieren

.\Start-SPMigration.ps1 `
  -SourceUrl "http://sharepoint/sites/Quelle" `
  -OutputPath "C:\Temp\SP-Export" `
  -Export

Danach den kompletten Ordner C:\Temp\SP-Export auf den SharePoint-2019-Farmserver kopieren und MappingTable.json bearbeiten. Dort insbesondere TargetTitle sowie TargetInternalName pruefen.

Anschliessend:

.\Start-SPMigration.ps1 `
  -OutputPath "C:\Temp\SP-Export" `
  -TargetUrl "http://sharepoint/sites/Ziel" `
  -Import `
  -MappingTable "C:\Temp\SP-Export\MappingTable.json"

Nur Dateien importrelevant ausfuehren

.\Start-SPMigration.ps1 `
  -TargetUrl "http://sharepoint/sites/Ziel" `
  -Import `
  -MappingTable "C:\Temp\SP-Export\MappingTable.json" `
  -ImportFiles `
  -Overwrite

Nur Listen importrelevant ausfuehren

.\Start-SPMigration.ps1 `
  -TargetUrl "http://sharepoint/sites/Ziel" `
  -Import `
  -MappingTable "C:\Temp\SP-Export\MappingTable.json" `
  -ImportLists

Aktuelles Verhalten beim Import

Dokumentbibliotheken

  • Dateien werden in die per MappingTable.json aufgeloeste Zielbibliothek importiert.
  • Unterordner werden bei Bedarf angelegt.
  • Metadaten werden anschliessend ueber die MetadataColumnMappings gesetzt.

Listen

  • Listeneintraege werden neu angelegt oder bei erneutem Import ueber StartSPMigrationSourceUniqueId wiedergefunden und aktualisiert.
  • Feldwerte werden ueber FieldValues, FieldTextValues und MetadataColumnMappings gemappt.

GUI-Logging und Mapping-Bearbeitung

  • Beim Import protokolliert die GUI fuer jeden verarbeiteten Columnwert, ob die Zuweisung am Item erfolgreich war oder mit welchem Fehler sie uebersprungen wurde. Das Log nennt Container bzw. Item sowie Quell- und Zielspalte, schreibt aber nicht den eigentlichen Feldinhalt mit.
  • In den Mapping-Tabs Bibliotheken, Listen, System Columns und Custom Columns kann eine Zeile per Rechtsklick und Zeile loeschen vollstaendig entfernt werden. Beim Speichern wird sie entsprechend nicht mehr in die MappingTable.json geschrieben.

Bekannte Einschraenkungen

  • Die Zielaufloesung kann ueber MappingTable.json mit TargetTitle ueberschrieben werden.
  • Listenanhaenge werden derzeit noch nicht physisch mit importiert.
  • Ordner in normalen Listen werden beim Import derzeit uebersprungen.
  • Lookup- und LookupMulti-Spalten werden nicht importiert, weil Quell-IDs auf der Zielfarm keine stabile Bedeutung haben.
  • SharePoint-Gruppen in Personenfeldern werden exportseitig erkannt, aber noch nicht automatisch einer Zielgruppe zugeordnet; ein nicht aufloesbarer nichtleerer Wert wird als Feldfehler gemeldet.
  • Fehlende Ziellisten oder Zielbibliotheken werden derzeit nicht automatisch angelegt; das Skript gibt stattdessen eine Warnung zur manuellen Anlage aus.
  • Bereits vorhandene Dateien unter Files und Lists werden vor einem erneuten Export nicht automatisch entfernt. Fuer jeden produktiven Lauf sollte deshalb ein neuer, leerer OutputPath verwendet werden.
  • Schlaegt das Setzen von Metadaten nach dem Datei-Upload fehl, kann die bereits hochgeladene Datei als Teilimport im Ziel verbleiben. Der Fehler wird protokolliert; eine automatische Rollback-/Quarantaene-Logik ist noch nicht vorhanden.
  • Dateiversionen, Check-in/Publish/Approve-Zustaende und Listen-Versionen werden noch nicht vollstaendig rekonstruiert.
  • Grosse Listen werden derzeit als eine JSON-Datei verarbeitet; fuer sehr grosse Datenmengen fehlt noch ein paginierter Chunk-Export.
  • Sehr spezielle Feldtypen koennen je nach Farm-Konfiguration zusaetzliche Anpassungen benoetigen.
  • Das Skript ist fuer SharePoint Server 2016/2019 mit SSOM gedacht, nicht fuer SharePoint Online, CSOM, PnP PowerShell oder Subscription Edition.
  • SharePoint Server 2016 und 2019 haben das Ende des erweiterten Microsoft-Supports erreicht. Das Werkzeug unterstuetzt sie technisch weiter, ersetzt aber keine Plattform-Sicherheitsstrategie.

Empfohlene naechste Ausbaustufen

  1. Manifestgesteuerte Run-Ordner mit SHA-256-Pruefung, damit keine Altdateien oder falschen Sidecars importiert werden.
  2. Transaktionaler Dokumentstatus mit Metadaten-Save vor finalem Check-in sowie Rollback oder Quarantaene bei Teilfehlern.
  3. Zweiphasiges Lookup-Mapping ueber konfigurierbare fachliche Schluessel statt ueber lokale Item-IDs.
  4. Paginierter Listenexport und chunkweiser Import fuer grosse Listen.
  5. Einheitliches Errorlog und Abschlussreport fuer Export, Dateien, Listen und einzelne Listenelemente.
  6. Automatisierte Integrationstests auf je einer SharePoint-2016- und SharePoint-2019-Testfarm.

Empfehlung

Zuerst -ValidateEnvironment ausfuehren. Danach immer in einen neuen, leeren Ausgabeordner exportieren und die exportierten *.properties.json, Listen-JSONs und die MappingTable.json pruefen. Anschliessend Zielnamen und Feldmappings vervollstaendigen und den Import zuerst gegen ein Test-Web laufen lassen.