Tuesday, January 20, 2015

Copy Permissions from One User to Another in SharePoint using PowerShell

Permission management in SharePoint is always a complex task especially on large environments. Granting permissions in SharePoint becomes cumbersome when you are in a situation to clone an existing user's access rights. Consider this scenario: You have an existing user in a department granted access to various SharePoint web applications, sites, lists, files, etc. and when a new user joins to this department, You-SharePoint Administrator get the requirement of adding new user to all of the places with same access rights as the existing team member!

How will you compare access rights of an existing team member and grant access in bulk? He may be granted permission on various levels with different access rights. It would become very time-consuming to find and grant same level of permissions to multiple users on multiple SharePoint objects. Existing user may be grated access as part of:

  • Farm Administrator group and/or as part of web application policies
  • Member of Site collection administrator group
  • Permissions granted at site level either as part of SharePoint group or with direct permissions
  • Permissions granted to list or libraries by breaking inheritance 
  • Access rights may be via List item or folder level permissions.
In short, permissions can be granted at the following levels in SharePoint:
sharepoint copy permissions from one user to another
Well, To copy permissions from one user to another user on above levels, I've written this PowerShell script - It just scans all possible levels for given source user's access rights and grants permission to the target user.

PowerShell script to clone SharePoint User Permissions:
Important: You must run this script as Farm Administrator! Otherwise, you'll get "Access Denied" error!!
This script iterates through each levels as in the above image and copies permissions between given users at List Item/Folder, Lists, site, Site Collection, Web Application and Farm levels. Just change the parameters for variables $SourceUserl, $TargetUser and $WebAppURL accordingly and run the script. You'll find the script outputs logs on the screen, on wherever it copies permissions.

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
 
#Function to copy user permissions 
Function Copy-UserPermissions($SourceUserID, $TargetUserID, [Microsoft.SharePoint.SPSecurableObject]$Object)
{
 #Determine the given Object type and Get URL of it
    Switch($Object.GetType().FullName)
  {
   "Microsoft.SharePoint.SPWeb"  { $ObjectType = "Site" ; $ObjectURL = $Object.URL; $web = $Object }
 "Microsoft.SharePoint.SPListItem"
   { 
  if($Object.Folder -ne $null)
  {
   $ObjectType = "Folder" ; $ObjectURL = "$($Object.Web.Url)/$($Object.Url)"; $web = $Object.Web
  }
  else
  {
   $ObjectType = "List Item"; $ObjectURL = "$($Object.Web.Url)/$($Object.Url)" ; $web = $Object.Web
  }
   }
   #Microsoft.SharePoint.SPList, Microsoft.SharePoint.SPDocumentLibrary, Microsoft.SharePoint.SPPictureLibrary,etc
   default { $ObjectType = "List/Library"; $ObjectURL = "$($Object.ParentWeb.Url)/$($Object.RootFolder.URL)"; $web = $Object.ParentWeb }
  }
 
 #Get Source and Target Users
 $SourceUser = $Web.EnsureUser($SourceUserID)
 $TargetUser = $Web.EnsureUser($TargetUserID)

 #Get Permissions of the Source user on given object - Such as: Web, List, Folder, ListItem
 $SourcePermissions = $Object.GetUserEffectivePermissionInfo($SourceUser)
 
  #Iterate through each permission and get the details
  foreach($SourceRoleAssignment in $SourcePermissions.RoleAssignments)
  {
    #Get all permission levels assigned to User account directly or via SharePOint Group
    $SourceUserPermissions=@()
        foreach ($SourceRoleDefinition in $SourceRoleAssignment.RoleDefinitionBindings)
        {
      #Exclude "Limited Accesses"
   if($SourceRoleDefinition.Name -ne "Limited Access")
   {
    $SourceUserPermissions += $SourceRoleDefinition.Name
   }
     }
  
  #Check Source Permissions granted directly or through SharePoint Group
    if($SourceUserPermissions)
    {
      if($SourceRoleAssignment.Member -is [Microsoft.SharePoint.SPGroup])
      {
       $SourcePermissionType = "'Member of SharePoint Group - " + $SourceRoleAssignment.Member.Name +"'"
      
       #Add Target User to the Source User's Group
       #Get the Group
       $Group = [Microsoft.SharePoint.SPGroup]$SourceRoleAssignment.Member
       
       #Check if user is already member of the group - If not, Add to group
       if( ($Group.Users | where {$_.UserLogin -eq $TargetUserID}) -eq $null )
       {
         #Add User to Group
         $Group.AddUser($TargetUser)
         #Write-Host Added to Group: $Group.Name
       }
      }
      else
      {
       $SourcePermissionType = "Direct Permission"
      
       #Add Each Direct permission (such as "Full Control", "Contribute") to Target User
       foreach($NewRoleDefinition in $SourceUserPermissions)
       {
        #Role assignment is a linkage between User object and Role Definition
     $NewRoleAssignment = New-Object Microsoft.SharePoint.SPRoleAssignment($TargetUser)
     $NewRoleAssignment.RoleDefinitionBindings.Add($web.RoleDefinitions[$NewRoleDefinition])

     $object.RoleAssignments.Add($NewRoleAssignment)
     $object.Update()
       }
      }
  $SourceUserPermissions = $SourceUserPermissions -join ";" 
  Write-Host "***$($ObjectType) Permissions Copied: $($SourceUserPermissions) at $($ObjectURL) via $($SourcePermissionType)***"
  }
  }
}
 
