How to grant farm-wide read access to a user in Sharepoint 2013

A few weeks ago I was asked to grant a particular user read rights to any resources of a Sharepoint farm; sites, folder, lists, etc…, irrespective of the permission currently set on those resources (i.e. possible unique permissions).

By the way, it appears that I’m also a particularly skilled Sharepoint administrator. It apparently happened over night, without me even noticing.

Unable to find any out-of-the-box solution for my particular situation, I came out with a bunch of scripts that did the trick and I think are worth sharing. Of course, the work I did has been mainly taping together snippets and scripts that I found somewhere else so the credit goes mostly to the Internet, as usual. There is a bit of original work, though.

The basic idea was to give the rights to a new group, GlobalReaders, created for the occasion in each root site, and then add the users to the group as needed. In the future, the users will be easily removed and the rights revoked, if needed.

1. Automating the creation of the GlobalReader group

If the farm has a large number of web applications and site collections, it can be hard to manually create the group. So the first step is automating this task. The following script scans all the sites of the farm and adds the desired group to their root web

function Add-GlobalReadersGroupsToAllSites
{
    $sites = Get-SPSite -Limit ALL
    Write-Host $sites
    foreach($site in $sites)
    {
        Write-Host "Adding GlobalReaders group to " $site.Url
        $site.RootWeb.SiteGroups.Add("GlobalReaders", $site.Owner, $site.Owner,
            "Members of this group has read rights on any resources of the farm (sites, lists, folders, etc...)")
        $site.RootWeb.Update()
    }
}

2. Assigning a group read rights on an SPWeb

The first brick of the procedure is a function that takes the name of a group and an SPWeb URL, and assign the group read rights over the Web itself. The script also takes into account folders and lists whose permission inheritance has been broken (unique permission).


# Assigns Read permission to a list of users for the Web provided
function Grant-ReadPermToGroup
{
    if($args.Length -lt 2)
    {
        Write-Error "Grant-ReadPermToUser requires 2 parameters. Usage: Grant-ReadPermToUser <SubSiteUrl> <GroupName>"
    }

    # Write-Host $args[0] " " $args[1]

    $web = Get-SPWeb -Identity $args[0]
    $site = $web.Site
    $groupList = $web.SiteGroups | Where { $_.Name -eq "GlobalReaders" }
    $readerGroup = $groupList[0]

    Write-Host "Assigning read permission for Web " $web "($($web.Url))"
    Write-Host "Group:"

    Write-Host "    "$readerGroup.Name

    Write-Host "Granting read permission to $readerGroup"
    $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($readerGroup)
    $role = $site.RootWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)

    $assignment.RoleDefinitionBindings.Add($role);
    Write-Host $assignment
    $web.RoleAssignments.Add($assignment)

    # #########################################################################
    # Now let's deal with lists and folders with unique permission assignments

    # Finds all the lists of the given web that have unique role assignments
    # (broken inheritance)
    $uniqueAssignList = $web | select -ExpandProperty Lists |
        Where { -not $_.Hidden -and $_.EntityTypeName -ne "PublishedFeedList" -and $_.HasUniqueRoleAssignments}

    foreach($l in $uniqueAssignList)
    {
        Write-Host "Grantig read permission for list " $l.Title
        # Assign read permission
        # $role = $site.RootWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        $role = $l.ParentWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        Write-Host "Role: " $role
        $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($readerGroup)
        $assignment.RoleDefinitionBindings.Add($role)

        $l.RoleAssignments.Add($assignment)
    }

    # Finds all the non-hidden folder (at any level)
    # of the given web that have unique role assignments
    # (broken inherintance)
    $uniqueFolders = $web |
        select -ExpandProperty Lists |
        Where { -not $_.Hidden -and $_.EntityTypeName -ne "PublishedFeedList"} |
        select -ExpandProperty Folders |
        Where { $_.HasUniqueRoleAssignments -and -not $_.Hidden } 

    foreach($f in $uniqueFolders)
    {
        Write-Host "Grantig read permission for folder " $f.Title
        # Assign read permission
        # $role = $site.RootWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        $role = $f.ParentList.ParentWeb.RoleDefinitions.GetByType([Microsoft.SharePoint.SPRoleType]::Reader)
        Write-Host "Role: " $role
        $assignment = New-Object Microsoft.SharePoint.SPRoleAssignment($readerGroup)
        $assignment.RoleDefinitionBindings.Add($role)

        $f.RoleAssignments.Add($assignment)
    }

    Write-Host "     OK! (^__^)"
}

Now we can easily cover the requirement for any SPWeb, provided that we can recursively loop over all of them (and we can, of course).

3. Recursively looping through all the webs

Once you assign the permission for a web, all the subweb are covered, unless they have unique permission. In this case, you have to individually take care of any subwebs that have unique permission (i.e. they do not inherit the permission from the parent because the inheritance has been broken by an administrator at some point in time):

# Grants read permission to a user for a Web and, recursively,
# all its sub-webs
function Grant-ReadPermRecursive
{
    if($args.Length -lt 2)
    {
        Write-Error "Parameters required: Web Url, Group Name"
    }
    # If runDry = True, the function walks the entire web tree, without actually adding new permissions
    $runDry = $false
    if($args.Length -ge 3)
    {
        $runDry = $args[2]
    }
    Write-Host "RunDry = "$runDry
    $web = Get-SPWeb -Identity $args[0]
    Write-Host "Web: " $web
    $groupName = $args[1]
    Write-Host "Username list: " $groupName
    if(!$runDry)
    {
        # Write-Host "I would actually grant permissions"
        Grant-ReadPermToGroup $web.Url $groupName
    }

    $subWebList = $web.Webs
    if($subWebList.Length -gt 0)
    {
        foreach($subWeb in $subWebList)
        {
            if($subWeb.HasUniquePerm)
            {
                Grant-ReadPermRecursive $subWeb.Url $groupName $runDry
            }
        }
    }
}

4. Wrapping it all

Finally, we can wrap the operation with a convenient function that operates on the farm level, looping over all the SPWebApplications and taking into account the root web of each:

$allFarmWebApplications = Get-SPWebApplication
foreach($webApplication in $allFarmWebApplications)
{
    Write-Host "WebApplication: " $webApplication.Url
    foreach($site in $webApplication.Sites)
    {
        Write-Host "   Site: "$site.Url "   RootWeb: "$site.RootWeb
        $rootWeb = $site.RootWeb
        Grant-ReadPermRecursive $rootWeb.Url "GlobalReaders" $false
        Write-Host "-----------------------------"
        Write-Host " "
    }
}

2 thoughts on “How to grant farm-wide read access to a user in Sharepoint 2013

Leave a comment