How to add new properties to the workflow service configuration

February 5, 2015

While investigating an issue I encountered when running a SharePoint 2013 workflow, see blog post for details, some of the suggested fixes I found suggested adding a new property into the workflow configuration settings.

I had a look at the MSDN article on how to do this but when looking at the steps it seems to indicate you could only set existing values not add new ones, see figure 1 for a snippet from the MSDN article

 “For Name, specify a name of the configuration setting (See below for the list of valid settings.)”.

Figure 1

I had a look around on how to add new settings but I wasn’t able to find anything except from people adding rows directly into the “WorkflowServiceConfig” table in the “WFResourceManagementDB” database. Anyone who has used SharePoint knows that it is generally not supported to touch any SharePoint database without putting them into an unsupported state.

As it turns out I was able to resolve my issue by updating an existing property so I didn’t need to do this in the end. After I had worked out what property I needed to update, which I had done via the PowerShell command supplied in the MSDN article, I thought it would be a good idea to create a wrapper PowerShell function in a common PowerShell file I have. I have several helper methods in this common PowerShell file and I use it to avoid having to remember specific PowerShell commands.

I started to implement this helper function and was adding some error handling to see what would happen when a property name was supplied that didn’t exist. I ran the helper method with an invalid name and it completed without any errors. I was expecting some kind of error message so I was slightly confused at this. I changed the name and debugged through the PowerShell, I use PowerGUI when writing scripts and it has this feature. When the script got to call the “Set-WFServiceConfiguration” function it completed with no errors.

I opened up the workflow table where the settings are stored and to my surprise the two test values I had tried were in the table.

I thought it was a bit strange that Microsoft would allow you to update properties but not add them, however the wording on the MSDN article does clearly suggest the name has to be one of the existing values. Added to this all the articles I found people were directly adding rows into the database table.

I have included the function I wrote below, see figure 2, in case anyone finds it useful but as always with PowerShell always try this in an non-production environment first.

Function to add property
function Set-SP2013WorkflowSetting{
   [cmdletbinding()]
   Param(    
    [parameter(Mandatory=$true)][string]$workflowManagerURL,
    [parameter(Mandatory=$true)][string]$propertyName,    
    [parameter(Mandatory=$true)][string]$propertyValue)    
    
    Write-Host "Starting to update workflow manager property $propertyName" -ForegroundColor Green
    
    $existingValue = Get-WFServiceConfiguration -serviceUri $workflowManagerURL -Name $propertyName
    
    if($existingValue -eq $null)
    {
        Write-Host "Property $propertyName doesn't have a value which means it isn't in the settings DB table. Please confirm you want to add a new setting" -ForegroundColor Magenta
        $title = "Add Workflow Setting"
        $message = "Do you want to add a new workflow setting?"

        $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
           "Add a new row into the settings table."

        $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
           "Doesn't add the new setting and exists the function."

        $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

        $result = $host.ui.PromptForChoice($title, $message, $options, 1)

        switch ($result)
        {
            0 {}
            1 {return}
        }
    }
    else
    {
        Write-Host "Poperty $propertyName has a value of $existingValue it will be changed to $propertyValue" -ForegroundColor Green
    }
    
    Set-WFServiceConfiguration -serviceUri $workflowManagerURL -Name $propertyName -Value $propertyValue
    
    Write-Host "Finished updating workflow manager property $propertyName" -ForegroundColor Green    
}

Figure 2
Advertisements

SharePoint 2013 Workflow Instance Size Error

January 6, 2015

While working on a recent project I got the chance to setup and use SharePoint 2013 workflows. This is a major step forward for the product and has a lot of benefits, however it does introduce a few complications.

As the workflows are now all stored and processed outside of SharePoint all communication is done via WCF services. This is great as it extracts the processing out of SharePoint and thus reduces the load on SharePoint.

The downside of this is it introduces additional areas where something can go wrong and this is what leads me onto the topic of this blog post.

The workflow I was working on was used to handle the approval of data on a SharePoint list item. In order for the workflow to move onto the next stage it required 5 groups of users to approve the data. The workflow had to wait until all 5 groups of users had completed and approved the data and once approved the workflow would move onto the next stage and send some emails.

