Issue with setting managed metadata fields in a SharePoint workflow


November 9, 2018 - 11:41, by Steven Van de Craen - 0 Comments

Scope

One of my on-prem customers is running Nintex Workflow on SharePoint 2016 and escalated an issue with setting a Taxonomy/Managed Metadata field value via a workflow. They experienced that the workflow would not change the value, or in some case it would but only once.

I managed to scope the issue down to the following:

  1. It works in SharePoint 2010 but doesn’t in SharePoint 2013, 2016, 2019 (had to spin up a VM of each for proper testing)
  2. It only affects Workflow Foundation/SharePoint 2010 Workflow Engine workflows (SharePoint Designer can create them, Nintex Workflow on-prem is based on this)
  3. For files that haven’t been given a value for the managed metadata field the workflow can set the value once. If the field has been set or cleared previously by UI, code, workflow, it will no longer accept new values via the workflow. For Office documents this may not work since background property promotion also causes the issue

So that makes it a pretty narrow scope and explains why there aren’t that many reports on this. But since not everyone is moving to the cloud at the same speed this is still be a relevant issue for some.

Reproduce

I’m going with SharePoint 2016, but I managed to reproduce this in 2013 and 2019 as well.

Create a document library and create a Managed Metadata field with some values and upload a document

Create a new workflow using the “SharePoint 2010 Workflow Platform” (SharePoint Designer or Nintex Workflow)

Create workflow 

Configure the workflow to update the Managed Metadata field. This field expects the format TERMLABEL|TERMGUID (I’m using a hardcoded value)

Workflow step

Publish the workflow and run it on new files. For non-Office documents this should work once.

Result #1

Next, change or clear the value through UI or whatever and run the same workflow again. You should see that even though the workflow has run it could change the value.

Workaround or fix?

I’ve been digging into the internals and it seems that once the managed metadata field is set or cleared, it keeps properties in the SPFile.Properties property bag that interfere with the update process. If you delete these properties and update the SPFile the workflow can update the value (again only once since the properties are added again).

Removing the properties requires an SPFile.Update() which in term creates a new version and is less than ideal.

Escalating this to PSS and hoping for a fix would probably be the right way, but since the narrow scope of the issue and since it is in older technology I have low hopes on this getting fixed soon.

In my case I wrote a Web Service that would allow for updating the Managed Metadata field value through the SharePoint Object Model. And this Web Method can be called in the workflow. It even allows for the workflow designer to specify the type of update (Update, UpdateOverwriteVersion or SystemUpdate). So although not very intuitive the workflow designer can now update Managed Metadata fields through the Web Method, and all other fields through the regular workflow action.

Web service input


Calling web services in Nintex Workflow and different authentication mechanisms


September 12, 2014 - 20:40, by Steven Van de Craen - 9 Comments

With the rise of claims based authentication in SharePoint we’ve faced new challenges in how to interact with web services hosted on those environments. Claims based authentication allows many different scenario’s with a mixture of Windows, Forms and SAML Authentication.

image

When you’re working with Nintex Workflow you’re faced with authentication in Actions such as “Call Web Service” or “Web Request”.

If you’re just using Windows Authentication (NTLM, Kerberos, Basic) on your site then Nintex will handle that authentication just fine for you and use the credentials you specified (manually entered or stored credentials).

imageimage

However you might have to deal with different or multiple authentication mechanisms such as Forms Based Authentication, ADFS or a combination. In such cases you’ll get a 403 FORBIDDEN regardless of the credentials you enter.

image

Overcoming this hurdle can be challenging.

  1. Use a different URL zone (with windows authentication) to make the call
  2. Pass an authentication cookie along with the request

Use a different URL zone (with windows authentication) to make the call

Nintex Actions execute on the server, not on your -already authenticated- client. The connection information you’ve entered (URL, username, password) is used to construct a connection and execute the operation. Since the Action executes locally on the server it can make use of a different URL to do the call. It is a best practice/requirement to have the Default Zone of your Web Application configured with -just- Windows Authentication in order to get things like Search to work properly. Why not make use of this and use that URL in your Actions?

