Src/Private/Get-AbrSPSitePermissions.ps1
|
function Get-AbrSPSitePermissions { <# .SYNOPSIS Collects detailed site-level and library-level permissions for all site collections and exports them to Excel. Includes users, groups, permission levels, and unique permissions on document libraries. .DESCRIPTION For each site collection: - Site-level role assignments (users and groups with their permission levels) - SharePoint groups and their membership - Document libraries with unique (broken) permissions - Library-level role assignments where unique permissions exist This data is Excel-only (too large for a Word report). Adds these sheets to $script:ExcelSheets: - 'Site Role Assignments' -- who has what on each site - 'SP Groups & Members' -- SharePoint group membership - 'Library Permissions' -- libraries with unique perms .NOTES Version: 0.1.0 Author: Pai Wei Sing PERFORMANCE NOTE: Retrieving permissions for large tenants (100+ sites) can be slow. This function is called only when InfoLevel.SiteCollections >= 2. A cap of 30 sites is applied by default to avoid very long run times. Adjust $MaxSites to increase coverage. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$TenantId, [string]$TenantRootUrl, # Sites to process (filtered list, already retrieved by SiteCollections section) [object[]]$Sites, [int]$MaxSites = 30 ) if (-not $script:PnPAvailable) { return } if (-not $Sites -or $Sites.Count -eq 0) { return } $SitesToProcess = $Sites | Select-Object -First $MaxSites $TotalSites = $Sites.Count $Capped = ($TotalSites -gt $MaxSites) Write-Host " - Collecting site permissions ($($SitesToProcess.Count) of $TotalSites sites)$(if ($Capped) { " [capped at $MaxSites]" })..." -ForegroundColor Cyan $RoleAssignments = [System.Collections.ArrayList]::new() # site-level perms $GroupMembers = [System.Collections.ArrayList]::new() # SP group membership $LibraryPerms = [System.Collections.ArrayList]::new() # library unique perms $SiteIndex = 0 foreach ($Site in $SitesToProcess) { $SiteIndex++ $SiteUrl = $Site.Url $SiteTitle = if ($Site.Title) { $Site.Title } else { $SiteUrl } Write-Host " [$SiteIndex/$($SitesToProcess.Count)] $SiteTitle" -ForegroundColor DarkGray try { # Connect to this specific site $SiteConn = Connect-PnPOnline -Url $SiteUrl -ClientId $script:Options.PnP.ClientId ` -Interactive -ReturnConnection -ErrorAction Stop #region Site-level role assignments try { $Web = Get-PnPWeb -Connection $SiteConn -ErrorAction Stop $Assignments = Get-PnPRoleAssignment -Connection $SiteConn -ErrorAction Stop foreach ($Assignment in $Assignments) { $Principal = $Assignment.Member $PrincipalName = $Principal.Title $PrincipalLogin = $Principal.LoginName $PrincipalType = $Principal.PrincipalType # User, SharePointGroup, SecurityGroup $PermLevels = ($Assignment.RoleDefinitionBindings | ForEach-Object { $_.Name }) -join ', ' $CleanLogin1 = $PrincipalLogin -replace 'i:0#\.f\|membership\|', '' $null = $RoleAssignments.Add([pscustomobject][ordered]@{ 'Site' = $SiteTitle 'Site URL' = $SiteUrl 'Principal Name' = $PrincipalName 'Login / Email' = $CleanLogin1 'Type' = "$PrincipalType" 'Permission Level' = $PermLevels 'Inherited' = ($Web.HasUniqueRoleAssignments -eq $false) }) } } catch { Write-AbrDebugLog "Site role assignments failed for $SiteUrl : $($_.Exception.Message)" 'WARN' 'PERMS' } #endregion #region SharePoint group membership try { $SPGroups = Get-PnPGroup -Connection $SiteConn -ErrorAction Stop foreach ($Group in $SPGroups) { try { $Members = Get-PnPGroupMember -Identity $Group.Title -Connection $SiteConn -ErrorAction Stop if ($Members) { foreach ($Member in $Members) { $CleanMemberLogin = if ($Member.Email) { $Member.Email } else { $Member.LoginName -replace 'i:0#\.f\|membership\|', '' } $null = $GroupMembers.Add([pscustomobject][ordered]@{ 'Site' = $SiteTitle 'Site URL' = $SiteUrl 'Group Name' = $Group.Title 'Member Name' = $Member.Title 'Member Email' = $CleanMemberLogin 'Member Type' = "$($Member.PrincipalType)" }) } } else { # Empty group -- still record it $null = $GroupMembers.Add([pscustomobject][ordered]@{ 'Site' = $SiteTitle 'Site URL' = $SiteUrl 'Group Name' = $Group.Title 'Member Name' = '(empty group)' 'Member Email' = '--' 'Member Type' = '--' }) } } catch { Write-AbrDebugLog "Group member fetch failed: $($Group.Title): $($_.Exception.Message)" 'WARN' 'PERMS' } } } catch { Write-AbrDebugLog "SP group fetch failed for $SiteUrl : $($_.Exception.Message)" 'WARN' 'PERMS' } #endregion #region Document library permissions (unique only) try { $Lists = Get-PnPList -Connection $SiteConn -ErrorAction Stop | Where-Object { $_.BaseType -eq 'DocumentLibrary' -and $_.Hidden -eq $false -and $_.Title -notin @('Style Library','Form Templates','Site Assets','Site Pages','_catalogs') } foreach ($List in $Lists) { try { $ListDetail = Get-PnPList -Identity $List.Id -Connection $SiteConn ` -Includes 'HasUniqueRoleAssignments','RoleAssignments.Member','RoleAssignments.RoleDefinitionBindings' ` -ErrorAction Stop if ($ListDetail.HasUniqueRoleAssignments) { foreach ($LA in $ListDetail.RoleAssignments) { $PrincipalName = $LA.Member.Title $PrincipalLogin = $LA.Member.LoginName -replace 'i:0#\.f\|membership\|', '' $PrincipalType = "$($LA.Member.PrincipalType)" $PermLevels = ($LA.RoleDefinitionBindings | ForEach-Object { $_.Name }) -join ', ' $null = $LibraryPerms.Add([pscustomobject][ordered]@{ 'Site' = $SiteTitle 'Site URL' = $SiteUrl 'Library' = $List.Title 'Principal Name' = $PrincipalName 'Login / Email' = $PrincipalLogin 'Type' = $PrincipalType 'Permission Level' = $PermLevels 'Unique Perms' = 'Yes' }) } } } catch { Write-AbrDebugLog "Library perm fetch failed: $($List.Title): $($_.Exception.Message)" 'WARN' 'PERMS' } } } catch { Write-AbrDebugLog "List fetch failed for $SiteUrl : $($_.Exception.Message)" 'WARN' 'PERMS' } #endregion # Disconnect from this site (reconnect to admin for next iteration) Disconnect-PnPOnline -Connection $SiteConn -ErrorAction SilentlyContinue } catch { Write-AbrDebugLog "Could not connect to $SiteUrl for permissions: $($_.Exception.Message)" 'WARN' 'PERMS' # Reconnect to admin URL for the next site try { Connect-PnPOnline -Url $script:TenantAdminUrl -ClientId $script:Options.PnP.ClientId ` -Interactive -ErrorAction SilentlyContinue | Out-Null } catch { } } } # Reconnect to admin URL after iterating all sites try { Connect-PnPOnline -Url $script:TenantAdminUrl -ClientId $script:Options.PnP.ClientId ` -Interactive -ErrorAction SilentlyContinue | Out-Null } catch { } # Add to Excel sheets if ($RoleAssignments.Count -gt 0) { $script:ExcelSheets['Site Role Assignments'] = $RoleAssignments Write-Host " - Site Role Assignments: $($RoleAssignments.Count) rows" -ForegroundColor DarkGray } if ($GroupMembers.Count -gt 0) { $script:ExcelSheets['SP Groups & Members'] = $GroupMembers Write-Host " - SP Groups & Members: $($GroupMembers.Count) rows" -ForegroundColor DarkGray } if ($LibraryPerms.Count -gt 0) { $script:ExcelSheets['Library Permissions'] = $LibraryPerms Write-Host " - Library Permissions: $($LibraryPerms.Count) rows" -ForegroundColor DarkGray } if ($Capped) { Write-Host " - NOTE: Permissions collected for first $MaxSites of $TotalSites sites." -ForegroundColor Yellow Write-Host " To increase coverage, raise InfoLevel.SiteCollections or reduce site count." -ForegroundColor Yellow } } |