如何使用 PowerShell 复制 SharePoint Online 中的用户权限?
SharePoint Online 中的权限管理始终是一项复杂的任务,尤其是在大型环境中。当您尝试克隆现有用户的访问权限时,在 SharePoint 中授予权限会变得很麻烦。考虑以下场景:您的部门中的现有用户被授予对各种 SharePoint 网站、列表、文件夹、文件等的访问权限,当新用户加入该部门时,您(SharePoint 管理员)需要向所有部门添加新用户与现有团队成员具有相同访问权限的地方!
那么,如何在SharePoint Online中克隆用户权限?没有任何方法可以立即将权限从一个用户复制到另一用户!您必须遍历以下权限层次结构,检查源用户是否具有权限,然后手动添加新用户。听起来很简单?坚持,稍等!当您拥有包含大量数据的较大站点时,这几乎是不可能的!您无法扫描文档库中的数千个文件,不是吗?
创建 AD 组、SharePoint 组或 Microsoft 365 组来管理权限将是一种选择!因此,您只需将新用户添加到相应的组即可。使用 ShareGate、ControlPoint、Boost Solutions 权限管理器等第三方工具将是另一种涉及成本的选择。嗯,还有另一个免费的选择。电源外壳!
SharePoint Online 权限层次结构
SharePoint Online 中的任何用户都可以通过以下方式进行访问:
- 添加为网站集管理员(或主要所有者)
- 在网站级别授予的权限,作为 SharePoint 组的一部分或使用直接权限
- 通过打破继承授予列表或库的权限
- 访问权限可以通过列表项(文件)或文件夹级别权限来实现。
因此,如果要复制现有用户的权限,则必须查看上述所有对象,然后向新用户授予权限(如果要复制的现有用户具有权限)。
用于在 SharePoint Online 中克隆用户权限的 PowerShell 脚本
让我们使用PowerShell来克隆用户的权限。该脚本循环访问权限层次结构的每个级别,例如网站集、子网站、列表和库、文件夹、列表项。如果源用户拥有权限,它将给定源用户的权限复制到目标用户。相应地设置变量 $SourceUser、$TargetUser 和 $SiteURL 的参数并运行脚本。您会发现脚本在屏幕上输出日志文本,无论它复制权限在哪里。
#Function to copy user permissions
Function Copy-PnPUserPermission($SourceUser, $TargetUser, [Microsoft.SharePoint.Client.SecurableObject]$Object)
{
#Determine the type of the object
Switch($Object.TypedObject.ToString())
{
"Microsoft.SharePoint.Client.Web"
{
$ObjectType = "Site" ; $ObjectURL = $Object.URL; $ObjectTitle = $Object.Title
}
"Microsoft.SharePoint.Client.ListItem"
{
If($Object.FileSystemObjectType -eq "Folder")
{
$ObjectType = "Folder" ; $ObjectURL = $Object.FieldValues.FileRef; $ObjectTitle = $Object.FieldValues.FileLeafRef
}
ElseIf($Object.FileSystemObjectType -eq "File")
{
$ObjectType = "File" ; $ObjectURL = $Object.FieldValues.FileRef; $ObjectTitle = $Object.FieldValues.FileLeafRef
}
Else
{
$ObjectType = "List Item"; $ObjectURL = $Object.FieldValues.FileRef ;$ObjectTitle = $Object.FieldValues.Title
}
}
Default
{
$ObjectType = $Object.BaseType #List, DocumentLibrary, etc
$ObjectTitle = $Object.Title
#Get the URL of the List or Library
$RootFolder = Get-PnPProperty -ClientObject $Object -Property RootFolder
$ObjectURL = $Object.RootFolder.ServerRelativeUrl
}
}
#Get all users and group permissions assigned to the object
$RoleAssignments = Get-PnPProperty -ClientObject $Object -Property RoleAssignments
#Get Direct Permissions applied to the Target User on the Object
$TargetUserPermissions=@()
ForEach($RoleAssignment in $RoleAssignments)
{
Get-PnPProperty -ClientObject $RoleAssignment -Property RoleDefinitionBindings, Member
$TargetUserRA = $RoleAssignment | Where {$_.Member.PrincipalType -eq "User" -and $_.Member.LoginName -eq $TargetUser.LoginName}
If($TargetUserRA)
{
$TargetUserPermissions = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name
}
}
#Loop through each user or group associated with the object
$TotalRoleAssignments = $Object.RoleAssignments.Count
For($i=0; $i -lt $TotalRoleAssignments; $i++)
{
$SourceRoleAssignment = $Object.RoleAssignments[$i]
#Get all permission levels assigned to User account directly or via SharePoint Group
$SourceUserPermissions=@()
Get-PnPProperty -ClientObject $SourceRoleAssignment -Property RoleDefinitionBindings, Member
#Leave the Hidden Permissions
If($SourceRoleAssignment.Member.IsHiddenInUI -eq $False)
{
#Get the Permission Levels assigned
$SourceUserPermissions = $SourceRoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name
#Leave Principals with no Permissions assigned
If($SourceUserPermissions.Length -eq 0) {Continue}
#Check Source Permissions granted directly or through SharePoint Group
If($SourceUserPermissions)
{
#Check if the Principal is SharePoint group
If($SourceRoleAssignment.Member.PrincipalType -eq "SharePointGroup")
{
#Get the Group Name
$GroupName = $SourceRoleAssignment.Member.LoginName
#Leave Limited Access Groups
If($GroupName -notlike "*limited access*" -and $GroupName -notlike "*SharingLinks*" -and $GroupName -notlike "*tenant*")
{
#Check if the Source user is member of the Group
$SourceUserIsGroupMember = Get-PnPGroupMember -Identity $GroupName | Where {$_.LoginName -eq $SourceUser.LoginName}
If($SourceUserIsGroupMember -ne $null)
{
#Check if user is already member of the group - If not, Add to group
$TargetUserIsGroupMember = Get-PnPGroupMember -Identity $GroupName | Where {$_.LoginName -eq $TargetUser.LoginName}
If($TargetUserIsGroupMember -eq $null)
{
##Add Target User to the Source User's Group
Add-PnPGroupMember -LoginName $TargetUser.LoginName -Identity $GroupName
Write-Host -f Green "`tAdded user to the Group '$GroupName' on $ObjectType '$ObjectTitle' at '$ObjectURL'"
}
}
}
}
ElseIf ($SourceRoleAssignment.Member.PrincipalType -eq "User" -and $SourceRoleAssignment.Member.LoginName -eq $SourceUser.LoginName)
{
#Add Each Direct permission (such as "Full Control", "Contribute") to the Target User
ForEach($SourceRoleDefinition in $SourceUserPermissions)
{
#Get the Permission Level
$RoleDefinition = Get-PnPRoleDefinition -Identity $SourceRoleDefinition
If($RoleDefinition.Hidden -eq $false)
{
#Check if the Target User does not have the Permission Level already
If($TargetUserPermissions -notcontains $RoleDefinition.Name)
{
#Grant Source User's Permission Level to the Target User
$RoleDefBinding = New-Object Microsoft.SharePoint.Client.RoleDefinitionBindingCollection($Object.Context)
$RoleDefBinding.Add($RoleDefinition)
$Permissions = $Object.RoleAssignments.Add($TargetUser,$RoleDefBinding)
$Object.Update()
Invoke-PnPQuery
Write-Host -f Green "`tGranted $($RoleDefinition.Name) Permission to the User on $ObjectType '$ObjectTitle' at '$ObjectURL'"
}
}
}
}
}
}
}
}
#Function to Clone Permissions on the Web and its underlying objects such as Lists and Libraries, Folders and List Items
Function Clone-PnPPermission
{
[cmdletbinding()]
Param
(
[Parameter(Mandatory=$True)] [Microsoft.SharePoint.Client.Web] $Web,
[Parameter(Mandatory=$True)] [Microsoft.SharePoint.Client.User] $SourceUser,
[Parameter(Mandatory=$True)] [Microsoft.SharePoint.Client.User] $TargetUser,
[Parameter(Mandatory=$false)] [bool] $ScanSubsites,
[Parameter(Mandatory=$false)] [bool] $ScanFolders,
[Parameter(Mandatory=$false)] [bool] $ScanFiles
)
#Call the function to clone permissions on the web
Write-host -f Yellow "Scanning Permissions of the Web: $($Web.URL)"
Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $Web
#Clone Permissions on Lists
Write-host "Scanning Permissions on Lists at $($web.url)" -f Yellow
#Exclude system lists
$ExcludedLists = @("Site Assets","Preservation Hold Library","Style Library", "Site Collection Images","Site Pages", "Content and Structure Reports",
"Form Templates", "Home Page Links", "Forms", "Workflow Tasks", "MicroFeed")
$Lists = Get-PnPProperty -ClientObject $Web -Property Lists
$Lists = $Lists | Where {($_.Hidden -eq $false) -and $ExcludedLists -notcontains $_.Title}
Foreach($List in $Lists)
{
$ListHasUniquePermissions = Get-PnPProperty -ClientObject $List -Property HasUniqueRoleAssignments
If($List.HasUniqueRoleAssignments)
{
#Call the function to Copy Permissions to TargetUser on lists
Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $List
}
#Get List Items (and folders)
If($ScanFolders -or $ScanFiles)
{
#Get Items from List
If($List.ItemCount -gt 0)
{
$global:counter = 0;
$AllListItems = Get-PnPListItem -List $List -PageSize 500 -Fields ID, FileSystemObjectType, FileLeafRef -ScriptBlock `
{ Param($items) $global:counter += $items.Count; Write-Progress -PercentComplete ($global:Counter / ($List.ItemCount) * 100) `
-Activity "Getting Items from List '$($List.Title)'" -Status "Processing Items $global:Counter to $($List.ItemCount)";}
Write-Progress -Activity "Completed Retrieving Items from List $($List.Title)" -Completed
}
}
If($ScanFolders)
{
#Clone Permissions on List folders
Write-host "Scanning Permissions on Folders on List '$($List.Title)'" -f Yellow
#Get Folders from List Items
$Folders = $AllListItems | Where { ($_.FileSystemObjectType -eq "Folder") -and ($_.FieldValues.FileLeafRef -ne "Forms") -and (-Not($_.FieldValues.FileLeafRef.StartsWith("_")))}
If($Folders.count -gt 0)
{
$ItemCounter = 1
#Get Folder permissions
Foreach($Folder in $Folders)
{
Write-Progress -PercentComplete ($ItemCounter / ($Folders.Count) * 100) -Activity "Processing Folders $ItemCounter of $($Folders.Count)" -Status "Searching Unique Permissions in Folders of List '$($List.Title)'"
$FolderHasUniquePermissions = Get-PnPProperty -ClientObject $Folder -Property HasUniqueRoleAssignments
If($FolderHasUniquePermissions -eq $True)
{
#Call the function to Copy Permissions to TargetUser
Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $folder
}
$ItemCounter++
}
Write-Progress -Activity "Completed Cloning Folder Permissions on List $($List.Title)" -Completed
}
}
If($ScanFiles)
{
Write-host "Scanning Permissions on Files/Items on List '$($List.Title)'" -f Yellow
#Get Files / List Items with Unique Permissions
$ListItems = $AllListItems | Where { ($_.FileSystemObjectType -ne "Folder") }
If($ListItems.count -gt 0)
{
$ItemCounter = 1
#Get Item level permissions
Foreach($Item in $ListItems)
{
Write-Progress -PercentComplete ($ItemCounter / ($ListItems.Count) * 100) -Activity "Processing Items $ItemCounter of $($ListItems.Count)" -Status "Searching Unique Permissions in Files/List Items of '$($List.Title)'"
$ItemHasUniquePermissions = Get-PnPProperty -ClientObject $Item -Property HasUniqueRoleAssignments
If($ItemHasUniquePermissions -eq $True)
{
#Call the function to Copy Permissions to TargetUser
Copy-PnPUserPermission -SourceUser $SourceUser -TargetUser $TargetUser -Object $Item
}
$ItemCounter++
}
Write-Progress -Activity "Completed Cloning Item Permissions on List $($List.Title)" -Completed
}
}
}
}
Function Clone-PnPUser
{
[cmdletbinding()]
Param
(
[Parameter(Mandatory=$True)] [String] $SiteURL,
[Parameter(Mandatory=$True)] [String] $SourceUserEmail,
[Parameter(Mandatory=$True)] [String] $TargetUserEmail,
[Parameter(Mandatory=$false)] [bool] $ScanSubsites,
[Parameter(Mandatory=$false)] [bool] $ScanFolders,
[Parameter(Mandatory=$false)] [bool] $ScanFiles
)
#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Interactive
#Get the Context of the Root web
$Ctx = Get-PnPContext
#Prepare the source and target users
$SourceUser = Get-PnPUser | Where-Object Email -eq $SourceUserEmail
If($SourceUser -eq $null) {
$SourceUser = New-PnPUser -LoginName $SourceUserEmail
}
$TargetUser = Get-PnPUser | Where-Object Email -eq $TargetUserEmail
If($TargetUser -eq $null) {
$TargetUser = New-PnPUser -LoginName $TargetUserEmail
}
#Check Whether the Source User is a Site Collection Administrator
Write-host "Scanning Site Collection Administrators..." -f Yellow
$SourceUserIsSiteAdmin = Get-PnPSiteCollectionAdmin | Where {$_.LoginName -eq $SourceUser.LoginName}
If($SourceUserIsSiteAdmin -ne $Null)
{
#Check if the target user is site collection admin already!
$TargetUserIsSiteAdmin = Get-PnPSiteCollectionAdmin | Where {$_.LoginName -eq $TargetUser.LoginName}
If($TargetUserIsSiteAdmin -eq $Null)
{
#Add the Target user as Site Collection Admin
Add-PnPSiteCollectionAdmin -Owners $TargetUser
Write-host "Added Site Collection Administrator!" -f Green
}
}
#Clone Permissions on the site and its underlying objects
$Web = Get-PnPWeb
Clone-PnPPermission -Web $Web -SourceUser $SourceUser -TargetUser $TargetUser -ScanSubsites $ScanSubSites -ScanFolders $ScanFolders -ScanFiles $ScanFiles
#Call the function for subsites
If($ScanSubsites)
{
#Get all subsites
$WebsCollection = Get-PnPSubWeb -Includes HasUniqueRoleAssignments
#Loop throuh each SubSite (web)
Foreach($SubWeb in $WebsCollection)
{
If($SubWeb.HasUniqueRoleAssignments -eq $True)
{
#Call the function to Copy Permissions to TargetUser
Clone-PnPPermission -Web $SubWeb -SourceUser $SourceUser -TargetUser $TargetUser -ScanSubsites $ScanSubSites -ScanFolders $ScanFolders -ScanFiles $ScanFiles
}
}
}
Write-Host -f Green "*** Permission Cloned Successfully! *** "
}
#Define Parameters
$SiteURL = "https://Crescent.sharepoint.com/sites/Marketing"
$SourceUserEmail ="Salaudeen@Crescent.com"
$TargetUserEmail ="Steve@Crescent.com"
#Call the function to clone user permissions
Clone-PnPUser -SiteURL $SiteURL -SourceUserEmail $SourceUserEmail -TargetUserEmail $TargetUserEmail
#Clone-PnPUser -SiteURL $SiteURL -SourceUserEmail $SourceUserEmail -TargetUserEmail $TargetUserEmail -ScanSubsites $true -ScanFolders $true -ScanFiles $True
请注意,此脚本不会复制 Active Directory 组或 Microsoft 365 组权限。它的范围是网站集级别。因此,如果您想克隆多个网站集的权限,您必须使用相关网站 URL 调用“Clone-PnPUser”函数。
当新的团队成员取代旧的团队成员时,这将非常有帮助。通过此脚本,可以将旧团队成员的所有权限简单地转移给新团队成员。不仅是直接权限,此脚本还将目标用户添加到源用户所属的所有 SharePoint 组中。

