337 lines
9.8 KiB
PowerShell
337 lines
9.8 KiB
PowerShell
# 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
|