The UI to handle the approval locked down the form so only users in certain groups could actually approve each section so in most cases a user would come in and approve 1 out of the 5 sections and this worked fine. There was also an admin group who could approve all 5 sections at one time. It was in this case where an error was being thrown by the workflow causing it to be terminated, see figure 1

image

Figure 1

When clicking on the workflow I could see the internal status was ‘Terminated’ and when clicking on the information icon I could see the error was “System.Activities.Statements.WorkflowTerminatedException: The workflow instance was too large to persist (over 5120 kilobytes). The maximum persistable workflow instance size is 5120 kilobytes.”, see figure 2.

image

Figure 2

For SharePoint errors this is actually reasonably detailed as it supplies a message explaining what the issue is instead of the usual something went wrong error. As this was my first real experience of SharePoint 2013 workflows I didn’t really know where to start debugging this so I had a Google around.

There are a few articles out there but nothing which matched what I was looking for. The closest I came was an article which suggested changing a server configuration property called ‘WorkflowServiceMaxActivityXamlSizeInBytes’.

I had a look into this and discovered this property can be updated by using a PowerShell command called ‘Set-WFServiceConfiguration’, see MSDN article. I tried to increase this value but unfortunately it didn’t seem to make any difference. I looked over the additional properties which can be set and I found one called ‘WorkflowServiceMaxInstanceSizeKB’ which seemed to match the error I was getting in terms of the description of the error but also the max value limit on the error message seemed to match the default value on the MSDN article. I tried changing this using the PowerShell command but again the workflow was still failing with the same error.

Given these properties were exposed so Microsoft obviously expected these might need to be changed in certain circumstances and the fact that the ‘WorkflowServiceMaxInstanceSizeKB’ property matched so closely to the error I was encountering I done some investigation into this and found there was another property called ‘WorkflowServiceMaxInstanceCompressedSizeKB’ which also had the same default value of 5120.

Unfortunately all articles referring to this were people adding a row directly into a table in one of the workflow databases. Anyone who knows SharePoint knows touching any SharePoint databases is not supported so I was reluctant to do this, however since it was a development environment I tried it but I still encountered the same error.

It wasn’t until I came across an article which was describing a slightly different error that on a I noticed down at the bottom of the article it said you have to restart the ‘Workflow Manager Backend’ windows service in order for settings changes to be picked up and after I done this the workflow started working.

I then had to do some testing to check which of the properties I had changed was the one which fixed the error and in my case it turns out I only needed the ‘WorkflowServiceMaxInstanceSizeKB’ property which could be set via PowerShell, see figure 3 for the exact script, so I didn’t have to worry about adding a value directly into the workflow database.

Set-WFServiceConfiguration -ServiceUri http://workflowmanagerurl:12291/ -Name “WorkflowServiceMaxInstanceSizeKB” -Value 30720

Figure 3

Overall the process of changing the workflow settings is very easy using the available PowerShell functions the key is to remember and re-start the ‘Workflow Manager Backend’ windows service to get these updated values picked up.


Error editing publishing page in SharePoint

November 25, 2013

While working on a recent project I was navigating our development site and when I tried to edit a page I got the standard SharePoint error screen, see figure 1. There was very little customisations on the site as it was still in the earlier stages of the development process so I was slightly confused as to what the issue could be.

image

Figure 1

As with most SharePoint errors the easiest way to get to the bottom of the issue is to check the ULS logs so I copied the correlation ID and opened up ULS Viewer on the server. I opened the latest ULS log file and filtered by the correlation ID. Looking through the log file I finally found the details of the error, see Figure 2.

Application error when access /Pages/default.aspx, Error=Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
at Microsoft.SharePoint.SPFieldMultiColumnValue.get_Item(Int32 index)

System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
at Microsoft.SharePoint.SPFieldMultiColumnValue.get_Item(Int32 index)