Function Clone-SPUser($SourceUserID, $TargetUserID, $WebAppURL)
{
 ###Check Whether the Source Users is a Farm Administrator ###
  Write-host "Scanning Farm Administrators Group..."
 #Get the SharePoint Central Administration site
 $AdminWebApp = Get-SPwebapplication -includecentraladministration | where {$_.IsAdministrationWebApplication}
 $AdminSite = Get-SPWeb $AdminWebApp.Url
 $AdminGroupName = $AdminSite.AssociatedOwnerGroup
 $FarmAdminGroup = $AdminSite.SiteGroups[$AdminGroupName]

 #enumerate in farm adminidtrators groups
    foreach ($user in $FarmAdminGroup.users)
    {
  if($User.LoginName.Endswith($SourceUserID,1)) #1 to Ignore Case
  {
   #Add the target user to Farm Administrator Group
   $FarmAdminGroup.AddUser($TargetUserID,"",$TargetUserID , "")
   Write-Host "***Added to Farm Administrators Group!***"
  }     
    }
 
 ### Check Web Application User Policies ###
  Write-host "Scanning Web Application Policies..."
 $WebApp = Get-SPWebApplication $WebAppURL  
  
 Foreach ($Policy in $WebApp.Policies)
 {
  #Check if the search users is member of the group
      if($Policy.UserName.EndsWith($SourceUserID,1))
  {
   #Write-Host $Policy.UserName
   $PolicyRoles=@()
        foreach($Role in $Policy.PolicyRoleBindings)
        {
         $PolicyRoles+= $Role
        }
  }
 }
 #Add Each Policy found
 if($PolicyRoles)
 {
  $WebAppPolicy = $WebApp.Policies.Add($TargetUserID, $TargetUserID)
  foreach($Policy in $PolicyRoles)
  {
   $WebAppPolicy.PolicyRoleBindings.Add($Policy)
     }
     $WebApp.Update()
    Write-host "***Added to Web application Policies!***"
   }
   
  ### Drill down to Site Collections, Webs, Lists & Libraries, Folders and List items ###
  #Get all Site collections of given web app
  $SiteCollections = Get-SPSite -WebApplication $WebAppURL -Limit All
 
  #Convert UserID Into Claims format - If WebApp is claims based! Domain\User to i:0#.w|Domain\User
    if( (Get-SPWebApplication $WebAppURL).UseClaimsAuthentication)
    {
  $SourceUserID = (New-SPClaimsPrincipal -identity $SourceUserID -identitytype 1).ToEncodedString()
    $TargetUserID = (New-SPClaimsPrincipal -identity $TargetUserID -identitytype 1).ToEncodedString()
    }
  
  #Loop through all site collections 
    foreach($Site in $SiteCollections)
    {
   #Prepare the Target user 
   $TargetUser = $Site.RootWeb.EnsureUser($TargetUserID)
  
      Write-host "Scanning Site Collection Administrators Group for:" $site.Url
    ###Check Whether the User is a Site Collection Administrator
      foreach($SiteCollAdmin in $Site.RootWeb.SiteAdministrators)
        {
   if($SiteCollAdmin.LoginName.EndsWith($SourceUserID,1))
   {
    #Make the user as Site collection Admin
       $TargetUser.IsSiteAdmin = $true
       $TargetUser.Update()
    Write-host "***Added to Site Collection Admin Group***"
   }     
     }

  #Get all webs
  $WebsCollection = $Site.AllWebs
    #Loop throuh each Site (web)
    foreach($Web in $WebsCollection)
    {
   if($Web.HasUniqueRoleAssignments -eq $True)
   {
    Write-host "Scanning Site:" $Web.Url
      
        #Call the function to Copy Permissions to TargetUser
        Copy-UserPermissions $SourceUserID $TargetUserID $Web   
      } 

   #Check Lists with Unique Permissions
   Write-host "Scanning Lists on $($web.url)..."
   foreach($List in $web.Lists)
   {
    if($List.HasUniqueRoleAssignments -eq $True -and ($List.Hidden -eq $false))
    {
     #Call the function to Copy Permissions to TargetUser
     Copy-UserPermissions $SourceUserID $TargetUserID $List
    }
     
    #Check Folders with Unique Permissions
        $UniqueFolders = $List.Folders | where { $_.HasUniqueRoleAssignments -eq $True }
                #Get Folder permissions
    if($UniqueFolders)
    {
                 foreach($folder in $UniqueFolders)
           {
           #Call the function to Copy Permissions to TargetUser
                     Copy-UserPermissions $SourceUserID $TargetUserID $folder     
     }
    }
     
        #Check List Items with Unique Permissions
       $UniqueItems = $List.Items | where { $_.HasUniqueRoleAssignments -eq $True }
    if($UniqueItems)
    {
                 #Get Item level permissions
                 foreach($item in $UniqueItems)
           {
           #Call the function to Copy Permissions to TargetUser
           Copy-UserPermissions $SourceUserID $TargetUserID $Item 
     }
    }
      }
    }
  }
 Write-Host "Permission are copied successfully!"  
}
#Define variables for processing
$WebAppURL = "http://sharepoint.crescent.com"