imageimage

Define a set of credentials that can be used in “Call web service” or “Web Request” Actions and have it execute against the URL that has Windows Authentication. If this option is available to you it probably is the preferred way of working.

Pass an authentication cookie along with the request

If the above is no option for you things get trickier and “specific”, meaning it is specific to a certain scenario but might not be possible for yours.

In MY case I have a SharePoint 2013 on-prem environment with “mixed” authentication (Windows and Forms Based). SharePoint issues a FedAuth cookie when the user successfully authenticates. If you send this cookie along with the web request it will work just fine. Note that the “Call web service” action does NOT allow you to specify additional headers so the “Web Request” Action becomes your new best friend here.

Using the “Web Request” Actions allows for much more flexibility, but you’ll have to build the request message yourself. I our case that means the SOAP message.

image

Once you have all of that in place the “Web Request” will happily call out to the web service. See it here working with the FedAuth cookie I “borrowed”.

imageimage

Getting the FedAuth cookie

The base premise is that you need to ‘replay’ the authentication mechanism in code to get the FedAuth cookie. Once you have this you can send it along with future requests from Nintex Workflow. Again this is really specific to my case and may not be possible for you because of additional security or complex authentication schemes.

For my SharePoint 2013 on-prem environment with “mixed” authentication (Windows and Forms Based) I force the call to do Windows Authentication:

public static class AuthHelper
{
    public static Cookie GetFedAuthCookie(Uri uri, ICredentials credentials)
    {
        Cookie result = null;

        // Emulate the authentication via a request to the /_windows/default.aspx page using the provided credentials
        HttpWebRequest request = WebRequest.Create(uri.GetLeftPart(UriPartial.Authority) + "/_windows/default.aspx?ReturnUrl=%2f_layouts%2fAuthenticate.aspx%3fSource%3d%252FDefault%252Easpx&Source=%2FDefault.aspx") as HttpWebRequest;
        request.Credentials = credentials ?? CredentialCache.DefaultNetworkCredentials;
        request.Method = "GET";
        request.CookieContainer = new CookieContainer();
        request.AllowAutoRedirect = false;

        // Execute the HTTP request 
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;
        if (null != response)
        {
            result = response.Cookies["FedAuth"];
        }

        return result;
    }
}

I actually made this available as a Web Service so that it can be called from with a Nintex Workflow.

public class AuthService : IAuthService
{
    public string GetFedAuthCookie(string requestUrl, string userName, string password)
    {
        string result = null;

        try
        {
            NetworkCredential credential = !String.IsNullOrEmpty(userName) ? new NetworkCredential(userName, password) : null;
            Cookie cookie = AuthHelper.GetFedAuthCookie(new Uri(requestUrl), credential);

            if (cookie != null)
            {
                result = cookie.Value;
            }
        }
        catch (Exception ex)
        {
            result = null;
        }

        return result;
    }
 }

And now I can call my Authentication service prior to the other services.

image

Door #3

It feels like it must be possible to use access tokens that can be passed along similar to the FedAuth cookie. Considering this is how the App model works in SharePoint 2013, there has to be a way to leverage this for what we’re trying to accomplish. But that’s for another post.


SharePoint 2013: Workflows failing on start


April 29, 2014 - 12:10, by Steven Van de Craen - 3 Comments

Recently I helped out a colleague with an issue in a load balanced SharePoint 2013 environment with Nintex Workflow 2013 on it. All the workflows that were started on WFE1 worked fine, but all started on WFE2 failed on start with the following issue logged to the SharePoint ULS logs:

Load Workflow Class: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.

at System.Diagnostics.PerformanceCounter.InitializeImpl()

at System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, String instanceName, Boolean readOnly)

at System.Workflow.Runtime.PerformanceCounterManager.CreateCounters(String name)

at System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService.OnStarted()

