Ensuring Secure OneDrive Sync with SharePoint Online Tenant Sync Client Restriction
Ensuring Secure OneDrive Sync with SharePoint Online Tenant Sync Client Restriction
Section titled “Ensuring Secure OneDrive Sync with SharePoint Online Tenant Sync Client Restriction”Overview
Section titled “Overview”This PowerShell script implements SharePoint Online tenant sync client restrictions to ensure secure OneDrive synchronization across your organization. The script helps prevent unauthorized data access and ensures compliance with organizational security policies.
Prerequisites
Section titled “Prerequisites”System Requirements
Section titled “System Requirements”- PowerShell: 5.1 or later (Windows PowerShell 7+ recommended)
- SharePoint Online Management Shell: Latest version
- Microsoft Graph PowerShell SDK: For modern API interactions
- Appropriate Permissions: SharePoint Administrator or Global Administrator
Required Modules
Section titled “Required Modules”# Install required modulesInstall-Module -Name Microsoft.Online.SharePoint.PowerShell -ForceInstall-Module -Name Microsoft.Graph -ForceInstall-Module -Name PnP.PowerShell -ForcePermissions Required
Section titled “Permissions Required”- SharePoint Administrator: For tenant-level configuration
- Global Administrator: For comprehensive access
- Application Permissions: For automated execution
Script Configuration
Section titled “Script Configuration”Parameters
Section titled “Parameters”param( [Parameter(Mandatory=$false)] [string]$TenantUrl = "https://yourtenant-admin.sharepoint.com",
[Parameter(Mandatory=$false)] [string]$LogFile = "C:\Logs\SharePointSyncRestriction.log",
[Parameter(Mandatory=$false)] [ValidateSet("Allow", "Block", "Unspecified")] [string]$DefaultSyncClientRestriction = "Allow",
[Parameter(Mandatory=$false)] [string[]]$AllowedDomains = @("contoso.com", "partner.com"),
[Parameter(Mandatory=$false)] [string[]]$BlockedDomains = @("personal-email.com", "suspicious-domain.com"),
[Parameter(Mandatory=$false)] [switch]$EnforceGrooveBlock = $false,
[Parameter(Mandatory=$false)] [switch]$GenerateReport = $false,
[Parameter(Mandatory=$false)] [switch]$TestMode = $false)Global Variables
Section titled “Global Variables”$ScriptVersion = "1.3.0"$CompanyName = "YourCompany"$SupportEmail = "support@yourcompany.com"$MaxRetryAttempts = 3$RetryDelaySeconds = 30Script Implementation
Section titled “Script Implementation”Main Function
Section titled “Main Function”function Set-SharePointSyncClientRestriction { param( [string]$TenantUrl, [string]$LogPath, [string]$DefaultRestriction, [string[]]$AllowedDomains, [string[]]$BlockedDomains, [bool]$EnforceGroove, [bool]$GenerateReport, [bool]$TestMode )
try { # Initialize logging Initialize-Logging -LogPath $LogPath
Write-Log "Starting SharePoint sync client restriction configuration - Version $ScriptVersion" Write-Log "Target Tenant: $TenantUrl" 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 current configuration $currentConfig = Get-CurrentSyncConfiguration Write-Log "Current sync client restriction: $($currentConfig.DefaultRestriction)"
# Configure default restriction if ($DefaultRestriction -ne $currentConfig.DefaultRestriction) { Write-Log "Updating default sync client restriction to: $DefaultRestriction" if (-not $TestMode) { Set-DefaultSyncRestriction -Restriction $DefaultRestriction } }
# Configure domain restrictions if ($AllowedDomains.Count -gt 0 -or $BlockedDomains.Count -gt 0) { Write-Log "Configuring domain restrictions" Set-DomainRestrictions -AllowedDomains $AllowedDomains -BlockedDomains $BlockedDomains -TestMode $TestMode }
# Configure Groove client blocking if requested if ($EnforceGroove) { Write-Log "Configuring Groove client blocking" Set-GrooveClientBlocking -TestMode $TestMode }
# Generate report if requested if ($GenerateReport) { Write-Log "Generating configuration report" $report = New-ConfigurationReport -CurrentConfig $currentConfig Export-Report -Report $report -Path "C:\Reports\SharePointSyncReport-$(Get-Date -Format 'yyyyMMdd-HHmmss').csv" }
# Validate configuration if (-not $TestMode) { $validationResult = Test-Configuration -ExpectedConfig @{ DefaultRestriction = $DefaultRestriction AllowedDomains = $AllowedDomains BlockedDomains = $BlockedDomains }
if ($validationResult.Success) { Write-Log "Configuration validation successful" } else { Write-Log "Configuration validation failed: $($validationResult.ErrorMessage)" -Level "ERROR" return $false } }
Write-Log "SharePoint sync client restriction configuration completed successfully" return $true
} catch { Write-Log "Unexpected error in Set-SharePointSyncClientRestriction: $($_.Exception.Message)" -Level "ERROR" return $false } finally { # Disconnect from SharePoint Online Disconnect-PnPOnline }}Connection Functions
Section titled “Connection Functions”SharePoint Online Connection
Section titled “SharePoint Online Connection”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 }}Configuration Functions
Section titled “Configuration Functions”Get Current Configuration
Section titled “Get Current Configuration”function Get-CurrentSyncConfiguration { try { Write-Log "Retrieving current sync client configuration"
# Get tenant sync client restriction $syncRestriction = Get-SPOTenantSyncClientRestriction
# Get domain restrictions $domainRestrictions = Get-SPOTenantSyncClientRestriction -ErrorAction SilentlyContinue
return [PSCustomObject]@{ DefaultRestriction = $syncRestriction AllowedDomains = @() BlockedDomains = @() GrooveBlocked = $false LastModified = Get-Date }
} catch { Write-Log "Failed to get current configuration: $($_.Exception.Message)" -Level "ERROR" return $null }}Set Default Sync Restriction
Section titled “Set Default Sync Restriction”function Set-DefaultSyncRestriction { param([string]$Restriction)
try { Write-Log "Setting default sync client restriction to: $Restriction"
$attempt = 1 while ($attempt -le $MaxRetryAttempts) { try { Set-SPOTenantSyncClientRestriction -Restriction $Restriction Write-Log "Successfully set default sync client restriction" return $true } catch { Write-Log "Attempt $attempt failed: $($_.Exception.Message)" -Level "WARNING" if ($attempt -lt $MaxRetryAttempts) { Start-Sleep -Seconds $RetryDelaySeconds $attempt++ } else { throw "Failed after $MaxRetryAttempts attempts" } } }
} catch { Write-Log "Failed to set default sync restriction: $($_.Exception.Message)" -Level "ERROR" return $false }}Domain Restrictions Configuration
Section titled “Domain Restrictions Configuration”function Set-DomainRestrictions { param( [string[]]$AllowedDomains, [string[]]$BlockedDomains, [bool]$TestMode )
try { Write-Log "Configuring domain restrictions" Write-Log "Allowed domains: $($AllowedDomains -join ', ')" Write-Log "Blocked domains: $($BlockedDomains -join ', ')"
if ($TestMode) { Write-Log "TEST MODE: Would configure domain restrictions" return $true }
# Configure allowed domains foreach ($domain in $AllowedDomains) { try { Write-Log "Adding allowed domain: $domain" Add-SPOTenantSyncClientRestriction -AllowedDomain $domain } catch { Write-Log "Failed to add allowed domain $domain`: $($_.Exception.Message)" -Level "WARNING" } }
# Configure blocked domains foreach ($domain in $BlockedDomains) { try { Write-Log "Adding blocked domain: $domain" Add-SPOTenantSyncClientRestriction -BlockedDomain $domain } catch { Write-Log "Failed to add blocked domain $domain`: $($_.Exception.Message)" -Level "WARNING" } }
Write-Log "Domain restrictions configuration completed" return $true
} catch { Write-Log "Failed to configure domain restrictions: $($_.Exception.Message)" -Level "ERROR" return $false }}Groove Client Blocking
Section titled “Groove Client Blocking”function Set-GrooveClientBlocking { param([bool]$TestMode)
try { Write-Log "Configuring Groove client blocking"
if ($TestMode) { Write-Log "TEST MODE: Would block Groove client" return $true }
# Block legacy Groove client Set-SPOTenantSyncClientRestriction -BlockMacSync $true Set-SPOTenantSyncClientRestriction -BlockUnmanagedSyncClient $true
Write-Log "Groove client blocking configured successfully" return $true
} catch { Write-Log "Failed to configure Groove client blocking: $($_.Exception.Message)" -Level "ERROR" return $false }}Validation Functions
Section titled “Validation Functions”Configuration Validation
Section titled “Configuration Validation”function Test-Configuration { param([hashtable]$ExpectedConfig)
try { Write-Log "Validating configuration"
$currentConfig = Get-CurrentSyncConfiguration $validationErrors = @()
# Validate default restriction if ($currentConfig.DefaultRestriction -ne $ExpectedConfig.DefaultRestriction) { $validationErrors += "Default restriction mismatch: Expected $($ExpectedConfig.DefaultRestriction), Found $($currentConfig.DefaultRestriction)" }
# Validate allowed domains $missingAllowed = $ExpectedConfig.AllowedDomains | Where-Object { $_ -notin $currentConfig.AllowedDomains } if ($missingAllowed.Count -gt 0) { $validationErrors += "Missing allowed domains: $($missingAllowed -join ', ')" }
# Validate blocked domains $missingBlocked = $ExpectedConfig.BlockedDomains | Where-Object { $_ -notin $currentConfig.BlockedDomains } if ($missingBlocked.Count -gt 0) { $validationErrors += "Missing blocked domains: $($missingBlocked -join ', ')" }
if ($validationErrors.Count -gt 0) { Write-Log "Configuration validation failed:" -Level "ERROR" foreach ($error in $validationErrors) { Write-Log " - $error" -Level "ERROR" }
return [PSCustomObject]@{ Success = $false ErrorMessage = $validationErrors -join "; " } }
return [PSCustomObject]@{ Success = $true ErrorMessage = "" }
} catch { Write-Log "Configuration validation failed: $($_.Exception.Message)" -Level "ERROR" return [PSCustomObject]@{ Success = $false ErrorMessage = $_.Exception.Message } }}Reporting Functions
Section titled “Reporting Functions”Generate Configuration Report
Section titled “Generate Configuration Report”function New-ConfigurationReport { param([PSCustomObject]$CurrentConfig)
try { Write-Log "Generating configuration report"
$reportData = @()
# Add configuration summary $reportData += [PSCustomObject]@{ Category = "Configuration" Item = "Default Sync Restriction" Value = $CurrentConfig.DefaultRestriction Status = "Configured" LastModified = $CurrentConfig.LastModified }
# Add domain restrictions foreach ($domain in $CurrentConfig.AllowedDomains) { $reportData += [PSCustomObject]@{ Category = "Domain Restriction" Item = "Allowed Domain" Value = $domain Status = "Allowed" LastModified = $CurrentConfig.LastModified } }
foreach ($domain in $CurrentConfig.BlockedDomains) { $reportData += [PSCustomObject]@{ Category = "Domain Restriction" Item = "Blocked Domain" Value = $domain Status = "Blocked" LastModified = $CurrentConfig.LastModified } }
# Add Groove client status $reportData += [PSCustomObject]@{ Category = "Client Restrictions" Item = "Groove Client" Value = if ($CurrentConfig.GrooveBlocked) { "Blocked" } else { "Allowed" } Status = if ($CurrentConfig.GrooveBlocked) { "Blocked" } else { "Allowed" } LastModified = $CurrentConfig.LastModified }
return $reportData
} catch { Write-Log "Failed to generate configuration report: $($_.Exception.Message)" -Level "ERROR" return @() }}Export Report
Section titled “Export Report”function Export-Report { param( [array]$Report, [string]$Path )
try { Write-Log "Exporting report to: $Path"
# Ensure directory exists $directory = Split-Path $Path -Parent if (-not (Test-Path $directory)) { New-Item -Path $directory -ItemType Directory -Force | Out-Null }
# Export to CSV $Report | Export-Csv -Path $Path -NoTypeInformation -Encoding UTF8
Write-Log "Report exported successfully"
# Also export to JSON for programmatic use $jsonPath = $Path.Replace(".csv", ".json") $Report | ConvertTo-Json -Depth 3 | Set-Content -Path $jsonPath -Encoding UTF8
} catch { Write-Log "Failed to export report: $($_.Exception.Message)" -Level "ERROR" }}Utility Functions
Section titled “Utility Functions”Logging Function
Section titled “Logging Function”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 } }}Initialize Logging
Section titled “Initialize Logging”function Initialize-Logging { param([string]$LogPath)
try { # Ensure log directory exists $logDirectory = Split-Path $LogPath -Parent if (-not (Test-Path $logDirectory)) { New-Item -Path $logDirectory -ItemType Directory -Force | Out-Null }
# Create log file if it doesn't exist if (-not (Test-Path $LogPath)) { Set-Content -Path $LogPath -Value "SharePoint Sync Client Restriction Log - Created $(Get-Date)" -Encoding UTF8 }
Write-Log "Logging initialized: $LogPath"
} catch { Write-Host "Failed to initialize logging: $($_.Exception.Message)" -ForegroundColor Red }}Deployment Instructions
Section titled “Deployment Instructions”Scheduled Task Configuration
Section titled “Scheduled Task Configuration”# Create scheduled task for regular monitoring$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File `"C:\Scripts\Set-SharePointSyncRestriction.ps1`" -GenerateReport"$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 2am$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -WakeToRunRegister-ScheduledTask -TaskName "SharePoint Sync Restriction Monitor" -Action $action -Trigger $trigger -Settings $settings -RunLevel HighestAzure Automation Integration
Section titled “Azure Automation Integration”# Azure Automation runbook configuration$AutomationAccountName = "YourAutomationAccount"$ResourceGroupName = "YourResourceGroup"
Import-AzAutomationRunbook -Path "Set-SharePointSyncRestriction.ps1" -AutomationAccountName $AutomationAccountName -ResourceGroupName $ResourceGroupName -Name "Set-SharePointSyncRestriction" -Type PowerShellPublish-AzAutomationRunbook -AutomationAccountName $AutomationAccountName -ResourceGroupName $ResourceGroupName -Name "Set-SharePointSyncRestriction"Security Considerations
Section titled “Security Considerations”Access Control
Section titled “Access Control”- 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 configuration changes
- Regular Review: Periodically review and update restrictions
Compliance Requirements
Section titled “Compliance Requirements”- Data Protection: Ensure compliance with GDPR, CCPA, and other regulations
- Industry Standards: Meet HIPAA, SOX, or industry-specific requirements
- Documentation: Maintain detailed configuration documentation
- Change Management: Implement formal change control procedures
Monitoring and Alerting
Section titled “Monitoring and Alerting”Health Check Script
Section titled “Health Check Script”function Test-SharePointSyncHealth { try { $config = Get-CurrentSyncConfiguration
# Check if restrictions are properly configured $healthStatus = [PSCustomObject]@{ TenantUrl = $TenantUrl DefaultRestriction = $config.DefaultRestriction LastCheck = Get-Date Status = "Healthy" Issues = @() }
# Add specific checks based on your requirements
return $healthStatus
} catch { return [PSCustomObject]@{ TenantUrl = $TenantUrl Status = "Error" LastCheck = Get-Date Issues = @($_.Exception.Message) } }}Alert Configuration
Section titled “Alert Configuration”# Configure email alerts for configuration changesfunction Send-ConfigurationAlert { param( [string]$Message, [PSCustomObject]$Details )
$emailBody = @"SharePoint Sync Client Restriction Alert========================================
$Message
Details:- Tenant: $($Details.TenantUrl)- Timestamp: $(Get-Date)- Configuration: $($Details | ConvertTo-Json -Depth 2)
Please review the configuration and take appropriate action."@
Send-MailMessage -To "admin@yourcompany.com" -From "alerts@yourcompany.com" -Subject "SharePoint Sync Restriction Alert" -Body $emailBody -SmtpServer "smtp.yourcompany.com"}Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”- Connection Failures: Check credentials and network connectivity
- Permission Errors: Verify account has appropriate SharePoint permissions
- Configuration Conflicts: Check for conflicting policies or settings
- Script Timeouts: Increase timeout values for large tenants
Debug Mode
Section titled “Debug Mode”# Enable detailed logging$VerbosePreference = "Continue"$DebugPreference = "Continue"
# Run in test mode firstSet-SharePointSyncClientRestriction -TestMode -Verbose -DebugRecovery Procedures
Section titled “Recovery Procedures”- Configuration Reset: Use SharePoint admin center to reset settings
- Backup Restoration: Restore from configuration backups
- Manual Configuration: Configure manually via admin center
Conclusion
Section titled “Conclusion”This SharePoint Online sync client restriction script provides comprehensive control over OneDrive synchronization across your organization. By implementing tenant-level restrictions, domain filtering, and client blocking, you can ensure data security while maintaining productivity.
The script’s modular design allows for customization based on specific organizational requirements while maintaining security best practices and compliance with regulatory requirements.
Regular monitoring and maintenance of sync restrictions ensure continued effectiveness and help identify potential security issues before they impact your organization’s data protection posture.