Skip to content

Auditing SharePoint Online Site Sharing Settings with PowerShell

Auditing SharePoint Online Site Sharing Settings with PowerShell

Section titled “Auditing SharePoint Online Site Sharing Settings with PowerShell”

This PowerShell script provides comprehensive auditing capabilities for SharePoint Online site sharing settings, helping organizations maintain security compliance and identify potential data exposure risks through improper sharing configurations.

  • PowerShell: 5.1 or later (PowerShell 7+ recommended)
  • SharePoint Online Management Shell: Latest version
  • Microsoft Graph PowerShell SDK: For enhanced API access
  • Appropriate Permissions: SharePoint Administrator or Global Administrator
Terminal window
# Install required modules
Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Force
Install-Module -Name Microsoft.Graph -Force
Install-Module -Name PnP.PowerShell -Force
  • SharePoint Administrator: For site collection access
  • Global Administrator: For comprehensive tenant access
  • Site Collection Administrator: For detailed site access
Terminal window
param(
[Parameter(Mandatory=$false)]
[string]$TenantUrl = "https://yourtenant-admin.sharepoint.com",
[Parameter(Mandatory=$false)]
[string]$OutputPath = "C:\Reports\SharePointAudit",
[Parameter(Mandatory=$false)]
[string]$LogFile = "C:\Logs\SharePointSharingAudit.log",
[Parameter(Mandatory=$false)]
[ValidateSet("All", "External", "Internal", "Anonymous")]
[string]$AuditScope = "All",
[Parameter(Mandatory=$false)]
[switch]$IncludeSiteGroups = $false,
[Parameter(Mandatory=$false)]
[switch]$IncludePermissions = $false,
[Parameter(Mandatory=$false)]
[switch]$GenerateSummary = $true,
[Parameter(Mandatory=$false)]
[switch]$ExportToExcel = $true,
[Parameter(Mandatory=$false)]
[switch]$TestMode = $false
)
Terminal window
$ScriptVersion = "2.2.0"
$CompanyName = "YourCompany"
$SupportEmail = "support@yourcompany.com"
$MaxRetryAttempts = 3
$RetryDelaySeconds = 30
$BatchSize = 50
Terminal window
function Invoke-SharePointSharingAudit {
param(
[string]$TenantUrl,
[string]$ReportPath,
[string]$LogPath,
[string]$Scope,
[bool]$IncludeGroups,
[bool]$IncludePermissions,
[bool]$GenerateSummary,
[bool]$ExportExcel,
[bool]$TestMode
)
try {
# Initialize logging and output directory
Initialize-AuditEnvironment -LogPath $LogPath -ReportPath $ReportPath
Write-Log "Starting SharePoint Online sharing audit - Version $ScriptVersion"
Write-Log "Target Tenant: $TenantUrl"
Write-Log "Audit Scope: $Scope"
Write-Log "Test Mode: $TestMode"
# Connect to SharePoint Online
if (-not (Connect-SharePointOnline -TenantUrl $TenantUrl)) {
Write-Log "Failed to connect to SharePoint Online" -Level "ERROR"
return $false
}
# Get all site collections
$siteCollections = Get-AllSiteCollections -TestMode $TestMode
Write-Log "Found $($siteCollections.Count) site collections to audit"
# Initialize audit results
$auditResults = @()
$summaryStats = Initialize-SummaryStats
# Process each site collection
foreach ($site in $siteCollections) {
Write-Log "Auditing site: $($site.Url)"
try {
$siteAudit = Invoke-SiteAudit -Site $site -Scope $Scope -IncludeGroups $IncludeGroups -IncludePermissions $IncludePermissions -TestMode $TestMode
if ($siteAudit) {
$auditResults += $siteAudit
Update-SummaryStats -Summary $summaryStats -SiteAudit $siteAudit
}
# Add delay to avoid throttling
Start-Sleep -Milliseconds 500
} catch {
Write-Log "Failed to audit site $($site.Url): $($_.Exception.Message)" -Level "ERROR"
$summaryStats.FailedAudits++
}
}
# Generate reports
if ($auditResults.Count -gt 0) {
Write-Log "Generating audit reports"
# Export detailed results
Export-DetailedReport -Results $auditResults -Path $ReportPath -Format "CSV"
# Export to Excel if requested
if ($ExportExcel) {
Export-ExcelReport -Results $auditResults -Path $ReportPath
}
# Generate summary report
if ($GenerateSummary) {
$summaryReport = New-SummaryReport -Results $auditResults -Summary $summaryStats
Export-SummaryReport -Report $summaryReport -Path $ReportPath
}
}
# Generate security alerts for high-risk findings
$securityAlerts = Get-SecurityAlerts -Results $auditResults
if ($securityAlerts.Count -gt 0) {
Write-Log "Found $($securityAlerts.Count) security alerts"
Export-SecurityAlerts -Alerts $securityAlerts -Path $ReportPath
Send-SecurityAlerts -Alerts $securityAlerts
}
Write-Log "SharePoint sharing audit completed successfully"
Write-Log "Total sites audited: $($auditResults.Count)"
Write-Log "High-risk sites found: $($summaryStats.HighRiskSites)"
return $true
} catch {
Write-Log "Unexpected error in Invoke-SharePointSharingAudit: $($_.Exception.Message)" -Level "ERROR"
return $false
} finally {
# Disconnect from SharePoint Online
Disconnect-PnPOnline
}
}
Terminal window
function Connect-SharePointOnline {
param([string]$TenantUrl)
try {
Write-Log "Connecting to SharePoint Online: $TenantUrl"
# Try PnP connection first (recommended)
try {
Connect-PnPOnline -Url $TenantUrl -Interactive
Write-Log "Connected using PnP PowerShell"
return $true
} catch {
Write-Log "PnP connection failed, trying traditional method" -Level "WARNING"
}
# Fallback to traditional SharePoint Online connection
$credential = Get-Credential -Message "Enter SharePoint Online administrator credentials"
Connect-SPOService -Url $TenantUrl -Credential $credential
Write-Log "Connected using SharePoint Online Management Shell"
return $true
} catch {
Write-Log "Failed to connect to SharePoint Online: $($_.Exception.Message)" -Level "ERROR"
return $false
}
}
Terminal window
function Get-AllSiteCollections {
param([bool]$TestMode)
try {
Write-Log "Retrieving all site collections"
if ($TestMode) {
# Return test data
return @(
[PSCustomObject]@{ Url = "https://yourtenant.sharepoint.com/sites/test1"; Title = "Test Site 1"; Template = "STS#0" }
[PSCustomObject]@{ Url = "https://yourtenant.sharepoint.com/sites/test2"; Title = "Test Site 2"; Template = "SITEPAGEPUBLISHING#0" }
)
}
# Get all site collections
$sites = Get-SPOSite -Limit All -IncludePersonalSite $false
Write-Log "Retrieved $($sites.Count) site collections"
return $sites
} catch {
Write-Log "Failed to retrieve site collections: $($_.Exception.Message)" -Level "ERROR"
return @()
}
}
Terminal window
function Invoke-SiteAudit {
param(
[PSCustomObject]$Site,
[string]$Scope,
[bool]$IncludeGroups,
[bool]$IncludePermissions,
[bool]$TestMode
)
try {
Write-Log "Auditing site: $($Site.Url)"
if ($TestMode) {
return Get-TestSiteAudit -Site $Site
}
# Connect to specific site
Connect-PnPOnline -Url $Site.Url -Interactive
# Get site sharing settings
$sharingSettings = Get-SiteSharingSettings -SiteUrl $Site.Url
# Get external sharing information
$externalSharing = Get-ExternalSharingInfo -SiteUrl $Site.Url
# Get site groups if requested
$siteGroups = @()
if ($IncludeGroups) {
$siteGroups = Get-SiteGroups -SiteUrl $Site.Url
}
# Get permissions if requested
$permissions = @()
if ($IncludePermissions) {
$permissions = Get-SitePermissions -SiteUrl $Site.Url
}
# Calculate risk score
$riskScore = Calculate-RiskScore -SharingSettings $sharingSettings -ExternalSharing $externalSharing -SiteGroups $siteGroups
# Create audit result
$auditResult = [PSCustomObject]@{
SiteUrl = $Site.Url
SiteTitle = $Site.Title
SiteTemplate = $Site.Template
AuditDate = Get-Date
SharingCapability = $sharingSettings.SharingCapability
SiteDefaultLinkPermission = $sharingSettings.SiteDefaultLinkPermission
SiteDefaultSharingLinkType = $sharingSettings.SiteDefaultSharingLinkType
ExternalUserExpirationInDays = $sharingSettings.ExternalUserExpirationInDays
RequireAnonymousLinksExpireInDays = $sharingSettings.RequireAnonymousLinksExpireInDays
ExternalSharingEnabled = $externalSharing.Enabled
AnonymousAccessEnabled = $externalSharing.AnonymousAccess
GuestUsersCount = $externalSharing.GuestUsersCount
ExternalLinksCount = $externalSharing.ExternalLinksCount
SiteGroupsCount = $siteGroups.Count
PermissionsCount = $permissions.Count
RiskScore = $riskScore.Score
RiskLevel = $riskScore.Level
SecurityRecommendations = $riskScore.Recommendations
SiteGroups = $siteGroups
Permissions = $permissions
}
return $auditResult
} catch {
Write-Log "Failed to audit site $($Site.Url): $($_.Exception.Message)" -Level "ERROR"
return $null
} finally {
# Disconnect from site
Disconnect-PnPOnline
}
}
Terminal window
function Get-SiteSharingSettings {
param([string]$SiteUrl)
try {
$sharingSettings = Get-PnPTenantSite -Url $SiteUrl -Includes SharingCapability, SiteDefaultLinkPermission, SiteDefaultSharingLinkType, ExternalUserExpirationInDays, RequireAnonymousLinksExpireInDays
return [PSCustomObject]@{
SharingCapability = $sharingSettings.SharingCapability
SiteDefaultLinkPermission = $sharingSettings.SiteDefaultLinkPermission
SiteDefaultSharingLinkType = $sharingSettings.SiteDefaultSharingLinkType
ExternalUserExpirationInDays = $sharingSettings.ExternalUserExpirationInDays
RequireAnonymousLinksExpireInDays = $sharingSettings.RequireAnonymousLinksExpireInDays
}
} catch {
Write-Log "Failed to get sharing settings for $SiteUrl`: $($_.Exception.Message)" -Level "WARNING"
return $null
}
}
Terminal window
function Get-ExternalSharingInfo {
param([string]$SiteUrl)
try {
# Get external users
$externalUsers = Get-PnPExternalUser -Site $SiteUrl -ErrorAction SilentlyContinue
$guestUsersCount = if ($externalUsers) { $externalUsers.Count } else { 0 }
# Get external sharing links
$externalLinks = Get-PnPSharingLink -Site $SiteUrl -ErrorAction SilentlyContinue
$externalLinksCount = if ($externalLinks) { $externalLinks.Count } else { 0 }
# Check for anonymous access
$anonymousAccess = Test-AnonymousAccess -SiteUrl $SiteUrl
return [PSCustomObject]@{
Enabled = $guestUsersCount -gt 0 -or $externalLinksCount -gt 0
GuestUsersCount = $guestUsersCount
ExternalLinksCount = $externalLinksCount
AnonymousAccess = $anonymousAccess
}
} catch {
Write-Log "Failed to get external sharing info for $SiteUrl`: $($_.Exception.Message)" -Level "WARNING"
return [PSCustomObject]@{
Enabled = $false
GuestUsersCount = 0
ExternalLinksCount = 0
AnonymousAccess = $false
}
}
}
Terminal window
function Get-SiteGroups {
param([string]$SiteUrl)
try {
$groups = Get-PnPGroup -Site $SiteUrl
$groupInfo = @()
foreach ($group in $groups) {
$members = Get-PnPGroupMember -Identity $group -Site $SiteUrl
$groupInfo += [PSCustomObject]@{
GroupName = $group.Title
GroupType = if ($group.OwnerTitle) { "Custom" } else { "Default" }
MemberCount = $members.Count
AllowMembersEditMembership = $group.AllowMembersEditMembership
RequestToJoinLeaveEmailSetting = $group.RequestToJoinLeaveEmailSetting
AutoAcceptRequestToJoinLeave = $group.AutoAcceptRequestToJoinLeave
OnlyAllowMembersViewMembership = $group.OnlyAllowMembersViewMembership
}
}
return $groupInfo
} catch {
Write-Log "Failed to get site groups for $SiteUrl`: $($_.Exception.Message)" -Level "WARNING"
return @()
}
}
Terminal window
function Get-SitePermissions {
param([string]$SiteUrl)
try {
$permissions = Get-PnPList -Site $SiteUrl | Where-Object { $_.Hidden -eq $false } | ForEach-Object {
$listPermissions = Get-PnPListPermission -Identity $_ -Site $SiteUrl
foreach ($permission in $listPermissions) {
[PSCustomObject]@{
ListName = $_.Title
PrincipalName = $permission.Principal.Title
PrincipalType = $permission.Principal.PrincipalType
RoleBindings = $permission.RoleBindings -join ", "
PermissionLevels = $permission.PermissionLevels -join ", "
}
}
}
return $permissions
} catch {
Write-Log "Failed to get site permissions for $SiteUrl`: $($_.Exception.Message)" -Level "WARNING"
return @()
}
}
Terminal window
function Calculate-RiskScore {
param(
[PSCustomObject]$SharingSettings,
[PSCustomObject]$ExternalSharing,
[array]$SiteGroups
)
$score = 0
$recommendations = @()
# Base scoring for external sharing
if ($ExternalSharing.Enabled) { $score += 20 }
if ($ExternalSharing.AnonymousAccess) { $score += 40 }
if ($ExternalSharing.GuestUsersCount -gt 10) { $score += 15 }
if ($ExternalSharing.ExternalLinksCount -gt 5) { $score += 10 }
# Scoring for sharing settings
if ($SharingSettings.SharingCapability -eq "ExternalUserAndGuestSharing") { $score += 15 }
if ($SharingSettings.SiteDefaultLinkPermission -eq "View") { $score += 5 }
if ($SharingSettings.SiteDefaultSharingLinkType -eq "Anonymous") { $score += 25 }
if (-not $SharingSettings.ExternalUserExpirationInDays) { $score += 10 }
if (-not $SharingSettings.RequireAnonymousLinksExpireInDays) { $score += 15 }
# Scoring for site groups
if ($SiteGroups.Count -gt 20) { $score += 10 }
$largeGroups = $SiteGroups | Where-Object { $_.MemberCount -gt 50 }
if ($largeGroups.Count -gt 0) { $score += 15 }
# Generate recommendations
if ($ExternalSharing.AnonymousAccess) {
$recommendations += "Disable anonymous access or implement strict access controls"
}
if (-not $SharingSettings.ExternalUserExpirationInDays) {
$recommendations += "Set external user expiration policy"
}
if ($ExternalSharing.GuestUsersCount -gt 10) {
$recommendations += "Review and reduce external user access"
}
# Determine risk level
$riskLevel = switch ($score) {
{ $_ -ge 80 } { "Critical" }
{ $_ -ge 60 } { "High" }
{ $_ -ge 40 } { "Medium" }
{ $_ -ge 20 } { "Low" }
default { "Minimal" }
}
return [PSCustomObject]@{
Score = $score
Level = $riskLevel
Recommendations = $recommendations
}
}
Terminal window
function Export-DetailedReport {
param(
[array]$Results,
[string]$Path,
[string]$Format
)
try {
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$fileName = "SharePointSharingAudit-Detailed-$timestamp.$Format"
$fullPath = Join-Path $Path $fileName
Write-Log "Exporting detailed report to: $fullPath"
# Ensure directory exists
if (-not (Test-Path $Path)) {
New-Item -Path $Path -ItemType Directory -Force | Out-Null
}
# Export based on format
switch ($Format) {
"CSV" {
$Results | Export-Csv -Path $fullPath -NoTypeInformation -Encoding UTF8
}
"JSON" {
$Results | ConvertTo-Json -Depth 10 | Set-Content -Path $fullPath -Encoding UTF8
}
}
Write-Log "Detailed report exported successfully"
} catch {
Write-Log "Failed to export detailed report: $($_.Exception.Message)" -Level "ERROR"
}
}
Terminal window
function Export-ExcelReport {
param(
[array]$Results,
[string]$Path
)
try {
if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
Write-Log "ImportExcel module not available, skipping Excel export" -Level "WARNING"
return
}
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$fileName = "SharePointSharingAudit-Excel-$timestamp.xlsx"
$fullPath = Join-Path $Path $fileName
Write-Log "Exporting Excel report to: $fullPath"
# Create Excel workbook with multiple worksheets
$excelParams = @{
Path = $fullPath
AutoSize = $true
AutoFilter = $true
FreezeTopRow = $true
BoldTopRow = $true
}
# Summary worksheet
$summaryData = $Results | Group-Object RiskLevel | ForEach-Object {
[PSCustomObject]@{
RiskLevel = $_.Name
SiteCount = $_.Count
Percentage = [math]::Round(($_.Count / $Results.Count) * 100, 2)
}
}
$summaryData | Export-Excel @excelParams -WorksheetName "Summary"
# Detailed results worksheet
$Results | Select-Object -ExcludeProperty SiteGroups, Permissions | Export-Excel @excelParams -WorksheetName "Detailed Results"
# High-risk sites worksheet
$highRiskSites = $Results | Where-Object { $_.RiskLevel -in @("Critical", "High") }
if ($highRiskSites.Count -gt 0) {
$highRiskSites | Export-Excel @excelParams -WorksheetName "High Risk Sites"
}
Write-Log "Excel report exported successfully"
} catch {
Write-Log "Failed to export Excel report: $($_.Exception.Message)" -Level "ERROR"
}
}
Terminal window
function Get-SecurityAlerts {
param([array]$Results)
$alerts = @()
# Critical risk sites
$criticalSites = $Results | Where-Object { $_.RiskLevel -eq "Critical" }
foreach ($site in $criticalSites) {
$alerts += [PSCustomObject]@{
AlertType = "Critical Risk Site"
SiteUrl = $site.SiteUrl
SiteTitle = $site.SiteTitle
RiskScore = $site.RiskScore
Description = "Site has critical security configuration issues"
Recommendations = $site.SecurityRecommendations -join "; "
Priority = "High"
Timestamp = Get-Date
}
}
# Anonymous access sites
$anonymousSites = $Results | Where-Object { $_.AnonymousAccessEnabled -eq $true }
foreach ($site in $anonymousSites) {
$alerts += [PSCustomObject]@{
AlertType = "Anonymous Access"
SiteUrl = $site.SiteUrl
SiteTitle = $site.SiteTitle
RiskScore = $site.RiskScore
Description = "Site allows anonymous access"
Recommendations = "Review anonymous access settings and implement proper access controls"
Priority = "High"
Timestamp = Get-Date
}
}
# High external user count sites
$highExternalUserSites = $Results | Where-Object { $_.GuestUsersCount -gt 20 }
foreach ($site in $highExternalUserSites) {
$alerts += [PSCustomObject]@{
AlertType = "High External User Count"
SiteUrl = $site.SiteUrl
SiteTitle = $site.SiteTitle
RiskScore = $site.RiskScore
Description = "Site has $($site.GuestUsersCount) external users"
Recommendations = "Review external user access and remove unnecessary accounts"
Priority = "Medium"
Timestamp = Get-Date
}
}
return $alerts
}
Terminal window
function Initialize-AuditEnvironment {
param([string]$LogPath, [string]$ReportPath)
try {
# Ensure log directory exists
$logDirectory = Split-Path $LogPath -Parent
if (-not (Test-Path $logDirectory)) {
New-Item -Path $logDirectory -ItemType Directory -Force | Out-Null
}
# Ensure report directory exists
if (-not (Test-Path $ReportPath)) {
New-Item -Path $ReportPath -ItemType Directory -Force | Out-Null
}
# Initialize log file
if (-not (Test-Path $LogPath)) {
Set-Content -Path $LogPath -Value "SharePoint Sharing Audit Log - Created $(Get-Date)" -Encoding UTF8
}
Write-Log "Audit environment initialized"
} catch {
Write-Host "Failed to initialize audit environment: $($_.Exception.Message)" -ForegroundColor Red
}
}
Terminal window
function Initialize-SummaryStats {
return [PSCustomObject]@{
TotalSites = 0
AuditedSites = 0
FailedAudits = 0
HighRiskSites = 0
MediumRiskSites = 0
LowRiskSites = 0
SitesWithExternalSharing = 0
SitesWithAnonymousAccess = 0
TotalExternalUsers = 0
TotalExternalLinks = 0
AuditStartTime = Get-Date
AuditEndTime = $null
}
}
Terminal window
function Update-SummaryStats {
param([PSCustomObject]$Summary, [PSCustomObject]$SiteAudit)
$Summary.AuditedSites++
$Summary.TotalSites++
switch ($SiteAudit.RiskLevel) {
"Critical" { $Summary.HighRiskSites++ }
"High" { $Summary.HighRiskSites++ }
"Medium" { $Summary.MediumRiskSites++ }
"Low" { $Summary.LowRiskSites++ }
"Minimal" { $Summary.LowRiskSites++ }
}
if ($SiteAudit.ExternalSharingEnabled) { $Summary.SitesWithExternalSharing++ }
if ($SiteAudit.AnonymousAccessEnabled) { $Summary.SitesWithAnonymousAccess++ }
$Summary.TotalExternalUsers += $SiteAudit.GuestUsersCount
$Summary.TotalExternalLinks += $SiteAudit.ExternalLinksCount
}
Terminal window
function Write-Log {
param(
[string]$Message,
[ValidateSet("INFO", "WARNING", "ERROR", "DEBUG")]
[string]$Level = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$hostname = $env:COMPUTERNAME
$logEntry = "[$timestamp] [$hostname] [$Level] $Message"
# Write to log file
Add-Content -Path $LogFile -Value $logEntry -Encoding UTF8
# Write to console
switch ($Level) {
"ERROR" { Write-Host $logEntry -ForegroundColor Red }
"WARNING" { Write-Host $logEntry -ForegroundColor Yellow }
"DEBUG" { Write-Host $logEntry -ForegroundColor Gray }
default { Write-Host $logEntry -ForegroundColor Green }
}
}
Terminal window
# Create scheduled task for regular audits
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File `"C:\Scripts\Invoke-SharePointSharingAudit.ps1`" -GenerateSummary -ExportToExcel"
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 1am
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -WakeToRun
Register-ScheduledTask -TaskName "SharePoint Sharing Audit" -Action $action -Trigger $trigger -Settings $settings -RunLevel Highest
Terminal window
# Azure Automation runbook configuration
$AutomationAccountName = "YourAutomationAccount"
$ResourceGroupName = "YourResourceGroup"
Import-AzAutomationRunbook -Path "Invoke-SharePointSharingAudit.ps1" -AutomationAccountName $AutomationAccountName -ResourceGroupName $ResourceGroupName -Name "SharePointSharingAudit" -Type PowerShell
Publish-AzAutomationRunbook -AutomationAccountName $AutomationAccountName -ResourceGroupName $ResourceGroupName -Name "SharePointSharingAudit"
  • Least Privilege: Use service accounts with minimum required permissions
  • Secure Storage: Store credentials in Azure Key Vault or Windows Credential Manager
  • Audit Logging: Maintain comprehensive audit trail of all audit activities
  • Regular Reviews: Periodically review audit scope and permissions
  • Sensitive Data: Handle audit results as sensitive information
  • Secure Storage: Store reports in secure, access-controlled locations
  • Retention Policies: Implement appropriate data retention for audit logs
  • Compliance Requirements: Ensure compliance with relevant regulations
Terminal window
# Configure email alerts for critical findings
function Send-SecurityAlerts {
param([array]$Alerts)
$criticalAlerts = $Alerts | Where-Object { $_.Priority -eq "High" }
if ($criticalAlerts.Count -gt 0) {
$emailBody = @"
SharePoint Security Alert
========================
Critical security issues detected in SharePoint Online:
$($criticalAlerts | ForEach-Object { "Site: $($_.SiteUrl)`nIssue: $($_.Description)`nRecommendations: $($_.Recommendations)`n---" })
Please review these sites and take appropriate action immediately.
"@
Send-MailMessage -To "security-team@yourcompany.com" -From "alerts@yourcompany.com" -Subject "Critical SharePoint Security Alerts" -Body $emailBody -SmtpServer "smtp.yourcompany.com"
}
}
  1. Connection Failures: Check credentials and network connectivity
  2. Permission Errors: Verify account has appropriate SharePoint permissions
  3. Throttling: Implement delays and batch processing for large tenants
  4. Memory Issues: Process sites in batches for large environments
Terminal window
# Enable detailed logging
$VerbosePreference = "Continue"
$DebugPreference = "Continue"
# Run in test mode first
Invoke-SharePointSharingAudit -TestMode -Verbose -Debug
Terminal window
# Process sites in parallel for faster execution
$siteCollections | ForEach-Object -ThrottleLimit 5 -Parallel {
# Audit logic here
}

This SharePoint Online sharing audit script provides comprehensive visibility into your organization’s sharing configurations and helps identify potential security risks. By regularly auditing site sharing settings, you can maintain compliance with security policies and prevent data exposure through improper sharing configurations.

The script’s modular design allows for customization based on specific organizational requirements while maintaining security best practices and providing actionable insights for security teams.

Regular execution of this audit helps ensure ongoing security compliance and provides valuable data for security decision-making and policy optimization.