Getting Error Message for Exception System.Web.HttpUnhandledException (0x80004005): Exception of type ‘System.Web.HttpUnhandledException’ was thrown. —> System.InvalidOperationException: Failed to compare two elements in the array. —> System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index
at Microsoft.SharePoint.SPFieldMultiColumnValue.get_Item(Int32 index)

Figure 2

Looking over the details it wasn’t obvious what the actual error was but it seemed to point to an issue with the page layouts so I decided to review all custom ones to see if there was anything obviously wrong. I opened the site in the browser and navigated to the master page gallery. When I tried to edit one of the custom page layouts I got an error, see figure 3.

image

Figure 3

The error seemed to be highlighting an issue with the content type the page layout was associated with so I returned to the master page gallery and hovered over the associated content type link, see RHS column on Figure 4, and I could see the URL on the bottom of the page was “_layouts/15/ManageContentType.aspx?ctype=#VALUE!” whereas it should contain the ID of the content type.

 

screenshot2

Figure 4

Normally this would not be an issue with page layouts uploaded via the browser or SharePoint designer, however in my case the custom page layouts had been uploaded into SharePoint via PowerShell.

I checked the PowerShell script and I could see the original version uploaded a page layout into the correct location, however it was setting the associated content type property of the page layout to be a string, see figure 5. I knew from previous experience this needed to be a concatenated string but I couldn’t remember the exact format so I quickly put together a test script which got the value of an OOTB page layout. Using this script I could see the value actually had to be a concatenated string of the content type name and ID, see figure 6 for updated PowerShell.

Incorrect version
  1. $newFile.Item["PublishingAssociatedContentType"] = "Article Page"

Figure 5
Correct version
  1. $newFile.Item["PublishingAssociatedContentType"] = ";#Article Page;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D;#"

Figure 6

As the site was still in the early stages of development I was able to easily delete all pages which used the custom page layouts, delete the page layouts and re-upload them with the corrected script. I then double checked the associated content link in the master page gallery, see figure 7, and it was correctly populated. After that I was able to edit pages and content within the site.

screenshot 
Figure 7

Hopefully this saves some other people some time and I suppose the key lesson would be, as always, be careful with PowerShell and double check its actually doing what you expect.


403 when accessing ExplainRank.aspx in SharePoint

October 8, 2013

One of the most recent projects I have been working on is a SharePoint 2013 public facing web site. The site has been live for a while but the client recently reported the search results were not returning in the order they expected.

I checked the search results page and it was sorting by rank so I decided I should invest some time looking at how the SharePoint ranking model works and what I can do to impact this. While there is a lot of information out there I found it difficult to grasp how all these factors were combining in my environment, especially when things like number of page clicks can influence the ranking.

After some further research I found a link by Waldek Mastykarz Understanding item ranking in SharePoint 2013 Search. This is an excellent article which documents this very useful feature, however when I tried to access this on my environment I got an error, see figure 1.

image

Figure 1

As with all SharePoint problems I jumped on the server and started up ULS Viewer and hit the page again. I have a quick look through the logs and the item displayed in figure 2

image

Figure 2

I had a check on the site and I was a site collection admin so next I opened central admin and navigated to the service application list. Once there I selected my search service application by clicking next to the name, see figure 3.

image

Figure 3

Once selected I then clicked on administrators in the ribbon, see figure 4

image

Figure 4

In the popup I added my account as an admin and when I refreshed my explainrank.aspx page it now worked. While its not difficult to work out what the error is on the page I thought I would quickly blog this for others and myself, oh and if anyone from Microsoft reads this you could use a better error message on the page 🙂


SharePoint 2013 Search Web Part Missing Results

April 26, 2013

While working on a few SharePoint 2013 projects I have been utilising the SharePoint search in a lot of places to provide dynamic content and there has been quite a few occassions when things don’t seem to work as expected such as CBS Server Side Date Error or Results web part ordering. I suppose this is to be expected when the search engine has been completely replaced with a mix of the old SharePoint search engine and FAST search.

When working on one aspect of a recent project I was using the results script web part to query some events stored in a list. As per you would expect these events had the typical details such as title, date, description, etc. At first everything seemed to be working as expected until the client reported there was a bunch of events missing from the search generated list. My first thought was these were new events and weren’t in the search index so I kicked off a full crawl. Once this was completed I checked the site and the events were still missing.

