Use PnP PowerShell to Remove owners in bulk using a CSV file.
# Connect PnP with the same email as $SiteCollAdmin
# Define the log file path
$logFilePath = "Remove-logfile.log"
# Function to log messages
function Log-Message {
param (
[string]$Message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm"
$logMessage = "$timestamp - $Message"
$logMessage | Add-Content -Path $logFilePath
}
# Define the function to process and remove owners from the site
function Remove-SiteOwners {
param (
[string]$SiteUrl,
[string]$Owners
)
try {
# Connect to the SharePoint Online site
Connect-PnPOnline -Url $SiteUrl -Interactive -ClientId xxxxxxx-xxxxxxxx....
Log-Message "Connected to site: $SiteUrl"
# Get site details and check if it's a Teams site (Microsoft 365 Group)
$site = Get-PnPSite -Includes GroupId
if ($site.GroupId -ne [Guid]::Empty) {
# If GroupId is not empty, it's a Teams site
Log-Message "Site $SiteUrl is a Teams site (Microsoft 365 Group)"
# Split the owners by semicolon
$ownerList = $Owners -split ";"
# Loop through each owner and remove them from the Microsoft 365 Group
foreach ($userEmail in $ownerList) {
$userEmail = $userEmail.Trim() # Ensure no extra spaces around the emails
try {
Remove-PnPMicrosoft365GroupOwner -Identity $site.GroupId -User $userEmail -ErrorAction Stop
Log-Message "Removed $userEmail from Microsoft 365 Group owners for site $SiteUrl"
}
catch {
Log-Message "Failed to remove $userEmail from Microsoft 365 Group owners for site $SiteUrl. Error: $_"
Write-Warning "Failed to remove $userEmail from Microsoft 365 Group owners for site $SiteUrl"
}
}
} else {
# If it's a regular SharePoint site
Log-Message "Site $SiteUrl is a regular SharePoint site"
# Get the associated owner group
$ownersGroup = Get-PnPGroup -AssociatedOwnerGroup
# Split the owners by semicolon
$ownerList = $Owners -split ";"
# Loop through each owner and remove them from the SharePoint owner group
foreach ($userEmail in $ownerList) {
$userEmail = $userEmail.Trim() # Ensure no extra spaces around the emails
try {
Remove-PnPGroupMember -LoginName $userEmail -Group $ownersGroup.Id -ErrorAction Stop
Log-Message "Removed $userEmail from SharePoint group $($ownersGroup.Title) on site $SiteUrl"
}
catch {
Log-Message "Failed to remove $userEmail from SharePoint group $($ownersGroup.Title) on site $SiteUrl. Error: $_"
Write-Warning "Failed to remove $userEmail from SharePoint group $($ownersGroup.Title) on site $SiteUrl"
}
}
}
}
catch {
Log-Message "Failed to remove owners from site: $SiteUrl. Error: $_"
Write-Warning "Failed to remove owners from site: $SiteUrl"
}
}
# Path to your CSV file
$csvPath = "Sites.csv"
# Import the CSV file and store it as an array of sites
$sites = Import-Csv -Path $csvPath
# Total number of sites
$totalSites = $sites.Count
# Initialize a counter for processed sites
$processedCount = 0
# Loop through each site URL in the CSV file and process the owners
for ($i = 0; $i -lt $totalSites; $i++) {
$site = $sites[$i]
$SiteUrl = $site.URL
$Owners = $site.Owners
# Print progress update
$processedCount = $i + 1
$percentComplete = [math]::Round(($processedCount / $totalSites) * 100, 2)
Write-Host "Processing site $processedCount of $totalSites ($percentComplete% complete): $SiteUrl"
Log-Message "Processing site $processedCount of $totalSites ($percentComplete% complete): $SiteUrl"
# Define the site collection admin (for example purposes, replace as needed)
$SiteCollAdmin = "owner email"
try {
# Set the site collection admin
Set-PnPTenantSite -Url $SiteUrl -Owners $SiteCollAdmin -ErrorAction Stop
Log-Message "Set site collection admin for site: $SiteUrl"
# Call the Remove-SiteOwners function to remove owners
Remove-SiteOwners -SiteUrl $SiteUrl -Owners $Owners
# Remove the site collection admin
Remove-PnPSiteCollectionAdmin -Owners $SiteCollAdmin
Log-Message "Removed site collection admin for site: $SiteUrl"
}
catch {
Log-Message "Failed to set or remove admin for site: $SiteUrl. Error: $_"
Write-Warning "Failed to set or remove admin for site: $SiteUrl"
}
# Optionally add a short delay between iterations (to avoid throttling)
Start-Sleep -Seconds 1
}
# Final message after all sites are processed
Write-Host "Completed processing $processedCount out of $totalSites sites."
Log-Message "Completed processing $processedCount out of $totalSites sites."