Dans SharePoint Online, il n’existe pas d’outil natif permettant d’obtenir une vue exhaustive et fiable des permissions à l’échelle d’un tenant. Dès qu’un environnement a quelques années de vie, les permissions uniques se multiplient, les accès directs apparaissent et la gouvernance devient difficile à maintenir.
Dans certains contextes, un audit partiel ne suffit pas. Lors d’un audit de conformité, d’une migration tenant à tenant ou d’un incident de sécurité, il faut être capable de répondre précisément à une question simple : qui a accès à quoi, à quel niveau.
Cet article présente un script PowerShell qui parcourt l’ensemble d’un tenant SharePoint Online et exporte les permissions au niveau :
- site
- bibliothèque de documents
- dossier et fichier (optionnel)
Le script est basé sur PnP.PowerShell, car c’est aujourd’hui la seule approche permettant de lire fidèlement le modèle de permissions SharePoint (héritage, rôles, groupes SharePoint).
Ce que fait exactement le script
Le script réalise les actions suivantes :
- Connexion au tenant admin SharePoint
- Récupération de tous les sites SharePoint (hors OneDrive)
- Connexion à chaque site
- Parcours de toutes les bibliothèques de documents visibles
- Export des permissions au niveau bibliothèque
- Optionnellement, parcours des fichiers et dossiers
- Export uniquement des items ayant des permissions uniques (ou tout, si demandé)
- Génération d’un CSV exploitable
Prérequis
Install-Module PnP.PowerShell -Scope CurrentUser
Le script utilise une authentification interactive, ce qui est le plus sûr pour un audit ponctuel.
Script PowerShell – Inventaire complet des permissions SharePoint Online
Copie-colle tel quel dans un fichierSPO-Permissions-Full-Audit.ps1
#requires -Modules PnP.PowerShell
param(
[Parameter(Mandatory=$true)]
[string]$TenantAdminUrl, # https://contoso-admin.sharepoint.com
[Parameter(Mandatory=$false)]
[string]$OutputCsv = "C:\Temp\SPO_Permissions_Report.csv",
[Parameter(Mandatory=$false)]
[switch]$ScanItemLevel,
[Parameter(Mandatory=$false)]
[switch]$IncludeInheritedPermissions,
[Parameter(Mandatory=$false)]
[int]$PageSize = 500
)
function Write-ReportRow {
param(
[string]$SiteUrl,
[string]$Library,
[string]$ObjectType,
[string]$Title,
[string]$ObjectUrl,
[bool]$HasUniquePermissions,
[string]$Principal,
[string]$PrincipalType,
[string]$PermissionLevels,
[string]$GrantedThrough
)
[pscustomobject]@{
SiteUrl = $SiteUrl
Library = $Library
ObjectType = $ObjectType
Title = $Title
Url = $ObjectUrl
HasUniquePermissions = $HasUniquePermissions
Principal = $Principal
PrincipalType = $PrincipalType
PermissionLevels = $PermissionLevels
GrantedThrough = $GrantedThrough
} | Export-Csv -Path $OutputCsv -Append -NoTypeInformation
}
function Get-ObjectUrl {
param(
[Microsoft.SharePoint.Client.SecurableObject]$Object,
[Microsoft.SharePoint.Client.Web]$Web
)
$baseUrl = $Web.Url.Replace($Web.ServerRelativeUrl, "")
if ($Object -is [Microsoft.SharePoint.Client.List]) {
$root = Get-PnPProperty -ClientObject $Object -Property RootFolder
return $baseUrl + $root.ServerRelativeUrl
}
if ($Object -is [Microsoft.SharePoint.Client.ListItem]) {
if ($Object.FileSystemObjectType -eq "Folder") {
$folder = Get-PnPProperty -ClientObject $Object -Property Folder
return $baseUrl + $folder.ServerRelativeUrl
}
else {
Get-PnPProperty -ClientObject $Object -Property File
return $baseUrl + $Object.File.ServerRelativeUrl
}
}
}
function Export-Permissions {
param(
[Microsoft.SharePoint.Client.SecurableObject]$Object,
[Microsoft.SharePoint.Client.Web]$Web,
[string]$SiteUrl,
[string]$LibraryName
)
Get-PnPProperty -ClientObject $Object -Property HasUniqueRoleAssignments, RoleAssignments
$hasUnique = $Object.HasUniqueRoleAssignments
foreach ($ra in $Object.RoleAssignments) {
Get-PnPProperty -ClientObject $ra -Property Member, RoleDefinitionBindings
$roles = ($ra.RoleDefinitionBindings | Select-Object -ExpandProperty Name) |
Where-Object { $_ -ne "Limited Access" }
if (-not $roles) { continue }
$objectType = if ($Object -is [Microsoft.SharePoint.Client.List]) {
"Library"
}
elseif ($Object.FileSystemObjectType -eq "Folder") {
"Folder"
}
else {
"File"
}
$title = if ($Object -is [Microsoft.SharePoint.Client.List]) {
$Object.Title
}
else {
$Object["FileLeafRef"]
}
Write-ReportRow `
-SiteUrl $SiteUrl `
-Library $LibraryName `
-ObjectType $objectType `
-Title $title `
-ObjectUrl (Get-ObjectUrl -Object $Object -Web $Web) `
-HasUniquePermissions $hasUnique `
-Principal $ra.Member.Title `
-PrincipalType $ra.Member.PrincipalType `
-PermissionLevels ($roles -join ",") `
-GrantedThrough "Direct or Group"
}
}
# --- EXECUTION ---
if (Test-Path $OutputCsv) { Remove-Item $OutputCsv -Force }
Connect-PnPOnline -Url $TenantAdminUrl -Interactive
$sites = Get-PnPTenantSite -IncludeOneDriveSites:$false
foreach ($site in $sites) {
Write-Host "Scanning site $($site.Url)" -ForegroundColor Cyan
Connect-PnPOnline -Url $site.Url -Interactive
$web = Get-PnPWeb
$libraries = Get-PnPList | Where-Object {
$_.BaseTemplate -eq 101 -and -not $_.Hidden
}
foreach ($lib in $libraries) {
Export-Permissions -Object $lib -Web $web -SiteUrl $site.Url -LibraryName $lib.Title
if ($ScanItemLevel) {
$items = Get-PnPListItem -List $lib -PageSize $PageSize
foreach ($item in $items) {
if ($IncludeInheritedPermissions -or $item.HasUniqueRoleAssignments) {
Export-Permissions -Object $item -Web $web -SiteUrl $site.Url -LibraryName $lib.Title
}
}
}
}
}
Write-Host "Audit completed. CSV generated at $OutputCsv" -ForegroundColor Green
Comment exécuter le script
Mode recommandé (bibliothèques + items avec permissions uniques)
.\SPO-Permissions-Full-Audit.ps1
-TenantAdminUrl "https://contoso-admin.sharepoint.com"
-ScanItemLevel
Mode exhaustif (très lourd)
.\SPO-Permissions-Full-Audit.ps1
-TenantAdminUrl "https://contoso-admin.sharepoint.com"
-ScanItemLevel
-IncludeInheritedPermissions
Ce que tu obtiens réellement
Un CSV qui permet enfin de répondre à :
- où sont les permissions uniques
- quels fichiers ou dossiers contournent l’héritage
- quels utilisateurs ont des accès directs
- quelles bibliothèques sont mal gouvernées
C’est exactement ce que les audits demandent.
Bonnes pratiques pour exploiter réellement un inventaire des permissions SharePoint
Un inventaire complet des accès SharePoint n’est pas une fin en soi. Sa valeur ne réside pas dans la quantité de lignes du CSV, mais dans les décisions qu’il permet de prendre. Sans cadre clair, ce type de rapport devient rapidement un document oublié, trop volumineux pour être exploité.
La première bonne pratique consiste à se concentrer sur les exceptions. Les permissions héritées sont rarement le problème. Ce sont les permissions uniques, les accès directs et les contournements du modèle standard qui créent le risque. C’est pour cette raison qu’un scan ciblant en priorité les objets avec des permissions uniques est généralement plus efficace qu’un inventaire aveugle de tout l’héritage.
La seconde bonne pratique est de réduire les accès directs au minimum. Lorsqu’un utilisateur apparaît directement dans les permissions d’une bibliothèque ou d’un fichier, cela indique presque toujours un contournement de la gouvernance. À moyen terme, ces accès deviennent invisibles et difficiles à maintenir. Les remplacer par des groupes, idéalement des groupes Entra ID, simplifie énormément la gestion et le nettoyage futur.