Next I looked at my query to see if there was something wrong with it. For example I was filtering out all past events and restricting the search results to the events content type but everything seemed ok.

I spent some time looking around and eventually I noticed that the missing items were very similar to other items which were getting displayed in the search list. There would be 4/5 events in the list with the same title and description but with different dates or locations. I had a look at the web part properties and I noticed there was an option to show a link to display duplicate results, see figure 1.

WebPartProperties

Figure 1

I selected this option but it never done anything. From previous experience with some web parts I knew there are sometimes web part properties which are not exposed via the UI which can still be set if you export the web part, a good example of this is to get the Content By Search web part to run server side. I exported the web part and opened this with notepad and searched for “Duplicate”. Wouldn’t you know it there was a boolean flag in the DataProviderJSON property called TrimDuplicates and this is defaulted to true, see snippet below in figure 2.

<property name="DataProviderJSON" type="string">{"QueryGroupName":"Default","QueryPropertiesTemplateUrl":"sitesearch://webroot","IgnoreQueryPropertiesTemplateUrl":false,"SourceID":"8413cd39-2156-4e00-b54d-11efd9abdb89","SourceName":"Local SharePoint Results","SourceLevel":"Ssa","CollapseSpecification":"","QueryTemplate":"ContentTypeId:0x0100440D27D8CDA949078CD3C29F3F3D7012*{searchboxquery}","FallbackSort":null,"FallbackSortJson":"null","RankRules":null,"RankRulesJson":"null","AsynchronousResultRetrieval":false,"SendContentBeforeQuery":true,"BatchClientQuery":true,"FallbackLanguage":-1,"FallbackRankingModelID":"","EnableStemming":true,"EnablePhonetic":false,"EnableNicknames":false,"EnableInterleaving":true,"EnableQueryRules":true,"EnableOrderingHitHighlightedProperty":false,"HitHighlightedMultivaluePropertyLimit":-1,"IgnoreContextualScope":false,"ScopeResultsToCurrentSite":false,"TrimDuplicates":true,"Properties":{},"PropertiesJson":"{}","ClientType":"","UpdateAjaxNavigate":true,"SummaryLength":180,"DesiredSnippetLength":90,"PersonalizedQuery":false,"FallbackRefinementFilters":null,"IgnoreStaleServerQuery":false,"RenderTemplateId":"DefaultDataProvider","AlternateErrorMessage":null,"Title":""}</property>

Figure 2 – Please note this has the QueryTemplate element remove so the exact query being executed isn’t visible to others 🙂

Most of the snippet above is default settings which you can ignore the import one is TrimDuplicates. In my case as soon as I set the this to false, saved the web part, imported it into the web part gallery and added it to a page all my results started to display correctly.

The key element here is how SharePoint determines if items are duplicates and in my case I suppose it’s fair enough as most of the key information such as the title and description were pretty much exactly the same.

This is another classic example of SharePoint and how its working as expected but because there are 101 configuration options you are not getting the results expected. It’s important to fully understand the product and all the options and what it can offer. This is one I will not forget and hopefully it provides a prompt for others in a similar situation.


Export User Profile Information Using PowerShell

April 5, 2013

While working on my most recent project I have spent a lot of time using the user profile service and while it offers some real benefits I have found it can be difficult to troubleshoot, especially when it comes to synchronizing with external sources like active directory (AD).

After configuring the user profile service on the client’s live environment I noticed that the email and manager fields were missing for some users. I wanted to try and get an idea of the extent of the issue so I went to the manage user profiles screen in central admin (CA) and while its useful for finding individual users its doesn’t really help view certain properties for all users.

Thinking about the options available I knew from previous experience I could write some C# code to get this information but I have been making an effort to brush up on my PowerShell (PS) skills so I decided this would be a perfect opportunity.

As with all PS scripts its essential these are first tested in a development environment before run on live. I began by creating my PS script on my development environment using PowerGUI as I think this is an excellent tool which makes creating PS scripts much easier.

