Gérer SharePoint avec PowerShell et Microsoft Graph
Introduction
Faitza_Sharepoint.ps1 automatise les opérations courantes sur SharePoint Online
via l'API Microsoft Graph : dépôt de rapports, récupération de fichiers de référence,
navigation dans les bibliothèques et lecture/écriture de listes (CMDB, catalogue, registre…).
Toutes les fonctions utilisent Get-Faitza_Modul_Headers -resource "graph" pour
obtenir un Bearer token valide. Voir l'article dédié :
Get-Faitza_Modul_Headers — Token Bearer pour les APIs Microsoft.
Send-Faitza_Sharepoint_File
Droits requis — Sites.ReadWrite.All · Files.ReadWrite.All
Dépose un fichier local dans une bibliothèque SharePoint via l'endpoint
/drive/root:/{path}:/content. Gère le cas de fichier verrouillé avec le switch
-Force (suppression puis re-upload).
function Send-Faitza_Sharepoint_File {
[CmdletBinding()]
param (
[string]$sp,
[string]$lo_file,
[string]$sp_folder = "",
[switch]$Force
)
$response = $null
try {
Write-Host "$($PSStyle.Background.Red)>>> Send-Faitza_SP_File <<<$($PSStyle.Reset)"
if (-not $sp) { throw "Il manque le nom du sp -sp" }
if (-not $lo_file) { throw "Il manque le fichier local -lo_file" }
$headers = Get-Faitza_Modul_Headers -resource "graph"
$site = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "https://graph.microsoft.com/v1.0/sites/faitza.sharepoint.com:/sites/$sp"
$fileName = Split-Path $lo_file -Leaf
if ([string]::IsNullOrWhiteSpace($sp_folder)) {
$sp_path = $fileName
} else {
$sp_path = "$($sp_folder.Trim("/"))/$fileName"
}
$uri_upload = "https://graph.microsoft.com/v1.0/sites/$($site.id)/drive/root:/$($sp_path):/[email protected]=replace"
$lo_file_bytes = [System.IO.File]::ReadAllBytes($lo_file)
try {
$response = Invoke-RestMethod -Method Put -Uri $uri_upload -Headers $headers -Body $lo_file_bytes -ContentType "application/octet-stream" -ErrorAction Stop
} catch {
if ($Force -and $_ -match "resourceLocked") {
Write-Host "Fichier verrouillé. Tentative de suppression forcée avant re-upload..." -ForegroundColor Yellow
$uri_item = "https://graph.microsoft.com/v1.0/sites/$($site.id)/drive/root:/$($sp_path)"
Invoke-RestMethod -Method Delete -Uri $uri_item -Headers $headers -ErrorAction Stop
Write-Host "Fichier supprimé. Re-upload en cours..." -ForegroundColor Yellow
$response = Invoke-RestMethod -Method Put -Uri $uri_upload -Headers $headers -Body $lo_file_bytes -ContentType "application/octet-stream" -ErrorAction Stop
} else {
throw "Fichier verrouillé"
}
}
Write-Host "Fichier uploadé avec succès dans $sp : $($response.name)"
return @{ url = $response.webUrl }
} catch {
throw "Erreur Send-Faitza_Sharepoint_File : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Send-Faitza_SP_File >>>$($PSStyle.Reset)"
}
}
Send-Faitza_Sharepoint_File `
-sp "Nom du sp" `
-lo_file "chemin du fichier local" `
-sp_folder "chemin du dossier sp"
Get-Faitza_Sharepoint_File
Droits requis — Sites.Read.All · Files.Read.All
Récupère un fichier depuis SharePoint. Trois modes disponibles : télécharger vers un fichier local,
importer directement comme objet Excel (-excel via le module ImportExcel),
ou parser directement comme JSON (-json). Sans flag, retourne le chemin local du fichier téléchargé.
function Get-Faitza_Sharepoint_File {
[CmdletBinding()]
param (
[string]$sp,
[string]$lo_file,
[string]$sp_file,
[switch]$excel,
[switch]$json,
[string[]]$WorkSheetName
)
$data = $null
try {
Write-Host "$($PSStyle.Background.Red)>>> Get-Faitza_Sharepoint_File <<<$($PSStyle.Reset)"
if (-not $sp) { throw "Il manque le nom du sp -sp" }
if (-not $sp_file) { throw "Il manque le chemin SharePoint -sp_file" }
if ($excel -and -not $lo_file) {
$lo_file = Join-Path $env:AGENT_TEMPDIRECTORY "temp_graph_$(Get-Random).xlsx"
} elseif ($json -and -not $lo_file) {
$lo_file = Join-Path $env:AGENT_TEMPDIRECTORY "temp_graph_$(Get-Random).json"
} elseif (-not $lo_file) {
$sp_path_raw = $sp_file.Trim("/") -replace "(?i)^sites/$sp/", ""
$rawFileName = Split-Path $sp_path_raw -Leaf
$normalized = $rawFileName.Normalize([System.Text.NormalizationForm]::FormD)
$cleanName = ($normalized.ToCharArray() | Where-Object { [System.Globalization.CharUnicodeInfo]::GetUnicodeCategory($_) -ne [System.Globalization.UnicodeCategory]::NonSpacingMark }) -join ''
$cleanName = $cleanName -replace '\s+', ' ' -replace '[^a-zA-Z0-9\s\.\-_]', ''
$lo_file = Join-Path $env:AGENT_TEMPDIRECTORY $cleanName.Trim()
}
$headers = Get-Faitza_Modul_Headers -resource "graph"
$site = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "https://graph.microsoft.com/v1.0/sites/faitza.sharepoint.com:/sites/$sp"
$sp_path_raw = $sp_file.Trim("/") -replace "(?i)^sites/$sp/", ""
$sp_path_encoded = ($sp_path_raw -split '/' | ForEach-Object { [uri]::EscapeDataString($_) }) -join '/'
$uri_download = "https://graph.microsoft.com/v1.0/sites/$($site.id)/drive/root:/$($sp_path_encoded):/content"
Invoke-RestMethod -Method Get -Uri $uri_download -Headers $headers -OutFile $lo_file
if (-not (Test-Path -LiteralPath $lo_file)) { throw "Fichier non créé sur disque" }
Write-Host "Fichier telecharge : $(Split-Path $lo_file -Leaf)"
if ($excel) {
if ($WorkSheetName -and $WorkSheetName.Count -gt 0) {
if ($WorkSheetName.Count -eq 1) {
$data = Import-Excel -Path $lo_file -WorkSheetName $WorkSheetName[0]
} else {
$data = @{}
foreach ($sheet in $WorkSheetName) {
Write-Host "Sheet Excel : $sheet"
$data[$sheet] = Import-Excel -Path $lo_file -WorkSheetName $sheet
}
}
} else {
$data = Import-Excel -Path $lo_file
}
Remove-Item $lo_file -ErrorAction SilentlyContinue
} elseif ($json) {
$data = Get-Content -Path $lo_file -Raw | ConvertFrom-Json
Remove-Item $lo_file -ErrorAction SilentlyContinue
} else {
$data = $lo_file
}
if ($data) { return $data }
} catch {
throw "Erreur Get-Faitza_Sharepoint_File : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Get-Faitza_Sharepoint_File >>>$($PSStyle.Reset)"
}
}
Get-Faitza_Sharepoint_File
-sp "Nom du sp" `
-lo_file "chemin du fichier local" `
-excel `
-json `
-sp_file "chemin du fichier sp"
Get-Faitza_Sharepoint_FolderIndex
Droits requis — Sites.Read.All · Files.Read.All
Liste récursivement le contenu d'un dossier SharePoint. S'appelle elle-même pour descendre dans les sous-dossiers.
Gère la pagination @odata.nextLink à chaque niveau.
function Get-Faitza_Sharepoint_FolderIndex {
[CmdletBinding()]
param (
[string]$sp,
[string]$sp_folder,
[string]$siteId
)
$isTopLevel = -not $siteId
try {
if ($isTopLevel) {
Write-Host "$($PSStyle.Background.Red)>>> Get-Faitza_SP_FolderIndex (Recursive) <<<$($PSStyle.Reset)"
if (-not $sp) { throw "Il manque le nom du sp -sp" }
$headers = Get-Faitza_Modul_Headers -resource "graph"
$site = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "https://graph.microsoft.com/v1.0/sites/faitza.sharepoint.com:/sites/$sp"
$siteId = $site.id
} else {
$headers = Get-Faitza_Modul_Headers -resource "graph"
}
$path = $sp_folder.Trim("/")
if ($path -eq "" -or $path -eq "Documents") {
$uri = "https://graph.microsoft.com/v1.0/sites/$siteId/drive/root/children"
} else {
$uri = "https://graph.microsoft.com/v1.0/sites/$siteId/drive/root:/$($path):/children"
}
$results = @()
do {
$response = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers
foreach ($item in $response.value) {
if ($item.file) {
$results += $item
} elseif ($item.folder) {
Write-Host " -> Entrée dans : $($item.name)"
$results += Get-Faitza_Sharepoint_FolderIndex -sp $sp -sp_folder "$path/$($item.name)" -siteId $siteId
}
}
$uri = $response.'@odata.nextLink'
} while ($uri)
return $results
} catch {
throw "Erreur Get-Faitza_Sharepoint_FolderIndex : $_"
} finally {
if ($isTopLevel) {
Write-Host "$($PSStyle.Background.Red)<<< Get-Faitza_SP_FolderIndex >>>$($PSStyle.Reset)"
}
}
}
Get-Faitza_Sharepoint_FolderIndex `
-sp "Nom du sp" `
-sp_folder "chemin du dossier sp a indexer"
Get-Faitza_Sharepoint_List
Droits requis — Sites.Read.All
Récupère tous les items d'une liste SharePoint par son nom affiché. Résout l'ID de liste dynamiquement,
gère la pagination et retourne les champs fields.* aplatis avec l'ID SharePoint de chaque item.
function Get-Faitza_Sharepoint_List {
[CmdletBinding()]
param (
[string]$sp,
[string]$sp_list
)
$results = @()
try {
Write-Host "$($PSStyle.Background.Red)>>> Get-Faitza_Sharepoint_List <<<$($PSStyle.Reset)"
if (-not $sp) { throw "Il manque le nom du sp -sp" }
if (-not $sp_list) { throw "Il manque le nom de la liste -sp_list" }
$headers = Get-Faitza_Modul_Headers -resource "graph"
$site = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "https://graph.microsoft.com/v1.0/sites/faitza.sharepoint.com:/sites/$sp"
$listQueryUri = "https://graph.microsoft.com/v1.0/sites/$($site.id)/lists?`$filter=displayName eq '$sp_list'"
$listResponse = Invoke-RestMethod -Method Get -Uri $listQueryUri -Headers $headers
if (-not $listResponse.value) { throw "La liste '$sp_list' est introuvable sur le site $sp" }
$listId = $listResponse.value[0].id
$uri = "https://graph.microsoft.com/v1.0/sites/$($site.id)/lists/$listId/items?`$expand=fields"
do {
$response = Invoke-RestMethod -Method Get -Uri $uri -Headers $headers
foreach ($item in $response.value) {
$fieldData = $item.fields
$fieldData | Add-Member -MemberType NoteProperty -Name "SharePointItemId" -Value $item.id -Force
$results += $fieldData
}
$uri = $response.'@odata.nextLink'
} while ($uri)
Write-Host "$($results.Count) éléments récupérés avec succès dans la liste '$sp_list'"
return $results
} catch {
throw "Erreur Get-Faitza_Sharepoint_List : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Get-Faitza_Sharepoint_List >>>$($PSStyle.Reset)"
}
}
Get-Faitza_Sharepoint_List `
-sp "Nom du sp" `
-sp_list "Nom de la liste"
Set-Faitza_Sharepoint_ListItem
Droits requis — Sites.ReadWrite.All
Met à jour un ou plusieurs champs d'un item de liste SharePoint via PATCH Graph.
Le paramètre -sp_item_id correspond à la valeur SharePointItemId
retournée par Get-Faitza_Sharepoint_List.
function Set-Faitza_Sharepoint_ListItem {
[CmdletBinding()]
param (
[string]$sp,
[string]$sp_list,
[string]$sp_item_id,
[hashtable]$sp_item_values
)
$response = $null
try {
Write-Host "$($PSStyle.Background.Red)>>> Set-Faitza_Sharepoint_ListItem <<<$($PSStyle.Reset)"
if (-not $sp) { throw "Il manque le nom du sp -sp" }
if (-not $sp_list) { throw "Il manque le nom de la liste -sp_list" }
if (-not $sp_item_id) { throw "Il manque l'ID de l'élément -sp_item_id" }
if (-not $sp_item_values) { throw "Il manque les valeurs à mettre à jour -sp_item_values" }
$headers = Get-Faitza_Modul_Headers -resource "graph"
$site = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "https://graph.microsoft.com/v1.0/sites/faitza.sharepoint.com:/sites/$sp"
$listQueryUri = "https://graph.microsoft.com/v1.0/sites/$($site.id)/lists?`$filter=displayName eq '$sp_list'"
$listResponse = Invoke-RestMethod -Method Get -Uri $listQueryUri -Headers $headers
if (-not $listResponse.value) { throw "La liste '$sp_list' est introuvable sur le site $sp" }
$listId = $listResponse.value[0].id
$uri = "https://graph.microsoft.com/v1.0/sites/$($site.id)/lists/$listId/items/$sp_item_id/fields"
$jsonBody = $sp_item_values | ConvertTo-Json
$response = Invoke-RestMethod -Method Patch -Uri $uri -Headers $headers -Body $jsonBody -ContentType "application/json"
Write-Host "Élément $sp_item_id mis à jour avec succès dans '$sp_list'."
return $response
} catch {
throw "Erreur Set-Faitza_Sharepoint_ListItem : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Set-Faitza_Sharepoint_ListItem >>>$($PSStyle.Reset)"
}
}
Set-Faitza_Sharepoint_ListItem
-sp "Nom du sp" `
-sp_list "Nom de la liste" `
-sp_item_id "ID de l'élément à modifier" `
-sp_item_values @{"Colonne1"="Valeur1"; "Colonne2"="Valeur2"}
Remove-Faitza_Sharepoint_File
Droits requis — Sites.ReadWrite.All · Files.ReadWrite.All
Supprime un fichier depuis SharePoint via DELETE Graph. Le chemin est encodé segment par segment pour gérer les espaces et caractères spéciaux.
function Remove-Faitza_Sharepoint_File {
[CmdletBinding()]
param (
[string]$sp,
[string]$sp_file
)
try {
Write-Host "$($PSStyle.Background.Red)>>> Remove-Faitza_SP_File <<<$($PSStyle.Reset)"
if (-not $sp) { throw "Il manque le nom du sp -sp" }
if (-not $sp_file) { throw "Il manque le chemin SharePoint -sp_file" }
$headers = Get-Faitza_Modul_Headers -resource "graph"
$site = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "https://graph.microsoft.com/v1.0/sites/faitza.sharepoint.com:/sites/$sp"
$sp_path_raw = $sp_file.Trim("/") -replace "(?i)^sites/$sp/", ""
$sp_path_encoded = ($sp_path_raw -split '/' | ForEach-Object { [uri]::EscapeDataString($_) }) -join '/'
$uri_delete = "https://graph.microsoft.com/v1.0/sites/$($site.id)/drive/root:/$($sp_path_encoded)"
Invoke-RestMethod -Method Delete -Uri $uri_delete -Headers $headers
Write-Host "Fichier supprimé avec succès dans $sp : $sp_file"
} catch {
throw "Erreur Remove-Faitza_Sharepoint_File : $_"
} finally {
Write-Host "$($PSStyle.Background.Red)<<< Remove-Faitza_SP_File >>>$($PSStyle.Reset)"
}
}
Remove-Faitza_Sharepoint_File `
-sp "Nom du sp" `
-sp_file "chemin/du/fichier.ext"
Pièges classiques
Les espaces et accents dans les noms de fichiers/dossiers doivent être encodés segment par segment.
La fonction utilise [uri]::EscapeDataString() sur chaque segment séparément —
pas sur le chemin entier, ce qui écaserait les /.
SharePoint peut verrouiller un fichier ouvert en coédition. Le switch -Force de
Send-Faitza_Sharepoint_File supprime le fichier existant avant de re-uploader —
à utiliser uniquement en automatisation où aucun utilisateur n'est en train d'éditer le fichier.
Sites.ReadWrite.All donne accès à tous les sites SharePoint du tenant.
Préférer Sites.Selected pour limiter l'accès aux seuls sites nécessaires.
Le flag -excel de Get-Faitza_Sharepoint_File utilise le module
ImportExcel (Doug Finke). Il doit être installé :
Install-Module ImportExcel -Scope CurrentUser.
// Commentaires
Aucun commentaire pour l'instant.