HomeMicrosoft 365Inventorier tous les accès SharePoint Online

Inventorier tous les accès SharePoint Online

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 :

  1. Connexion au tenant admin SharePoint
  2. Récupération de tous les sites SharePoint (hors OneDrive)
  3. Connexion à chaque site
  4. Parcours de toutes les bibliothèques de documents visibles
  5. Export des permissions au niveau bibliothèque
  6. Optionnellement, parcours des fichiers et dossiers
  7. Export uniquement des items ayant des permissions uniques (ou tout, si demandé)
  8. 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 fichier
SPO-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.

Charles Jenkins
Charles Jenkinshttps://trivium365.com/
Fort de plus de 15 ans d’expérience, il accompagne les organisations dans l’adoption des outils Microsoft pour optimiser leur collaboration et leur productivité. Passionné par l’innovation et l’amélioration continue, il met son expertise au service des équipes pour les aider à tirer le meilleur parti de la transformation numérique.