Before starting I worked out what the rough steps would be to get the user profile information and i came up with the following:

  1. Get the site the user profile service is associated with
  2. Get the user profile service
  3. Get all user profiles
  4. Loop through all user profiles outputting the required information
  5. Collate steps and parameterise into a function

I then started working on each step and finally put it all together. Each of the steps is detailed below.

Step 1

From previous experience I knew the easiest way to get the user profile service was to get the service context for the site the user profile service is associated with. The first line gets the site passing a variable holding the site URL, next I check we have the site. I then got the service context associated with the site by calling the Get-SPServiceContext method passing the site

  1. $site = Get-SPSite $SiteURL -ErrorAction SilentlyContinue
  2.     if($site -eq $null)
  3.     {
  4.         Write-Host "Unable to access site " $SiteURL " please ensure the URL is correct and you have access to this"
  5.         return
  6.     }
  7.     
  8.     $serviceContext = Get-SPServiceContext($site) -ErrorAction SilentlyContinue        
  9.     if($serviceContext -eq $null)
  10.     {
  11.         Write-Host "Unable to get the service context for site " $SiteURL
  12.         return
  13.     }

 

Step 2

Once I had the service context the next step is to get the user profile manager which is done by creating a new UserProfileManager instance passing in the associated service context.

  1. $userProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext)    
  2.     if($userProfileManager -eq $null)
  3.     {
  4.         Write-Host "User Profile Manager does NOT exist"
  5.         return
  6.     }

Step 3

With the instance of the user profile manager I then got all users by calling the GetEnumerator method.

  1. $users = $userProfileManager.GetEnumerator()

Step 4

In my situation there were hundreds of users so I decided outputting the information to the PS console would be difficult to read of I decided to output the details to a text file instead.

I looped though each user profile outputting the users preferred name. In addition so I could get additional user profile property values I looped through an array containing the names of user profile properties I wanted to get and output a string containing the property name and the property value for the current user profile

  1. foreach ($user in $users)
  2.     {        
  3.         $userName = $user.Item("PreferredName")
  4.         Write-Output "User Profile $userName" | Out-File $FileLocation -Append
  5.         if($UserProperties.Count -gt 0)
  6.         {        
  7.             for ($i = 0; $i -lt $UserProperties.Count;$i++)
  8.             {
  9.                 $UserProperty = $UserProperties[$i]                
  10.                 if($UserProperty -ne "" -and $UserProperty -ne $null)
  11.                 {
  12.                     try
  13.                     {
  14.                         $userPropertyValue = $user.Item($UserProperty)
  15.                         Write-Output "User Profile Property $UserProperty value is $userPropertyValue" | Out-File $FileLocation -Append
  16.                     }
  17.                     catch
  18.                     {
  19.                         Write-Host "Property $UserProperty doesn't exist"
  20.                     }    
  21.                 }
  22.             }
  23.         }
  24.     }

Step 5