Il est également essentiel de limiter la prolifération des groupes SharePoint historiques. Beaucoup d’environnements conservent encore des groupes créés il y a plusieurs années, dont personne ne maîtrise réellement la composition. Une revue régulière de ces groupes, ou leur remplacement par des groupes Entra ID bien nommés et bien gouvernés, permet de reprendre le contrôle sans tout casser.
Une autre bonne pratique consiste à classer les bibliothèques et sites sensibles. Tous les contenus n’ont pas le même niveau de criticité. Un inventaire de permissions est beaucoup plus utile lorsqu’il est croisé avec une notion de sensibilité ou de criticité métier. Cela permet de concentrer les efforts de remédiation là où l’impact potentiel est réel.
Enfin, il faut accepter qu’un inventaire exhaustif n’est pas un outil de surveillance continue. C’est un outil de diagnostic ponctuel. Pour une gouvernance durable, il doit être complété par des contrôles plus fréquents et plus légers, par exemple des audits réguliers du partage moderne via Microsoft Graph, ou des règles de prévention sur le partage externe.
Savoir “qui a accès à quoi” dans SharePoint Online reste un enjeu central pour la sécurité et la gouvernance Microsoft 365. Lorsqu’un environnement devient complexe, un inventaire complet des permissions est parfois indispensable pour repartir sur des bases saines.
Un script comme celui présenté ici permet d’obtenir une photographie fidèle de la réalité, à condition de l’utiliser avec méthode et discernement. L’objectif n’est pas de produire un rapport impressionnant, mais de réduire durablement la complexité, le risque et la dette de gouvernance.
Un bon inventaire est celui qui mène à moins d’exceptions, moins d’accès directs et un modèle de permissions plus simple à expliquer… et à maintenir.
