The major trick is being able to grant your "SharePoint Administrator" account as site collection administrator temporarily for that operation without changing the default Site Owner.
The PnP command to help you in that case is "Set-PnPTenantSite" and some CSOM usages in an old school way to avoid PnP context mixed-up.
For you information, "Set-PnPUser" works only if your account is already granted as site collection administrator.
The PS script below would help you.
Import-Module SharePointPnPPowerShellOnline
$tenantName = "<YOUR_TENANT_NAME>"
$localeId = 2057 # UK
$timeZoneId = 2 # London
#Connect to SPO Tenant
$cnx = Connect-PnPOnline "https://$tenantName-admin.sharepoint.com" -UseWebLogin -ReturnConnection
#Get Tenant CSOM instance
$tenant = new-object Microsoft.Online.SharePoint.TenantAdministration.Tenant([Microsoft.SharePoint.Client.ClientRuntimeContext]$cnx.Context)
function Remove-PnPTenantSiteCollectionAdministrator{
param([string]$siteUrl, [string[]]$users)
$ctx = $tenant.Context;
$targetSite = $tenant.GetSiteByUrl($siteUrl);
$ctx.Load($targetSite);
$ctx.ExecuteQuery();
$users |%{
$user = $targetSite.RootWeb.EnsureUser($_);
$ctx.Load($user)
$ctx.ExecuteQuery();
if($user.IsSiteAdmin){
$user.IsSiteAdmin = $false;
$user.Update();
}
}
$ctx.ExecuteQuery();
}
function Get-PnPTenantSiteAuthor{
param([string]$siteUrl)
$ctx = $tenant.Context;
$targetSite = $tenant.GetSiteByUrl($siteUrl);
$author = $targetSite.RootWeb.Author
$ctx.Load($author);
$ctx.ExecuteQuery()
[pscustomobject]@{
LoginName = $author.LoginName
DisplayName = $author.Title
UserPrincipalName = $author.UserPrincipalName
Email = $author.Email
}
}
#Get current UserName
$tenantWeb = Get-PnPWeb
$tenantWeb.Context.Load($tenantWeb.CurrentUser)
$tenantWeb.Context.ExecuteQuery()
$currentUser = $tenantWeb.CurrentUser
#Get All sites then execute
Get-PnPTenantSite | %{
$site = $_;
#Grant current user as admin if it's not already it's own
if($site.OwnerEmail -ne $currentUser.Email){
$site.Url | Set-PnPTenantSite -Owners $currentUser.Email |out-null
}
$user = Get-PnPTenantSiteAuthor $site.Url
try{
$ctx = $tenant.Context;
$targetSite = $tenant.GetSiteByUrl($site.Url);
$web = $targetSite.RootWeb;
$timeZones = $web.RegionalSettings.TimeZones
$ctx.Load($timeZones)
$ctx.ExecuteQuery()
$timeZone = $timeZones | ?{ $_.Id -eq $timeZoneId }
$web.RegionalSettings.LocaleId = $localeId
$web.RegionalSettings.TimeZone = $timeZone
$web.Update()
$ctx.ExecuteQuery()
}
finally{
#Remove Current User as admin except for it's own
if($site.OwnerEmail -ne $currentUser.Email){
Remove-PnPTenantSiteCollectionAdministrator $site.Url $currentUser.LoginName |out-null
}
#ensure owner is the real owner
if([string]::IsNullOrEmpty($site.Owner)){
$site.Url | Set-PnPTenantSite -Owners $res.UserPrincipalName |out-null
}
}
}