# SharePointHelper.psm1 -- Modul fuer SharePoint-Dokumentbibliothek-Operationen via Graph API function Resolve-SharePointSite { <# .SYNOPSIS Loest eine SharePoint-Site-URL in eine Site-ID auf. .PARAMETER Settings Zugangsdaten-Hashtable (TenantId, ClientId, ClientSecret) .PARAMETER SiteUrl SharePoint-Site-URL (z.B. "krahgruppe.sharepoint.com" oder "krahgruppe.sharepoint.com:/sites/IT") #> param( [Parameter(Mandatory)] [hashtable]$Settings, [Parameter(Mandatory)] [string]$SiteUrl ) # URL normalisieren $siteUrl = $SiteUrl -replace '^https?://', '' # Hostname und Pfad trennen if ($siteUrl -match '^([^:/]+):?(.*)$') { $hostname = $Matches[1].TrimEnd('/') $sitePath = $Matches[2].TrimStart(':').TrimStart('/').TrimEnd('/') } if ($sitePath) { $endpoint = "/sites/${hostname}:/${sitePath}" } else { $endpoint = "/sites/${hostname}" } $site = Invoke-GraphRequest -Endpoint $endpoint -Settings $Settings Write-Log "SharePoint Site aufgeloest: $($site.displayName) (ID: $($site.id))" -Level Debug return $site } function Get-DocumentLibrary { <# .SYNOPSIS Findet eine Dokumentbibliothek (Drive) auf einer SharePoint-Site. .PARAMETER LibraryName Name oder relativer Pfad der Bibliothek #> param( [Parameter(Mandatory)] [hashtable]$Settings, [Parameter(Mandatory)] [string]$SiteId, [string]$LibraryName = $null ) $drives = Invoke-GraphRequest -Endpoint "/sites/$SiteId/drives" -Settings $Settings if ($LibraryName) { $drive = $drives.value | Where-Object { $_.name -eq $LibraryName -or $_.webUrl -like "*$LibraryName*" } | Select-Object -First 1 } else { # Standard-Dokumentbibliothek $drive = $drives.value | Select-Object -First 1 } if (-not $drive) { throw "Dokumentbibliothek '$LibraryName' nicht gefunden auf Site $SiteId" } Write-Log "Dokumentbibliothek gefunden: $($drive.name) (ID: $($drive.id))" -Level Debug return $drive } function Upload-SharePointDocument { <# .SYNOPSIS Laedt ein Dokument in eine SharePoint-Dokumentbibliothek hoch. .PARAMETER DriveId Die Drive-ID der Dokumentbibliothek .PARAMETER TargetFolder Zielordner innerhalb der Bibliothek (z.B. "2025/Wartung") .PARAMETER FilePath Lokaler Pfad zur Datei .PARAMETER FileName Optionaler Dateiname (Standard: Original-Dateiname) .OUTPUTS Das erstellte DriveItem-Objekt #> param( [Parameter(Mandatory)] [hashtable]$Settings, [Parameter(Mandatory)] [string]$DriveId, [string]$TargetFolder = "", [Parameter(Mandatory)] [string]$FilePath, [string]$FileName = $null ) if (-not (Test-Path $FilePath)) { throw "Datei nicht gefunden: $FilePath" } if (-not $FileName) { $FileName = [IO.Path]::GetFileName($FilePath) } # Pfad zusammenbauen $uploadPath = if ($TargetFolder) { "$TargetFolder/$FileName" } else { $FileName } $uploadPath = $uploadPath -replace '\\', '/' $endpoint = "/drives/$DriveId/root:/${uploadPath}:/content" # Token holen $token = Get-GraphToken -Settings $Settings $headers = @{ Authorization = "Bearer $token" "Content-Type" = "application/octet-stream" } $uri = "https://graph.microsoft.com/v1.0$endpoint" $fileBytes = [IO.File]::ReadAllBytes($FilePath) # Fuer Dateien > 4MB muesste ein Upload-Session verwendet werden if ($fileBytes.Length -gt 4MB) { Write-Log "Datei groesser als 4MB -- verwende Upload-Session" -Level Info return Upload-LargeFile -Settings $Settings -DriveId $DriveId -UploadPath $uploadPath -FilePath $FilePath } try { $response = Invoke-RestMethod -Uri $uri -Method PUT -Headers $headers -Body $fileBytes Write-Log "Dokument hochgeladen: $uploadPath (ID: $($response.id))" -Level Info return $response } catch { Write-Log "Fehler beim Upload von '$FileName': $_" -Level Error throw } } function Upload-LargeFile { <# .SYNOPSIS Laedt grosse Dateien (>4MB) per Upload-Session hoch. #> param( [Parameter(Mandatory)] [hashtable]$Settings, [Parameter(Mandatory)] [string]$DriveId, [Parameter(Mandatory)] [string]$UploadPath, [Parameter(Mandatory)] [string]$FilePath ) # Upload-Session erstellen $sessionEndpoint = "/drives/$DriveId/root:/${UploadPath}:/createUploadSession" $session = Invoke-GraphRequest -Endpoint $sessionEndpoint -Method POST -Body @{ item = @{ "@microsoft.graph.conflictBehavior" = "rename" } } -Settings $Settings $uploadUrl = $session.uploadUrl $fileBytes = [IO.File]::ReadAllBytes($FilePath) $fileSize = $fileBytes.Length $chunkSize = 10MB $offset = 0 while ($offset -lt $fileSize) { $end = [Math]::Min($offset + $chunkSize - 1, $fileSize - 1) $chunk = $fileBytes[$offset..$end] $contentRange = "bytes $offset-$end/$fileSize" $headers = @{ "Content-Range" = $contentRange "Content-Length" = $chunk.Length } $response = Invoke-RestMethod -Uri $uploadUrl -Method PUT -Headers $headers -Body $chunk $offset = $end + 1 } Write-Log "Grosse Datei hochgeladen: $UploadPath" -Level Info return $response } function Set-SharePointItemFields { <# .SYNOPSIS Setzt Metadaten-Felder auf einem SharePoint-Listenelement / Dokument. .PARAMETER SiteId Die Site-ID .PARAMETER ListId Die Listen-/Bibliotheks-ID (kann auch der Drive-Name sein) .PARAMETER ItemId Die Item-ID (listItem.id vom DriveItem) .PARAMETER Fields Hashtable mit Feldnamen und Werten .EXAMPLE Set-SharePointItemFields -Settings $s -SiteId $sid -DriveId $did -DriveItemId $diid -Fields @{ Datum = "2025-11-28"; Kommentar = "Backup-Check" } #> param( [Parameter(Mandatory)] [hashtable]$Settings, [Parameter(Mandatory)] [string]$DriveId, [Parameter(Mandatory)] [string]$DriveItemId, [Parameter(Mandatory)] [hashtable]$Fields ) $endpoint = "/drives/$DriveId/items/$DriveItemId/listItem/fields" try { $result = Invoke-GraphRequest -Endpoint $endpoint -Method PATCH -Body $Fields -Settings $Settings Write-Log "Metadaten aktualisiert fuer Item $DriveItemId : $($Fields.Keys -join ', ')" -Level Info return $result } catch { Write-Log "Fehler beim Setzen der Metadaten: $_" -Level Error throw } } function Invoke-SharePointUpload { <# .SYNOPSIS Hauptfunktion: Laedt eine Datei in eine SharePoint-Dokumentbibliothek und setzt Metadaten anhand einer Target-Konfiguration. .PARAMETER TargetConfig Ein Target-Objekt aus sharepoint-targets.json .PARAMETER FilePath Lokaler Pfad zur Datei .PARAMETER Metadata Hashtable mit Metadaten-Werten (Keys = Feldnamen aus der Target-Config) .PARAMETER SubFolder Optionaler Unterordner (z.B. "{year}" wird aufgeloest) #> param( [Parameter(Mandatory)] [hashtable]$Settings, [Parameter(Mandatory)] $TargetConfig, [Parameter(Mandatory)] [string]$FilePath, [hashtable]$Metadata = @{}, [string]$FileName = $null ) # 1. Site aufloesen $site = Resolve-SharePointSite -Settings $Settings -SiteUrl $TargetConfig.siteUrl # 2. Dokumentbibliothek finden $drive = Get-DocumentLibrary -Settings $Settings -SiteId $site.id -LibraryName $TargetConfig.libraryName # 3. Zielordner bestimmen $targetFolder = $TargetConfig.targetFolder if ($targetFolder) { # Platzhalter in Ordnerpfad aufloesen $now = Get-Date $targetFolder = $targetFolder ` -replace '\{year\}', $now.ToString("yyyy") ` -replace '\{month\}', $now.ToString("MM") ` -replace '\{date\}', $now.ToString("yyyy-MM-dd") } # 4. Datei hochladen $driveItem = Upload-SharePointDocument ` -Settings $Settings ` -DriveId $drive.id ` -TargetFolder $targetFolder ` -FilePath $FilePath ` -FileName $FileName # 5. Metadaten setzen (aus Config-Defaults + uebergebene Werte) $allFields = @{} # Defaults aus der Target-Config if ($TargetConfig.defaultFields) { foreach ($prop in $TargetConfig.defaultFields.PSObject.Properties) { $value = $prop.Value # Platzhalter ersetzen $now = Get-Date $value = $value ` -replace '\{today\}', $now.ToString("yyyy-MM-dd") ` -replace '\{now\}', $now.ToString("yyyy-MM-ddTHH:mm:ssZ") ` -replace '\{year\}', $now.ToString("yyyy") ` -replace '\{month\}', $now.ToString("MM") ` -replace '\{filename\}', [IO.Path]::GetFileNameWithoutExtension($FilePath) $allFields[$prop.Name] = $value } } # Uebergebene Metadaten (ueberschreiben Defaults) foreach ($key in $Metadata.Keys) { $allFields[$key] = $Metadata[$key] } if ($allFields.Count -gt 0) { Set-SharePointItemFields ` -Settings $Settings ` -DriveId $drive.id ` -DriveItemId $driveItem.id ` -Fields $allFields } return @{ DriveItemId = $driveItem.id WebUrl = $driveItem.webUrl FileName = $driveItem.name Fields = $allFields } } Export-ModuleMember -Function Resolve-SharePointSite, Get-DocumentLibrary, Upload-SharePointDocument, Set-SharePointItemFields, Invoke-SharePointUpload