Putting all the above steps together adding additional logging, variable checks and ensuring all settings can be passed in I ended up with the function below.

  1. function GetAllUserProfiles([string]$SiteURL, [string]$FileLocation, [array]$UserProperties)
  2. {
  3.     if($siteURL -eq $null -or $FileLocation -eq $null)
  4.     {
  5.         Write-Host "Please ensure all parameters are supplied"
  6.         return
  7.     }        
  8.     
  9.     Write-Host "Starting to get all user profiles"
  10.     
  11.     if($SiteURL -eq $null)
  12.     {
  13.         Write-Host "Please supply the site URL the User Profile service application is assocaited with"
  14.         return
  15.     }
  16.     
  17.     $site = Get-SPSite $SiteURL -ErrorAction SilentlyContinue
  18.     if($site -eq $null)
  19.     {
  20.         Write-Host "Unable to access site " $SiteURL " please ensure the URL is correct and you have access to this"
  21.         return
  22.     }
  23.     
  24.     Write-Host "Got site " $SiteURL
  25.     
  26.     $serviceContext = Get-SPServiceContext($site) -ErrorAction SilentlyContinue
  27.         
  28.     if($serviceContext -eq $null)
  29.     {
  30.         Write-Host "Unable to get the service context for site " $SiteURL
  31.         return
  32.     }
  33.     
  34.     $userProfileManager = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext)
  35.     
  36.     if($userProfileManager -eq $null)
  37.     {
  38.         Write-Host "User Profile Manager does NOT exist"
  39.         return
  40.     }
  41.     
  42.     Write-Host "Got User Profile Manager "
  43.     
  44.     Write-Host "There are " $userProfileManager.Count " user profiles starting to loop through them. The results will be output to specified text file"
  45.     
  46.     $users = $userProfileManager.GetEnumerator()
  47.     foreach ($user in $users)
  48.     {        
  49.         $userName = $user.Item("PreferredName")
  50.         Write-Output "User Profile $userName" | Out-File $FileLocation -Append
  51.         if($UserProperties.Count -gt 0)
  52.         {        
  53.             for ($i = 0; $i -lt $UserProperties.Count;$i++)
  54.             {
  55.                 $UserProperty = $UserProperties[$i]                
  56.                 if($UserProperty -ne "" -and $UserProperty -ne $null)
  57.                 {
  58.                     try
  59.                     {
  60.                         $userPropertyValue = $user.Item($UserProperty)
  61.                         Write-Output "User Profile Property $UserProperty value is $userPropertyValue" | Out-File $FileLocation -Append
  62.                     }
  63.                     catch
  64.                     {
  65.                         Write-Host "Property $UserProperty doesn't exist"
  66.                     }    
  67.                 }
  68.             }
  69.         }
  70.     }
  71.     
  72.     Write-Host "Function completed"
  73. }

Conclusion

I have already used this function several times without any issues and I have found it a very easy and flexible way to pull information from the user profile store.

I hope this is helpful for others too.


Tagging Service is not available when using hashtags in Microblog entry

February 8, 2013

One of the most interesting new features in SharePoint 2013 is the changes to My Sites and the improvements to the social features such as the newsfeed, communities, hashtags, etc. In order to get up to speed I have spent some time looking at what’s new and how we can use this to help our clients.

As part of this a test environment was configured as a playground but when I came to test the hashtag functionality, see figure 1, it gave and an error saying the tagging service was unavailable, see figure 2.

Microblogging

Figure 1

TaggingServiceError

Figure 2

I checked all the services and everything seemed fine, so I checked all the event logs and ULS logs. I came across one error in the ULS logs, see below.

ULS Log Error

SPMicrofeedStore.UpdateFromListItem: Expected field ‘HashTags’ not found in list item, verify fieldList paramter

From my research into the new social features I knew there was a MicroFeed list which stored some of the content so my initial impression was the HashTags column may have been missing from this list, however when I checked the list I found the column was there.

After some further research I found an article which explained that the hashtags and keywords were partially stored in the Managed Metadata Service so I decided to focus my attention on this.

I had already checked the service had been created and it was associated with the web application. In addition I knew I could access it from Central Admin so I checked accessing the term store from within the site collection. While I was able to access the term store I could see the hashtags and keywords term sets were empty, see figure 3.

TaggingEmptyTermStore

Figure 3

From previous experience with using the term store to generate the navigation in another project I knew that even if the Managed Metadata Service is associated with the web application and term store is accessible from within the site collection it still needs to be set as the default for the web application. I checked this by logging into Central Administration going to manage service applications and locating my Managed Metadata Service in the list. To set the service as the default you need to click on the proxy service, see figure 4, and click properties in the ribbon. This will launch a popup and the top two check boxes need to be selected, see figure 5.

TaggingService

Figure 4

TaggingServiceSettings

Figure 5

As soon as I changed this the hashtag functionality started to work, see figure 6. One thing to note is you have to be careful when setting the Managed Metadata Service to be the default as if you have multiple Managed Metadata Services associated with the one web application it can cause some issues.

MicrobloggingWorking

Figure 6

Hopefully this will help out any other people who encounter the same issue. Happy SharePointing


%d bloggers like this: