Friday, August 28, 2015

SharePoint 2013: Changed JavaScript file not being downloaded

Problem

You've made some changes to a JavaScript file used in the master page of a farm web application. Using SharePoint Designer, you checked it out, edited it, and then checked it back in.  When you then open a browser and navigate to the home page of the web application, you can see the affect of your JavaScript modifications.  However, when you then browse to the page from your workstation using your standard account, no change is seen.  You try different browsers and ask different users, and the response is the same: no changes.  Flushing the browser cache has no affect.  You also try flushing page, output and blob caches, but no affect.  You have activated the Publishing feature for the site collection containing the target master page.
 
Resolution of this issue involves the consequences of activating the Publishing feature, which implements content management, including content approval.

Solution

  1. Using an administrator account, connect to the root of the site collection employing the JavaScript file.
  2. Go: Settings > Site Settings > Site Administration > Content and structure.
  3. From the View menu, select Pending Approval.
  4. Select the modified JavaScript file.
  5. From the Actions menu, select Approve.

References

Thursday, August 20, 2015

SharePoint 2013: The process cannot access the file... because it is being used by another process

Problem

You are performing a migration of a content database from SharePoint 2007 to SharePoint 2013. You've mounted the database to the 2013 farm and then migrate users:
$wa = Get-SPWebApplication https://URL
$wa.MigrateUsers($true)
$wa.ProvisionGlobally()
You then see the following error appear once the ProvisionGlobally method is executed:
Exception calling "ProvisionGlobally" with "0" argument(s): "The process cannot 
access the file 'C:\inetpub\wwwroot\wss\VirtualDirectories\[domainnamePort]\_app_bin\
Microsoft.SharePoint.WorkflowServices.ApplicationPages.dll' because it is being used 
by another process."
Browsing to the site, you experience an HTTP 404 error.  You begin troubleshooting.

Troubleshooting

  1. Check status of Windows Server services: all SharePoint and SharePoint-related services are running.
  2. Check status of application pools in IIS: all SharePoint application pools are started.
  3. Check status of sites in IIS: all SharePoint sites are started.
  4. Check status of services on server in Central Administration: Microsoft SharePoint Foundation Web Application is started.
  5. Check critical service account permissions to content database: spFarm, spService, spApp, spAdmin all have appropriate permissions.
  6. Check critical service account local permissions: all are members of local Administrators group.
  7. Check Alternate Access Mappings:  all mappings correct.
  8. Check file checkout: default file is not checked out.
  9. Unmount and remount content database: still have issue.
  10. Perform IISRESET: still have issue.
  11. Restart server: still have issue.
  12. Stop and restart Microsoft SharePoint Foundation Web Application service on server: service stopped without issue; however, when attempting to start it, observed this error:
    The process cannot access the file 'C:\inetpub\wwwroot\wss\VirtualDirectories\
    [DomainnamePort]\_app_bin\Microsoft.SharePoint.WorkflowServices.ApplicationPages.dll' because it is being used by another process. 
  13. Perform Internet search: some postings indicate that perhaps something is locking the port; do port check.
  14. Run netstat -ano: identified single process listening at port 80, and this was the NT Kernel & System.
  15. Perform Internet search: a posting suggested disabling anti-virus.
  16. Disable McAfee On-Access Scan on the web server: successfully browsed to site.

Solution

  • When observing an IIS file being locked by a process, in combination with an HTTP 4040 error, try disabling anti-virus.

References

  • Subsequently restarted McAfee anti-virus without observing any adverse affect on web application operation.

Wednesday, August 12, 2015

SharePoint 2013: PowerShell automation of user and group reconfigurations


Introduction

This is a collection of PowerShell methods to automate SharePoint user and group reconfigurations. Topics in this posting include:

  • Remove a SharePoint web user group's permission level
  • Cleanup Usernames after migration
  • Configure primary and secondary site collection administrators
  • Add additional Site Collection Administrators
  • Add an Active Directory user group as a site collection administrator
  • Remove all users from SharePoint web's user group

Remove a SharePoint web user group's permission level

To interact with SharePoint users and groups and their permission levels for a specific web in your web application, you need to work with four objects:
  • SPWeb: this is the object that contains all of the members associated with a specific web in your web application.
  • RoleDefinitions: this a property of the SPWeb object and contains a collection of permission levels, such Full Control, Design, Contribute and Read, that you have defined for the web application.
  • RoleAssignments: this is a property of the SPWeb object and contains a collection of SharePoint users and groups that have been assigned a permission level to the web.
  • RoleDefinitionBindings: this  is a property of the RoleAssignments object and contains a collection of the actual associations, or bindings, between a role definition and an SPWeb user or group member.