#Provide input for source and Target user Ids
$SourceUser ="Crescent\TonyW"
$TargetUser ="Crescent\Salaudeen"

#Call the function to clone user access rights
Clone-SPUser $SourceUser $TargetUser $WebAppURL 

Copy User Permissions at list level:
This script is broken into two functions: Copy-UserPermissions and Clone-SPSuer for convenience. Lets say, you want to copy permissions at list level, then you can utilize the Copy-UserPermission function as:
 
$WebURL = "http://sharepoint.crescent.com/sites/sales"

$web = Get-SPWeb $WebURL

$SourceUser ="i:0#.w|Crescent\TonyW"
$TargetUser ="i:0#.w|Crescent\Salaudeen"

$list = $Web.Lists["Invoice"]

#$folder = $list.Folders[0]
#$ListItem = $list.Items[0]

#Call the function to copy user permissions programmatically at LIST level
Copy-UserPermissions $SourceUser $TargetUser $list

This script just clone's user permissions at list level (copies at only list level, no drill-down to Folders and Items!).

Please note, This script doesn't clone permissions which are granted via Active Directory Security groups!



You might also like:
SharePoint Usage Reports
Usage reports, collaboration and audit for SharePoint.
Document SharePoint Farm
Automatically generate SharePoint documentation.
*Sponsored


Check out these SharePoint products:

16 comments :

  1. Wonderful script Salaudeen but for some reason it is not adding the target user to any groups my source user is a member of. Any ideas why that might be?

    ReplyDelete
    Replies
    1. I would recommend to run this script from FARM ADMIN account.

      Delete
  2. Does it also check if the user is permitted by an Active Directory group? Or would it be possible to extend the script checking that and adding the new account using a sharepoint group or give direct access at that site/item...?

    ReplyDelete
    Replies
    1. Nope! This script doesn't clone Active Directory groups Permissions!

      Delete
  3. Hi, great script, just the thing we where looking for.
    But i get a lot of error's (no not de access denied):
    Sometimes it seems like my object is empty (Site/List/Folder/Listitem) how can that be?
    And ther is no check on limited Access permission level ==> had to catch try that block.

    ReplyDelete
    Replies
    1. Limited access can't be copied! AFAIK.

      Delete
    2. Idd that's true, but you've should check on it no?

      Delete
  4. Thanks for the great script. however there are errors as mentioned in comments above... the errors are due to null values at line 192
    $UniqueFolders = $List.Folders | where { $_.HasUniqueRoleAssignments -eq $True }
    #Get Folder permissions
    foreach($folder in $UniqueFolders) ----- Here the $folder object is getting picked up, even if the $UniqueFolders is null arrray

    It can be handled as follows:

    if ($UniqueFolders -eq $null)
    { }
    else
    {
    #Get Folder permissions
    foreach($folder in $UniqueFolders)
    {
    #Call the function to Copy Permissions to TargetUser
    Copy-UserPermissions $SourceUserID $TargetUserID $folder
    }
    }

    Similary for the section for the $UniqueItems....








    ReplyDelete
  5. Why on sp2010 throw so many exception like :
    GetType : You cannot call a method on a null-valued expression.
    At C:\Users\mossadm\Desktop\CloneUser.PS1:8 char:23
    + Switch($Object.GetType <<<< ().FullName)
    + CategoryInfo : InvalidOperation: (GetType:String) [], RuntimeEx
    ception
    + FullyQualifiedErrorId : InvokeMethodOnNull

    GetUserEffectivePermissionInfo : You cannot call a method on a null-valued expr
    ession.
    At C:\Users\mossadm\Desktop\CloneUser.PS1:31 char:60
    + $SourcePermissions = $Object.GetUserEffectivePermissionInfo <<<< ($SourceUser
    )
    + CategoryInfo : InvalidOperation: (GetUserEffectivePermissionInf
    o:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    ReplyDelete
    Replies
    1. Im getting the same error ?? Any resolution to this?

      Delete
  6. Very nice script ! Thanks a lot

    ReplyDelete
  7. I am getting the following error when running this script
    You cannot call a method on a null-valued expression.
    At line:313 char:40
    + if($SiteCollAdmin.LoginName.EndsWith <<<< ($SourceUserID,1))
    + CategoryInfo : InvalidOperation: (EndsWith:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Clone-SPUser : Exception has been thrown by the target of an invocation.
    At line:447 char:13
    + Clone-SPUser <<<< $SourceUser $TargetUser $WebAppURL
    + CategoryInfo : NotSpecified: (:) [Clone-SPUser], TargetInvocationException
    + FullyQualifiedErrorId : System.Reflection.TargetInvocationException,Clone-SPUser

    ReplyDelete

Please Login and comment to get your questions answered!

You might also like:

Related Posts Plugin for WordPress, Blogger...