at System.EventHandler`1.Invoke(Object sender, TEventArgs e)

at System.Workflow.Runtime.WorkflowRuntime.StartRuntime()

at Microsoft.SharePoint.Workflow.SPWinOeHostServices..ctor(SPSite site, SPWeb web, SPWorkflowManager manager, SPWorkflowEngine engine) -

-- End of inner exception stack trace ---

at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)

at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)

at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)

at System.Reflection.Assembly.CreateInstance(String typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)

at Microsoft.SharePoint.Workflow.SPWorkflowManager.LoadPluggableClass(String classname, String assembly, Object[] parameters)

If you look up this issue you’ll find a lot of references to SharePoint 2007, but it seems that this is still possible in a SharePoint 2013 / Windows Server 2012 environment as well.

Cause

An incorrect registration of the Windows Workflow Foundation performance counters will cause this. You can easily verify this by opening up perfmon, adding a counter and looking for the category “Windows Workflow Foundation”.

perfmon - Add Counters

Solution

Register the performance counters on each affected server:

lodctr C:\Windows\Inf\Windows Workflow Foundation 3.0.0.0\perfcounters.ini

That restores the counters and will make your workflows start again!


Nintex Workflow and emailing to groups


February 19, 2014 - 15:08, by Steven Van de Craen - 2 Comments

Nintex Workflow is able to send emails via the Send notification action.

Nintex Workflow - Send notification

A question often asked is if it can send emails to SharePoint groups or Active Directory groups. The answer is; Yes it can!

There are some things you need to know though…

Send to an Active Directory group

You can use AD security groups, but they must have a (dummy) email value configured or the workflow will fail on this.

The email will only be sent to the direct users in the AD group. Nested AD groups will be ignored.

Send to a SharePoint group

This works about the same as AD groups. The email will be sent to direct users in the SharePoint group, and to direct users of any nested AD group in the SharePoint group. It won’t go into nested AD groups of the AD group though (same as above).

Also, you cannot nest SharePoint groups.

 

HTH


Follow Up - Programmatically approving a workflow


June 28, 2011 - 09:10, by Steven Van de Craen - 8 Comments

In my previous post “SharePoint 2010: Programmatically Approving/Rejecting Workflow” I mentioned the possibility of an issue when programmatically altering a workflow task. I have been testing it on several SharePoint 2010 farms with different parameters (different patch level, etc).

UPDATED July 2, 2011: RESOLVED

Set up

A simple SharePoint Designer 2010 workflow with the “Start Approval Process” action published to a document library. You start the workflow and a task is created for the approver.

The Approver is set to another test user, but all my code and script runs as the administrator (and thus System Account). Makes no difference though.

Note: when running the workflow multiple times (for testing), make sure to delete all tasks that are linked to your item. Or adapt the code below to not just take the first task in the collection…

Issue experienced

You alter the workflow task using a Visual Studio Console Application or Windows Application with the following (or similar code):

1 string url = "http://intranet/Shared Documents/Instructions.txt"; 2 using (SPSite site = new SPSite(url)) 3 { 4 SPWeb web = site.RootWeb; 5 SPListItem item = web.GetFile(url).Item; 6 SPWorkflowTask task = item.Tasks[0]; 7 8 Hashtable ht = new Hashtable(); 9 ht["TaskStatus"] = "Approved"; 10 11 SPWorkflowTask.AlterTask(task, ht, false); 12 }

After the code has run successfully you don’t see anything happen in the workflow status page. On some occasions you get “Due to heavy load, the latest workflow operation has been queued. It will attempt to resume at a later time” which could mean that the timer job hasn’t completed successfully yet. This message may or may not disappear for you.

Also, when trying to do a second alteration to the workflow task (by code or in the UI) you get “This task is currently locked by a running workflow and cannot be edited”.

So somehow your action was registered but not executed…

PowerShell to the rescue

Terminate the workflow (because it really hangs and there’s not much you can do with it) and kick off a new one.

Now open the SharePoint 2010 Management Shell (PowerShell) and run the following (or similar code):

$s = Get-SPSite("http://intranet")
$w = $s.RootWeb
$i = $w.GetFile("
http://intranet/Shared Documents/Instructions.txt").Item
$t = $i.Tasks[0]
$ht = new-object Hashtable
$ht["TaskStatus"] = "Approved"
[Microsoft.SharePoint.Workflow.SPWorkflowTask]::AlterTask($t, $ht, $false)

This WILL work and correctly finish the approval cycle in any of my tested environments.

Notes

You can really test this over and over and over; it will work in PowerShell but not in the Visual Studio 2010 application. I have tested Release versus Debug, in and out Visual Studio, etc.

Solution (July 2, 2011)

The big difference in both techniques is that the PowerShell remains open and isn’t disposing any objects. This allowed the workflow engine to complete the task.

Option #1
1 SPWorkflowTask.AlterTask(task, ht, true);

From MSDN:

“If true is passed as the argument to fSynchronous, this method waits up to 30 seconds to determine whether the attempted update was accepted by the workflow schedule as valid. The method then returns true if the update was accepted and not rolled back, or false if the update was not accepted. If false is passed as the argument to fSynchronous, this method always returns true.”

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.workflow.spworkflowtask.altertask.aspx

 

I have found this working most of the times, however some test runs still experienced the issue. Perhaps my test environment isn’t powerful enough or the action takes just a little too long to kick in ?

Option #2

Using the code below I learned it took about 21 seconds for the task to complete:

1 int i = 1; 2 while (!(bool)item.Tasks[taskId]["Completed"]) 3 { 4 Console.Clear(); 5 Console.Write("" + i + " " + task["Completed"]); 6 Thread.Sleep(1000); 7 i++; 8 }

That should give ‘option #1’ suffice time to complete the task, but still it didn’t in all occasions. If you’re experiencing this you could write up your own delay either as replacement to, or in combination with ‘option #1’.

1 SPWorkflowTask.AlterTask(task, ht, true); 2 // Additional delay 3 while (!(bool)item.Tasks[taskId]["Completed"]) 4 { 5 Thread.Sleep(1000); 6 }
Related

 

Big kudos to my colleagues Tom De Wilde and Timmy Gilissen for helping me out !!


SharePoint 2010: Programmatically Approving/Rejecting Workflow


June 23, 2011 - 22:41, by Steven Van de Craen - 0 Comments

Interacting with a workflow in SharePoint is relatively straight-forward if you understand the underlying process. I’ve done multiple projects in both SharePoint 2007 and SharePoint 2010 with some level of workflow interaction: starting a workflow, approving or rejecting a workflow task, or reassigning a workflow task.

Today however I reused that code on a SharePoint 2010 environment but the Task Approval didn’t kick in, rather the workflow seemed to ‘hang’ indefinitely.

SharePoint Designer “Start Approval Process” Workflow

SharePoint Designer “Start Approval Process” Workflow

Start the Approval process

Workflow Status Page

(don’t mind the errors, they’re about a missing outgoing mail server)

Programmatically approve the task

1 bool result = false; 2 string url = "http://devsp/Shared Documents/Sample 1.docx"; 3 4 using (SPSite site = new SPSite(url)) 5 { 6 using (SPWeb web = site.OpenWeb()) 7 { 8 SPListItem item = web.GetFile(url).Item; 9 SPWorkflowTask wfTask = item.Tasks[item.Tasks.Count -1]; 10 11 Hashtable htProps = new Hashtable(); 12 htProps["TaskStatus"] = "Approved"; 13 14 result = SPWorkflowTask.AlterTask(wfTask, htProps, true); 15 } 16 } 17 18 MessageBox.Show("AlterTask Outcome: " + result);

Result

Workflow Completed (Approved)

Some things to note

  • If you have a localized site you need the translated equivalent of “Approved” and “Rejected”. For Dutch that would be “Goedgekeurd”
  • Reassigning a task can be done by specifying a new SPUser value for the “AssignedTo” value similarly to the above Approve action
  • Next post will be about some weird behaviour resulting in “This task is currently locked by a running workflow and cannot be edited” or “Note: Due to heavy load, the latest workflow operation has been queued. It will attempt to resume at a later time” messages

Follow up