These four objects are all that you need to get started. First, get an instance of the web object:
$Web = Get-SPWeb "htp://contoso.com/web1"
Interrogate the web object to see all of that web's current role assignments:
$Web.RoleAssignments
If this web was created using default settings, it would include these assignments: Web1 Owners (Full Control), Web1 Members (Contribute), and Web1 Visitors (Read). Let's say, for example, that the role assignment that needs to be modified is the third in this collection, or Web1 Visitors.  Then, to do this, we need to get an instance of that assignment, which you can do via its index in the collection:
$ra= $Web.RoleAssignments["2"]
You could execute this by itself to see what's included in this object. So, executing:
$Web.RoleAssignments["2"]
gets you something like this:
Member RoleDefinitionBindings       Parent ParentSecurableObject
Web1 Visitors       {Read}       Web1       Web1
Now, an instance of a group within a web can have multiple permission levels, or Role Assignments. Therefore, we need to get an instance of the group's particular permission level, or Role Definition, that needs to be removed.  This aspect of the group is actually a property that is a member of the group's parent in this instance, or Web1.  So:
$rd = $ra.Parent.RoleDefinitions["Read"]
We now have an instance of the group's assignment, $ra, as it exists within Web1, and we have an instance of that group's permission level definition, $rd, as it exists within Web1.  We can now remove the instance of the particular permission level, or role definition, that we want to remove from the group's role assignments.    This is done like so:
$ra.RoleDefinitionBindings.Remove($rd)
The next step is to update the group with this change:
$ra.Update()
And now you're done. It really is that easy.  Here's what the total script would look like for removing the Read permission from the Web1 Visitors group:

1.$web = get-SPWeb "[root site collection url]\web1"
2.$group = $web.SiteGroups["Web1 Visitors"]
3.$ra = $group.ParentWeb.RoleAssignments.GetAssignmentByPrincipal($group)
4.$rd = $group.ParentWeb.RoleDefinitions["Read"]
5.$ra.RoleDefinitionBindings.Remove($rd)
6.$ra.Update()
7.$group.Update()
8.$web.Dispose()

Note that since this is the only permission that this group is configured with, this effectively removes Web1 Visitors from the list of SharePoint groups configured for this web.  This is in fact how you remove a SharePoint user group from a web.

Now take the more general case, where a web's user group, call it Web1 Members, has previously been assigned a number of different permission levels, say Design, Manage Hierarchy and Approve, in addition to its standard permission level of Contribute.  You want to remove all the non-standard permission levels to get this user group back to what it should be.  The script for this would be:

01.$web = get-SPWeb "[root site collection url]/web1"
02.$group = $web.SiteGroups["Web1 Members"]
03.$ra = $group.ParentWeb.RoleAssignments.GetAssignmentByPrincipal($group)
04.$rd = $group.ParentWeb.RoleDefinitions["Design"]
05.$ra.RoleDefinitionBindings.Remove($rd)
06.$rd = $group.ParentWeb.RoleDefinitions["Manage Hierarchy"]
07.$ra.RoleDefinitionBindings.Remove($rd)
08.$rd = $group.ParentWeb.RoleDefinitions["Approve"]
09.$ra.RoleDefinitionBindings.Remove($rd)
10.$ra.Update()
11.$group.Update()
12.$web.Dispose()

Cleanup Usernames after migration

I use this script to clean up usernames after migrating the content database to 2013 and upgrading to claims.  These usernames had a variety of special characters and domain prefixes that needed to be removed.  This script doesn't do the mapping of these usernames to Active Directory.  All it does is interrogate the SiteUsers collection of usernames and modify the DisplayName property of each one.  This script works with these two SharePoint objects:
  • SPWeb
  • SPUser
It also employs regular expressions to simplify the code.  It works by first interrogating the SPWeb object and then getting an instance of the SiteUsers collection.  It then works through each member of this collection, using that member to get an instance of the SPUser object.  Once the SPUser object instance is obtained, it performs various string operations on the SPUser object's DisplayName property.  Along the way, some record keeping is performed.  Thanks to The Johnson Blog for the regular expression used in this script.

