04 April 2014

Automated publishing of Nintex Workflow User Defined Actions, with workflow references

One of my absolute favourite features in Nintex Workflow is the User Defined Action (UDA). It lets you wrap up a commonly used piece of functionality with defined input and output parameters, into a neat, centrally managed object, which can be easily reused in workflows across the entire farm. A UDA can also easily be updated whereby Nintex Workflow automatically updates all workflows that reference the UDA.

For a workflow project we are currently building, I was a little disappointed to find that there is no supported method to automate the import of a UDA. This slows down our planned deployment process which is nearly entirely automated through PowerShell.

What was even more troubling was that importing a UDA through the user interface did not work correctly for imported workflows that referenced the UDA. Each UDA referenced within a workflow appeared like this:
This is how a UDA appears in a workflow if the reference is broken, for example through an import process
Attempting to configure the UDA only threw exceptions. The only way to resolve this was to manually delete the UDA references, replace them with new references, and update all the parameters in the references. This is very slow, error-prone and frustrating!

With some investigation, it turns out that each UDA has an internal GUID identifier which is used by workflows to reference the UDA. This GUID can been seen in both the exported .uda and .nwf files. It seems that when importing a UDA through the user interface, a new GUID is assigned to the UDA. This is what was breaking the references.

Warning: the solution I am presenting here is not technically part of the supported Nintex Workflow API, so it is not guaranteed to work for all future releases, but it works for my initial testing against Nintex Workflow 2013 version 3.0.6.0. This process is based on the suggestion from user tburdin on the excellent Nintex Connect Forums, in this thread. It uses the publicly exposed members of the Nintex Workflow assemblies to automate the import of UDAs. Here is the PowerShell snippet:

[System.Reflection.Assembly]::LoadWithPartialName('Nintex.Workflow') | Out-Null
 
$fs = New-Object System.IO.FileStream($filePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$ms = New-Object System.IO.MemoryStream
$fs.CopyTo($ms)
$fs.Close()
$fs.Dispose() 
$uda = [Nintex.Workflow.UserDefinedActions.UserDefinedAction]::Import($web, $ms, [Nintex.Workflow.ConfigurationScope]::Site)
$uda.Update($web, $publish, [Nintex.Workflow.Publishing.Scope]::SiteCollection, "")
$ms.Close()
$ms.Dispose()

Note that the code assumes you've populated the following variables:
  • $name: the name of your UDA.
  • $filePath: the full path to your exported .uda file.
  • $web: the SPWeb object defining the context of where the UDA is going to be imported
  • $publish: a Boolean value indicating whether to publish the UDA or simply import it
The above code publishes to the Site Collection level, but you can also publish to a single site or even to the entire Farm, by altering the values of the Nintex enumerations referenced in the code. To publish to a single site, use [Nintex.Workflow.ConfigurationScope]::Web and [Nintex.Workflow.Publishing.Scope]::Web, and to publish to a farm, use [Nintex.Workflow.ConfigurationScope]::Farm and [Nintex.Workflow.Publishing.Scope]::Farm.

The best aspect of this process is that it retains the original GUID reference of each UDA. So, when you import UDA using this method and then import workflows that reference the original UDAs, no re-wiring is required! You can fully automate the process and have a portable workflow deployment strategy.

It's worth noting that Nintex probably updates those GUIDs during the import process to ensure that you can never get in a situation where you have multiple UDAs in a farm that use the same GUID. For example, you could import the same UDA into multiple sites (you wouldn't want to do this, but it would certainly be possible). In this scenario, having two or more UDAs with the same GUID would almost certainly cause conflicts. Use with caution!