SharePoint and the case of the broken help

Greetings fellow SharePoint nugget seekers!

Problem

Unfortunately, help seems to be broken… there aren’t any help collections in the current language for the site you’re using.

Unfortunately help seems to be broken... There aren't any help collections in the current language for the site you're using.

Let me guess. You’ve recently gone live with a SharePoint environment and now a user has reported that clicking the help button displays the above message.

Let me ask. Did you by any chance rid your farm databases of those long unwieldy GUIDs in their names? To be specific, I am referring to the content database that hosts Central Administration. Perhaps you followed the steps in one of these articles (or similar ones, there are many out there).

I did. I wanted nice clean database names and yes I followed the steps in these articles. And then I got this error message being reported. It is only a mildly annoying side-effect, but it’s that impression on users when they are actually seeking assistance and find this error message behind the help button that I want to avoid. After all, I followed the guidance in those articles to make the environment cleaner, not make more problems.

Solution

Provide SPDataAccess permissions to the central administration content database for all web application pool identities. There is no need for any iisreset or system outage.

Don’t like this? Have a look at my list of alternative approaches.

Why is that the solution?

The approach of blog posts like the ones I linked to above is to create a brand new content database to host the Central Administration site collection. However the content database that gets created is a generic SharePoint content database while the Central Administration content database is in fact a bit special! There are a few extra bits and pieces that it is given, namely the database security roles WSS_Content_Application_Pools and SharePoint_Shell_Access.

When SharePoint is trying to render the help window, the web application pool identity requires some level of access to the central administration content database. I have experimented with the security roles that are available and found that SPDataAccess provides enough access to resolve the broken help message. You can always grant db_owner to the application pool account, however if you are trying to stay with least privilege security practices this may be as close as you can get without completely rebuilding the environment.

Prove the solution works

I have built a SharePoint 2013 environment and patched it to August 2015 cumulative update (or 15.0.4745.1000 if you were wondering).

The environment has a single web application on port 80 and a single site collection provisioned at the root of the web application.

The farm administrator account, web application pool identity, and site collection owner are three distinct domain accounts.

The SQL instance currently has databases for

  • Central Administration content database (with GUID)
  • Configuration database
  • Web application content database

SharePoint 2013 default databases

When opening help on the provisioned site collection website, the popup dialog displays successfully.

SharePoint 2013 default site collection help working

From this point, I executed some PowerShell similar to the steps described in the articles linked to above. This created the new content database and migrated the Central Administration site collection.

Create a new content database in powershell

Here are the databases in the instance now including the new database created without GUID.

SharePoint 2013 added content database without GUID

At this point, attempting to open the help dialog displays the error message.

SharePoint 2013 default site collection help is now broken

The attempt to open the help dialog box is logged in the web server application event log as ID 3760. SharePoint also logs an error in the ULS described as SQLClient.SQLException. A full stack trace is listed at the end of this blog post.

Now to follow the solution, provide the web application pool identity account access to the newly created content database with security role SPDataAccess.

Attempting to open the help dialog is now successful.

Here is a side-by-side look at two databases, the database created when provisioning the farm with GUID and the manually created database without GUID.

Central admin content database with GUID security roles

Notice the GUID database contains security roles for SharePoint_Shell_Access and WSS_Content_Application_Pools

Central admin content database without GUID security roles

Alternative approaches

psconfig
There is a command to create, connect or disconnect a server from the server farm. This includes a parameter to specify the central administration content database.
i.e. psconfig.exe -cmd configdb … -admincontentdatabase
Unfortunately this does not provide functionality to rename databases on farms that already exist.

build the farm with powershell
The cmdlet New-SPConfigurationDatabase has a parameter to specify the name of the Administration content database. But let’s face it, the reason we are in this situation is because we chose the wizard interface. It’s not really an option in the scope of this article to consider a farm rebuild.

rename the database
Wow did I really put this here as an alternative? In what universe can you rename a database and expect the application to keep working? Sorry there is no option to rename the central administration content database from any SharePoint interface. This is listed here just because I considered it as an option to see if it could solve the problem.

backup database, unmount, restore database renamed
No this is also not supported by SharePoint… sure it might be a possibility for other content databases, but since we are dealing with central administration just forget it.

Other “Help” error messages

You might come across other symptoms of this problem in other places. I’ve listed a few places that I saw the error message generated.

Performing a search

Performing-a-search-try-these-tips-unfortunately-help-seems-to-be-broken

Editing a view

Editing-a-view-learn-about-filtering-items-unfortunately-help-seems-to-be-broken

Appendix

Details of application event log

Event-ID-3760

SQL Database 'SharePoint_AdminContent' on SQL Server instance 'SQL INSTANCE' not found. Additional error information from SQL Server is included below.
Cannot open database "SharePoint_AdminContent" requested by the login. The login failed. Login failed for user 'DOMAIN\WebAppPool'.