01.If ((Get-PSSnapin -Name "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null ){Add-PsSnapin Microsoft.SharePoint.PowerShell}
02.$Web=Get-SPWeb http://[URL]
03.$SiteUsers=$web.SiteUsers | Where {($_.UserLogin.Contains("DOMAINPREFIX1")) -or ($_.UserLogin.Contains("DOMAINPREFIX2"))} | Sort-Object DisplayName
04.$web.dispose()
05.$Count=1
06.Write-Host ""
07.Write-Host "02 - Cleaning SharePoint User Display Names..."
08.Write-Host "=============================================="
09.ForEach ($User in $SiteUsers)
10.  {
11.    $CandidateUser=Get-SPUser -Identity $User.UserLogin -Web "http://[URL]"
12.    $DisplayName = $CandidateUser.DisplayName
13.    $OldDisplayName = $DisplayName
14.    $DisplayName = $DisplayName.Replace("DOMAINPREFIX1:","")
15.    $DisplayName = $DisplayName.Replace("DOMAINPREFIX2:","")
16.    $DisplayName = $DisplayName.Replace("í","i")
17.    $DisplayName = $DisplayName.Replace("á","a")
18.    $DisplayName = $DisplayName.Replace("ê","e")
19.    $DisplayName = ([Regex]::Replace($DisplayName, '\b(\w)', { param($m) $m.Value.ToUpper() })).Replace(".", " ")
20.    Set-SPUser -Identity $CandidateUser -Web "http://[URL]" -DisplayName $DisplayName
21.    Write-Host $Count, ": updating ", $OldDisplayname, " to ", $DisplayName -ForegroundColor Green
22.    $Count=$count + 1
23.  }

Configure primary and secondary site collection administrators

I use this script to automate configuration of the primary and secondary site collection administrators.  I developed this script to automate the migration of a SharePoint Server 2007 Enterprise farm, employing Active Directory Lightweight Directory Services (AD LDS) authentication to SharePoint Server 2013 employing standard AD authentication.  This script uses a single SharePoint object; SPUser, specifically configuring the -Owner and -SecondaryContact properties of the object.

01.If ((Get-PSSnapin -Name "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null ){Add-PsSnapin Microsoft.SharePoint.PowerShell}
02. 
03.Write-Host ""
04.Write-Host "Configuring Site Collection Administrators"
05.Write-Host "=========================================="
06. 
07.Function UpdateSiteCollectionAdmins
08.    {
09.        Write-Host ""
10.        Write-Host "Updating Site Collection Administrators (farm level)..."
11.        Write-Host ""
12.        Try
13.            {
14.                $CurrentOwner = Get-SPSite "http://[URL]" | Select Owner
15.                Write-Host "  Current primary site collection administrator: ", $CurrentOwner -BackgroundColor DarkGray
16.                Write-Host "  Modifying this to: i:0#.w|DOMAIN\spadmin..." -BackgroundColor DarkGray
17.                Set-SpSite "http://[URL]" -owneralias "i:0#.w|DOMAIN\spadmin"
18.                $CurrentOwner = Get-SPSite "http://[URL]" | Select Owner
19.                Write-Host "    New primary site collection administrator set to", $CurrentOwner -ForegroundColor Green
20.            }
21.        Catch
22.            {
23.                Write-Host "   Problem encountered trying to modify primary site collection administrator." -ForegroundColor red
24.                Write-host "   Skipping to next SP task" -ForegroundColor red
25.            }
26.        Finally {}
27.        Try
28.            {
29.                $SecondaryContact = Get-SPSite "http://[URL]" | Select SecondaryContact
30.                Write-Host "  Current secondary site collection administrator: ", $SecondaryContact -BackgroundColor DarkGray
31.                Write-Host "  Modifying this to: i:0#.w|DOMAIN\First.Last..." -BackgroundColor DarkGray
32.                Set-SpSite "http://[URL]" -SecondaryOwnerAlias "i:0#.w|DOMAIN\First.Last"
33.                $CurrentOwner = Get-SPSite "http://[URL]" | Select SecondaryContact
34.                Write-Host "    New secondary site collection administrator set to", $SecondaryContact -ForegroundColor Green
35.            }
36.        Catch
37.            {
38.                Write-Host "   Problem encountered trying to modify secondary site collection administrator." -ForegroundColor red
39.                Write-host "   Skipping to next SP task" -ForegroundColor red
40.            }
41.        Finally {}
42.    }
43. 
44.UpdateSiteCollectionAdmins

Add additional Site Collection Administrators

The following script adds additional site collection administrators (SCA).  These are the SCAs that you would normally add by using the Site Settings capabilities of the site collection (go: Settings > Site Settings > Go to top level site settings > User and Permissions > Site collection administrators).  These can also be added by setting the SPUser object's IsSiteAdmin property, which the following script presents.  Note the claims authentication formatting.

1.Write-Host "    Now adding i:0#.w|DOMAIN\spadmin to site collection administrators..."  -BackgroundColor DarkGray
2.$User = Get-SPUser -Web "[URL]" -Identity "i:0#.w|DOMAIN\spadmin"
3.$user.IsSiteAdmin = $true
4.$user.Update()
5.Write-Host "      Added"  -ForegroundColor Green

Add an Active Directory user group as a site collection administrator

 One thing I found useful in administration was to move from assigning site collection administrator privileges individually to assigning this permission to a user group.  You can of course do this using the Site Settings capabilities.  You can also do this via PowerShell and again using the site collection's SPUser object.  However, there is one caveat: you need to use the AD group's SID, not its display name.  For example, consider a user group containing system administrators., DOMAIN\SysAdmins, with SID s-1-2-34-1234567890-1234567890-1234567890-1234. Then, accounting for claims authentication, its SharePoint identity would be
 c:0+.w|s-1-2-34-1234567890-1234567890-1234567890-1234
 and the script would be:

1.Write-Host "    Now adding DOMAIN\SysAdmins to site collection administrators..."  -BackgroundColor DarkGray
2.New-SPUser -UserAlias "c:0+.w|s-1-2-34-1234567890-1234567890-1234567890-1234" -DisplayName "DOMAIN\SysAdmins" -Web "[URL]"
3.$User = Get-SPUser -Web "[URL]" -Identity "c:0+.w|s-1-2-34-1234567890-1234567890-1234567890-1234"
4.$user.IsSiteAdmin = $true
5.$user.Update()
6.Write-Host "      Added"  -ForegroundColor Green

Remove all users from SharePoint web's user group

Consider that you have particular user group in a web, call it Web1, and a user group, Web1 Members, containing a number of SharePoint users, and that you would like to completely empty Web1 Members of all users so as to replace them with a single Active Directory group, DOMAIN\Web1-Users. This was the challenge I faced when migrating a legacy 2007 farm and wanting to implement this in an automated way so as to ensure maximum repeatability.  The below script does this.

It employs just two key SharePoint objects: SPWeb and SPUser. To remove all users from a group, the script first obtains an instance of the particular group from the SPWeb's group collection, SiteGroups. Each member of this collection in turn contains a collection of its users that you can access via the group's Users collection. After obtaining an instance of a user in this group, the group's RemoveUser method is used to remove the user from the group.  Once all the SharePoint users have been removed from this group, it remains to add a single AD group to complete the migration to a full AD user group approach.

Adding an Active Directory user group to a SharePoint user group requires the use of a single object, SPWeb, and a method that you may not be familiar with, EnsureUser, which get's an instance of the user.
The EnsureUser method get's an instance of the user among the web's user collection or it add's the AD user to the web's user collection.  Contrast this with Get-SPUser, which requires that the user already be a member of the farm's user collection or otherwise it fails.  Note that in this case, the EnsureUser method engages an Active Directory group the same as an Active Directory user.
This script first gets an instance of the SharePoint web group that you want to work on.  It then creates an instance of the Active Directory "user" (in this case, group) that you want to add.  I then uses the group instance's AddUser method to add the "user" to the SharePoint group.  Note how the method is named.  Here's the complete script:

01.Function UpdateWeb1Security
02.    {
03.        Try
04.            {
05.                Write-Host "Web1 Members: removing all users" -BackgroundColor DarkGray
06.                $web = Get-SPWeb "http://[URL]"
07.                $SPGroup = $web.SiteGroups["Web1 Members"]
08.                $Users = $SPGroup.Users
09.                ForEach ($User in $Users)
10.                    {
11.                        $SPGroup.RemoveUser($User)
12.                        Write-Host " Successfully removed: ", $User.Name  -ForegroundColor Green  
13.                    }
14.            }
15.        Catch
16.            {
17.                Write-Host " Problem encountered trying to remove member. Skipping to next SP group" -ForegroundColor red
18.            }
19.        Finally {}
20.        Try
21.            {
22.                $web = Get-SPWeb "http://[URL]"
23.                $SPGroup = $web.SiteGroups["Web1 Members"]
24.                $ADGroup = $web.Site.RootWeb.EnsureUser(“DOMAIN\Web1-Users”)
25.                Write-Host $SPGroup.Name, ": adding AD group", $ADGroup.Name -BackgroundColor DarkGray
26.                $SPGroup.AddUser($ADGroup)
27.                Write-Host -ForegroundColor green " Successfully added"
28.                $Web.Dispose()
29.            }
30.        Catch
31.            {
32.                Write-Host -ForegroundColor Red "DOMAIN\Web1-Users: User not added"
33.            }
34.        Finally {}
35.    }

References

Notes

  • These methods were discovered through trial and error and exploring the SharePoint object model (OM) using Get-Member.

Monday, August 10, 2015

SharePoint 2013: how to set an AD group as Site Collection Administrator via PowerShell

To set the Primary and Secondary site collection administrators to an AD group, you need to use PowerShell and you must use the AD group's claims ID, which will be of the form "c:0+.w|SID". For example:
Set-SPSite "[URL]" -OwnerAlias "c:0+.w|s-1-5-21-1254545518-4216542334-3945668986-3788"
After you execute this commandlet, if you then go CA > Application Management > Site Collections > Change site collection administrators, it will display but with the message:
No exact match was found. Click the item(s) that did not resolve for more options.
You can ignore this.

If you want to keep your existing Primary and Secondary site collection administrators, and just want to add an AD group as an additional site collection administrator, just do this:
$User = Get-SPUser -Web "[URL]" -Identity "c:0+.w|s-1-5-21-1254545518-4216542334-3945668986-3788" $User.IsSiteAdmin = $true $User.Update()
In each case, you must use the claims ID of the AD group.
 

References

Notes

  • To view a list of all site collection administrators that are not the primary or secondary, just do this:
    $Web = Get-SPWeb "[URL]" $Web.SiteAdministrators | ft -auto
  • If you see an error like this occurring when executing Get-SPUser:
    Get-SPUser : You must specify a valid user object or user identity...
    it's likely due to the user identity not yet being added to the web application or site. The Get-SPUser commandlet can only get an identity object that already exists within the web application indicated in the -Web parameter of this commandlet. Therefore, to resolve this problem, you first need to add the new user to the web application or site that you want to work with; and you do this using the New-SPUser commandlet.
  • I have found that setting the primary and secondary site collection administrators usually also adds them to the general site collection administrators group and they will appear when I go to Site Collection Administrators in Settings for the site collection. However, this is not always the case. Sometimes, when I set the primary and secondary via powershell, they do not appear in the general site collection administrators group. Why this is I don't know yet.

Tuesday, August 4, 2015

SharePoint 2013: Microsoft.Office.Server.Search.Administration.ISearchServiceApplication], cannot be used for communication because it is in the Faulted state

Problem

A few users report that when they connect to pages containing Content Search web parts, the web part displays: Sorry, something went wrong.  When the VIEW DETAILS link is clicked, these users sees this:
The communication object, System.ServiceModel.Configuration.ConfigurationChannelFactory`1[Microsoft.Office.Server.Search.Administration.ISearchServiceApplication], cannot be used for communication because it is in the Faulted state.
Correlation ID: 00000000-0000-0000-0000-000000000000
Most other users report that when they navigate to such pages, the Content Search web part displays content as expected.  Below is my investigative method to resolve this issue.
  

Troubleshooting

  1. Facts
    1. A single user experiences the issue
    2. Farm employs Windows network load balanced: WFE1 + WFE2, where WFE1 has priority
    3. One Application server
    4. SharePoint 2013 farm is patched through July 2015
    5. All Windows Server 2012 are patched through July 2015
  2. Findings
    1. Verified user experience: user was able to reproduce issue.
    2. Could not reproduce issue from administrator workstation.
    3. Ad hoc selection of other users could also not reproduce user issue.
    4. Windows Server Services: Search services running on both WFE1, WFE2
    5. SharePoint Services on Server: Search services running on both WFE1, WFE2
    6. SharePoint Search Administration: all search components up and running (green checks)
    7. IIS: all sites and application pools started on both WFE1 and WFE2
    8. Able to reproduce issue by logging into WFE2 and then connecting to the page.
    9. ULS Logs: instances of the error message (presented above) was found in the ULS logs for WFE2 only.
  3. Research
    1. Found a posting, Unable to search Document Library, suggesting search component service restart and IIS restart to resolve similar problem.
  4. Testing
    1. Restart the Windows Server services SharePoint Search Host Controller and SharePoint Server Search 15: issue still presents on WFE2.
    2. Restart the SharePoint Server service Search Query and Site Settings Service on WFE2: connecting to page on WFE2 returns HTTP 500.
    3. Perform IISRESET on WFE2: connecting to page, while logged into WFE2, Content Search web part now displays content as expected.

Solution

  1. Identify that server hosting the problematic Search service
  2. In Central Administration > Services on Server, restart Search Query and Site Setting Service on the server.
  3. On that server, perform IISRESET.

References