# Start-SPMigration PowerShell-Skript fuer die Migration von SharePoint On-Premise Inhalten ueber das serverseitige Objektmodell. 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 ## Voraussetzungen - SharePoint On-Premise Server - Ausfuehrung auf einem Server mit installiertem SharePoint PowerShell-Modul - Berechtigungen auf Quell- und Ziel-Web - PowerShell mit Zugriff auf `SharePointServer` Das Skript laedt intern: ```powershell Import-Module SharePointServer ``` ## Dateien im Projekt - `Start-SPMigration.ps1`: Hauptskript fuer Export und optionalen Import - `FieldMapping.sample.csv`: altes CSV-Beispiel fuer Feldmapping, weiterhin als Fallback lesbar - `column-mapping-defaults.sample.json`: Beispiel fuer wiederverwendbare Column-Mapping-Defaults ## Exportierte Struktur Beim Export entsteht unter `-OutputPath` folgende Struktur: ```text OutputPath |-- Files | |-- | | |-- Unterordner | | | |-- Dokument.pdf | | | |-- Dokument.pdf.properties.json |-- Lists | |-- ListeA.json |-- MappingTable.json |-- manifest.csv ``` ### Dateien - Die Originaldatei wird in `Files\\...` 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 Name des Inhaltstyps wird bewusst nicht als fuehrende Importinformation verwendet, da er sich im Zielsystem unterscheiden kann. ## 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: ```json { "SchemaVersion": 1, "GeneratedAtUtc": "2026-04-15T08:00:00.0000000Z", "SourceWebUrl": "http://sharepoint/sites/Quelle", "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: - `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 werden beim Import ueber den exportierten `TermGuid` gesetzt; dabei wird angenommen, dass derselbe Term im Ziel bereits mit identischer GUID existiert - 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. 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: ```json { "SchemaVersion": 1, "TemplateType": "ColumnMappings", "Rules": [ { "ObjectType": "*", "ContainerSourceTitle": "*", "SourceInternalName": "SecurityClearance", "TargetInternalName": "GMNSecurityClearance", "ImportSupported": true } ] } ``` ## Parameter ### Pflichtparameter - `SourceUrl`: Quell-Web fuer den Export - `TargetUrl`: Ziel-Web fuer den Import `OutputPath` ist optional. Standard ist `.\SPMigrationOutput` im aktuellen Verzeichnis. ### 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. - 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 ### Nur Export ```powershell .\Start-SPMigration.ps1 ` -SourceUrl "http://sharepoint/sites/Quelle" ` -Export ` -IncludeHiddenLists ` -IncludeHiddenLibraries ``` ### Nur Import ```powershell .\Start-SPMigration.ps1 ` -TargetUrl "http://sharepoint/sites/Ziel" ` -Import ` -MappingTable "C:\Temp\SP-Export\MappingTable.json" ``` ### Export, MappingTable anpassen, dann Import ```powershell .\Start-SPMigration.ps1 ` -SourceUrl "http://sharepoint/sites/Quelle" ` -OutputPath "C:\Temp\SP-Export" ` -Export ``` Danach `MappingTable.json` bearbeiten und dort `TargetTitle` sowie `TargetInternalName` anpassen. Anschliessend: ```powershell .\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 ```powershell .\Start-SPMigration.ps1 ` -TargetUrl "http://sharepoint/sites/Ziel" ` -Import ` -MappingTable "C:\Temp\SP-Export\MappingTable.json" ` -ImportFiles ` -Overwrite ``` ### Nur Listen importrelevant ausfuehren ```powershell .\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. - Fehlende Ziellisten oder Zielbibliotheken werden derzeit nicht automatisch angelegt; das Skript gibt stattdessen eine Warnung zur manuellen Anlage aus. - Sehr spezielle Feldtypen koennen je nach Farm-Konfiguration zusaetzliche Anpassungen benoetigen. - Das Skript ist fuer SharePoint On-Premise mit serverseitigem Objektmodell gedacht, nicht fuer SharePoint Online. ## Empfehlung Zuerst immer mit `-Export` testen und die exportierten `*.properties.json`, Listen-JSONs und die `MappingTable.json` pruefen. Danach die Zielnamen und Feldmappings vervollstaendigen und erst dann den Import gegen das Zielsystem laufen lassen.