Details of ULS log stack trace
08/28/2015 14:47:10.19 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Monitoring nasq Medium Entering monitored scope (Request (GET:http://SERVER:80/_layouts/15/help.aspx?Lcid=1033&Key=WSSEndUser&ShowNav=true)). Parent No
08/28/2015 14:47:10.19 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Logging Correlation Data xmnv Medium Name=Request (GET:http://SERVER:80/_layouts/15/help.aspx?Lcid=1033&Key=WSSEndUser&ShowNav=true) e54c289d-3657-a014-0000-08d685ddb864
08/28/2015 14:47:10.39 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Database 880i High System.Data.SqlClient.SqlException (0x80131904): Cannot open database "SharePoint_AdminContent" requested by the login. The login failed. Login failed for user 'DOMAIN\WebAppPool'. at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) at System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK) at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover) at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout) at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance) at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData) at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection) at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry) at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry) at System.Data.SqlClient.SqlConnection.Open() at Microsoft.SharePoint.Utilities.SqlSession.OpenConnection() ClientConnectionId:df7f709f-7560-443b-95bc-8832ed908466 Error Number:4060,State:1,Class:11 e54c289d-3657-a014-0000-08d685ddb864
08/28/2015 14:47:10.39 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Database 880k High at Microsoft.SharePoint.Utilities.SqlSession.ExecuteReader(SqlCommand command, CommandBehavior behavior, SqlQueryData monitoringData, Boolean retryForDeadLock) at Microsoft.SharePoint.Utilities.SqlSession.ExecuteReader(SqlCommand command, Boolean retryForDeadLock) at Microsoft.SharePoint.Utilities.SqlSession.ExecuteReader(SqlCommand command) at Microsoft.SharePoint.Upgrade.SPDatabaseSequence.GetVersion(SPDatabase database, Guid id, Version defaultVersion, SqlSession session, SPDatabaseSequence sequence) at Microsoft.SharePoint.Upgrade.SPDatabaseSequence.GetVersion(Guid id, Version defaultVersion) at Microsoft.SharePoint.Upgrade.SPDatabaseSequence.get_SchemaVersion() at Microsoft.SharePoint.Upgrade.SPSequence.get_IsBackwardsCompatible() at Microsoft.SharePoint.Upgrade.SPUpgradeSession.IsBackwardsCompatible(Object o, Boolean bRecurse) at Microsoft.SharePoint.Administration.SPPersistedUpgradableObject.get_IsBackwardsCompatible() at Microsoft.SharePoint.Administration.SPPersistedUpgradableObject.ValidateBackwardsCompatibility() at Microsoft.SharePoint.SPSite.PreinitializeServer(SPRequest request) at Microsoft.SharePoint.SPWeb.InitializeSPRequest() at Microsoft.SharePoint.SPWeb.EnsureSPRequest() at Microsoft.SharePoint.SPWeb.InitWebPublic() at Microsoft.SharePoint.Help.HelpContextManager.get_ProductHelpLibraryUrl() at Microsoft.SharePoint.Help.HelpContextManager.GetWebHelpUrl(SPWeb web) at Microsoft.SharePoint.Help.HelpContextManager.get_ContextWebHelpUrl() at Microsoft.SharePoint.ApplicationPages.HelpPage.GetHelpItemAssetKey(SPQueryProvider siteHelpQueryProvider, SPQueryProvider productHelpQueryProvider) at Microsoft.SharePoint.ApplicationPages.HelpPage.b__0() at Microsoft.SharePoint.SPSecurity.<>c__DisplayClass5.b__3() at Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) at Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at Microsoft.SharePoint.ApplicationPages.HelpPage.OnLoad(EventArgs e) at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) at System.Web.UI.Page.ProcessRequest() at System.Web.UI.Page.ProcessRequest(HttpContext context) at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error) at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb) at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus) at System.Web.Hosting.UnsafeIISMethods.MgdIndicateCompletion(IntPtr pHandler, RequestNotificationStatus& notificationStatus) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) e54c289d-3657-a014-0000-08d685ddb864
08/28/2015 14:47:10.39 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Database 880j High SqlError: 'Cannot open database "SharePoint_AdminContent" requested by the login. The login failed.' Source: '.Net SqlClient Data Provider' Number: 4060 State: 1 Class: 11 Procedure: '' LineNumber: 65536 Server: 'SQL INSTANCE' e54c289d-3657-a014-0000-08d685ddb864
08/28/2015 14:47:10.39 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Database 880j High SqlError: 'Login failed for user 'DOMAIN\WebAppPool'.' Source: '.Net SqlClient Data Provider' Number: 18456 State: 1 Class: 14 Procedure: '' LineNumber: 65536 Server: 'SQL INSTANCE' e54c289d-3657-a014-0000-08d685ddb864
08/28/2015 14:47:10.39 w3wp.exe (0x1594) 0x2978 SharePoint Foundation Database 3760 Critical SQL Database 'SharePoint_AdminContent' on SQL Server instance 'SQL INSTANCE' not found. Additional error information from SQL Server is included below. Cannot open database "SharePoint_AdminContent" requested by the login. The login failed. Login failed for user 'DOMAIN\WebAppPool'. e54c289d-3657-a014-0000-08d685ddb864