Second Life of a Hungarian SharePoint Geek

February 7, 2015

Changing Site Collection Administrators via PowerShell Using Elevated Permissions

Filed under: PowerShell, Security, SP 2010 — Tags: , , — Peter Holpar @ 05:06

Recently we migrated the users of a SharePoint farm into another domain. As part of the migration the MySites of the users should have been migrated as well. For the user migration we used the Move-SPUser Cmdlet, however it seems to have no effect on the site primary and secondary administrators (that means the Owner and SecondaryContact properties of the corresponding SPSite object). As you might know, each MySite is a site collection in SharePoint, the user the MySite belongs to is by default the primary site collection administrator and there is no secondary admin specified. It is possible to change (“migrate”) the admins via the Central Administration web UI, however having hundreds of users, it was not a viable option to us. But no problem, we can surely change these values via PowerShell as well, couldn’t we? Let’s test it first, and use PowerShell to read the values. We have all of the MySites in a separate web application, so we tried to iterate through all its site collections and dump out the information we need.

These samples assume that the URL of the web application (the MySite root) is http://mysiteroot, and the MySites are under the managed path /users, for example, the URL of the MySite of user1 is http://mysiteroot/users/user1.

Our first try was:

$waUrl = "http://mysiteroot"

$wa = Get-SPWebApplication $waUrl
Get-SPSite -WebApplication $wa -Limit ALL | % {
  Write-Host "Url:" $_.Url
  Write-Host "Primary Administrator:" $_.Owner.LoginName
  Write-Host "Secondary Administrator:" $_.SecondaryContact.LoginName
  Write-Host "—————————–"
}

Surprise! The URLs are dumped out, however the Owner only for the root site and the own MySite of the user who executed the script, the SecondaryContact only for the root (in the MySite of the executing user had no SecondaryContact defined). That means one could access these properties only for the site where one is defined as site collection administrator (either primary or secondary). There is no error message (like Access denied) for the other sites, simply no information about the admins displayed.

However, if we run the code in an elevated privileges block, the Owner and SecondaryContact properties are dumped out as well:

$waUrl = "http://mysiteroot"

[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges (
  {
    $wa = Get-SPWebApplication $waUrl
    Get-SPSite -WebApplication $wa -Limit ALL | % {
      Write-Host "Url:" $_.Url
      Write-Host "Primary Administrator:" $_.Owner.LoginName
      Write-Host "Secondary Administrator:" $_.SecondaryContact.LoginName
      Write-Host "—————————–"
    }
  }
)

Note 1: If you would like to dump out the info without Write-Host in a RunWithElevatedPrivileges code block, you would not see the result in the console, even the Url properties would disappear.

The following sample display no result:

$waUrl = "http://mysiteroot"

[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges (
  {
    $wa = Get-SPWebApplication $waUrl
    Get-SPSite -WebApplication $wa -Limit ALL | % {
      $_.Url
      $_.Owner.LoginName
      $_.SecondaryContact.LoginName
    }
  }
)

Note 2: You don’t need to place all of your code in the elevated block. It is enough to place the code that gets the reference to the web application (site, web, etc.) in this block.

The following code works just as well as the original one with elevated privileges:

$waUrl = "http://mysiteroot"

[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges (
  {
    $wa = Get-SPWebApplication $waUrl
  }
)

Get-SPSite -WebApplication $wa -Limit ALL | % {
  Write-Host "Url:" $_.Url
  Write-Host "Primary Administrator:" $_.Owner.LoginName
  Write-Host "Secondary Administrator:" $_.SecondaryContact.LoginName
  Write-Host "—————————–"
}

After we displayed the information about the site admins, let’s see, how can we change the configuration. First I tried without elevation, although I was already sure, that it won’t perform the requested operation. In the following samples I try to set the Owner property, but in the case of SecondaryContact it would have the same outcome.

$url = "http://mysiteroot/users/user1"
$siteOwnerLogin = "domain\user1" 
$web = Get-SPWeb $url
$user = $web.EnsureUser($siteOwnerLogin)
$site = $web.Site
$site.Owner = $user

The code above does not work. We receive an error message (Attempted to perform an unauthorized operation.), and the site owner is not changed.

However, using the elevated code block will solve the problem again:

$url = "http://mysiteroot/users/user1"
$siteOwnerLogin = "domain\user1"

[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges(
     {
         $web = Get-SPWeb $url
         $user = $web.EnsureUser($siteOwnerLogin)
         $site = $web.Site
         $site.Owner = $user
     }
)

You can find opinions on the web, like this one, that states, that running PowerShell code in elevated block has no effect at all. The samples above demonstrate however, that it really DOES have effect.

But what effect of code elevation is it, that makes it possible to get and set the Owner and SecondaryContact properties in the former samples?

If you have a look at the source code of the Owner (or SecondaryContact) property, for example, using Reflector, you will see, that there is a double permission check, both of them can throw an UnauthorizedAccessException. We can ignore the first one (that is !this.AdministratorOperationMode), while this condition is only checked if there is no CurrentUser for the RootWeb of the site. The second permission check is the important one, that is checked if the CurrentUser is not null, and it is:

(!this.RootWeb.CurrentUser.IsSiteAdmin)

If this condition is true, then an UnauthorizedAccessException is thrown. The setter method of the Owner (or SecondaryContact) property calls first the getter, so the same condition is checked in this case as well. Although the call to the getter is in a try-catch block, the catch is valid only for the type SPException, so it has no effect on the UnauthorizedAccessException thrown by the getter.

Remark: I have to admit, that it is not yet clear to me, why the exception thrown by the getter is not displayed when calling the getter from PowerShell.

Let’s see a further example of code elevation from PowerShell to clear the reason, why the former samples with elevation solved the issue of getting / setting the Owner and SecondaryContact properties:

$url = "http://mysiteroot/users/user1"
$web = Get-SPWeb $url

$userName = $web.CurrentUser.LoginName
$userIsAdmin = $web.CurrentUser.IsSiteAdmin

# elevated privilages
[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges(
     {
         $web = Get-SPWeb $url
         $userNameElevated = $web.CurrentUser
         $userIsAdminElevated = $web.CurrentUser.IsSiteAdmin
     }
)

Write-Host "Before elevation"
Write-Host "User name: " $userName
Write-Host "User is site admin: " $userIsAdmin
Write-Host "After elevation"
Write-Host "User name: " $userNameElevated
Write-Host "User is site admin: " $userIsAdminElevated

Assuming the executing user has neither owner nor secondary contact for the site collection, the code returns the same user name before and after elevation, however the value of the IsSiteAdmin property is false before the elevation, but true after the elevation. As we have seen from the previous reflectoring, this change is just enough for the getter / setter  methods of the Owner and SecondaryContact properties to work.

In my next post I show you an alternative method that make it possible to read / change these values from PowerShell without any kind of elevation.

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: