PowerShell script for updating workflows

The other day when working on a project I needed to update some existing Visual Studio workflows. I had a Visual Studio project which contained a variety of workflows which were used across several SharePoint sites. While keeping all the workflows in one solution works it is not ideal for deployment as you can’t auto associate the workflows as part of the deployment.

I decided to try and automate the deployment so started thinking what my options were. Being a developer my first thought was code in a feature receiver on activated and deactivated, however I decided that this would be another opportunity to practice some PowerShell. This gave me the flexibility of changing the script if need be without having to alter code.

Before starting to write the script I went through in my head what it need to do and came up with the following:

  1. Get the list to which the workflow should be deployed
  2. Check to see if the workflow was already deployed
    1. If it was already deployed check to see if there were any running instances
      1. If there were running instances set the workflow to not allow any new instances but allow existing ones to finish.
      2. If not remove the workflow and go to step 3
    2. If not move on to step 3
  3. Associate the new version of the workflow to the list

 

With these steps in mind I started to create my script. I will cover each of the above and go through the script required to achieve the goal.

Point 1

First I wanted to get the list the workflow was associated with. This is very easy and can be accomplished by getting the web object then using that to get the list object much the same way you would if you were writing C#

Get Workflow List
  1. Write-Host 'Get web'
  2. $hrWeb = Get-SPWeb $siteURL
  3.  
  4. Write-Host 'Get Lists'
  5. $list = $hrWeb.Lists[$listName]
  6. $taskList = $hrWeb.Lists[$taskListName]
  7. $workflowHistoryList = $hrWeb.Lists[$historyListName]

 

Point 2

With the list the next task was to see if the workflow is currently deployed to the list. This presented a problem as my plan was to automatically deploy the workflow with a name and the date of the deployment i.e. “Workflow 22/12/2011’. This caused problems as I wouldn’t know the last deployment date so I had to use a like comparison on the name. I wasn’t comfortable just using a like comparison as I felt this wasn’t always going to get me what I wanted so I added an additional check to validate the name of the workflow but also if it was of the correct base workflow type.

Try and find workflow
  1. Write-Host 'Get base template'
  2. $basetemplate = $hrWeb.WorkflowTemplates.GetTemplateByName($workflowTemplateName,$culture);
  3. Write-Host $basetemplate.Id
  4.  
  5. #set up variable to hold workflow instance if we find it
  6. $existingWorkflow = $null
  7.  
  8. Write-Host 'Get workflow association'
  9. #loop through all associations on list
  10. foreach($tempWorkflowAssociation in $list.WorkflowAssociations)
  11. {
  12. #check if the base template id matches the base template of the current WF associaton
  13. #in additon check the name of the current WF association against the one we are interested in
  14. if($tempWorkflowAssociation.BaseTemplate.Id -eq $basetemplate.Id -and $tempWorkflowAssociation.Name -like $workflowName +"*" )
  15. {
  16. $existingWorkflow = $tempWorkflowAssociation
  17. break
  18. }
  19. }
  20. #check we have a workflow
  21. if($existingWorkflow -ne $null)
  22. {

 

Point 2.1

As you can see the last line in the previous snippet makes sure we have a workflow returned so at this point we know there is a version of the workflow we are interested in currently deployed to the list. Now I need to know if there are any running instances for this.

Check running instances
  1. Write-Host 'Got workflow associated with list'
  2. if($existingWorkflow.RunningInstances -ge 0)
  3. {

 

Point 2.1.1

If there are running instances these must continue working until they are complete but not allow any new instances of this workflow to be initiated. This can be done by setting the ‘No new instances’ option through the workflow settings in the UI or by the following command.

No new instances
  1. Write-Host 'There are running instances so set to allow no new running instances'
  2. $existingWorkflow.set_Enabled($false)
  3. $list.UpdateWorkflowAssociation($existingWorkflow)

 

Point 2.1.2

If there are no running instances then we want to remove the current version. This can be done by the command below

Remove workflow
  1. Write-Host 'No running instances so remove'
  2. $list.RemoveWorkflowAssociation($existingWorkflow)

 

Point 3

We are now in a position where we can try and associate the new version of the workflow to the list. As I mentioned above when attaching the new workflow I am adding it with the name and the date so I first build up the name then create an new workflow association object the add it to the list.

Associate new workflow
  1. Write-Host 'Create workflow association details'
  2. $date = Get-Date
  3. $workflowName = $workflowName + " " + $date.ToShortDateString()
  4. $newWorkflow=[Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation($basetemplate, $workflowName,$taskList,$workflowHistoryList)  
  5.  
  6. $newWorkflow.AllowManual = $allowManualStart
  7. $newWorkflow.AutoStartChange = $autoStartChange
  8. $newWorkflow.AutoStartCreate = $autoStartCreate
  9.  
  10. Write-Host 'Add workflow to list'
  11. $list.AddWorkflowAssociation($newWorkflow)

At this point the new version of the workflow should be attached to the list. I started creating this with some hardcoded values but then extracted it out into a reusable function. The full function is below.

Fully assocaition function
  1. function AssocaiteWorkflow([string]$siteURL, [string]$listName, [string]$taskListName, [string]$historyListName,
  2.     [string]$workflowTemplateName, [string]$workflowName, [bool]$allowManualStart, [bool]$autoStartChange, [bool]$autoStartCreate)
  3. {
  4.  
  5. Write-Host 'Start WF assocaition for ' $workflowName ' on list ' $listName ' and site ' $siteURL
  6.  
  7. #get culture
  8. $culture= Get-Culture
  9.  
  10. Write-Host 'Get web'
  11. $hrWeb = Get-SPWeb $siteURL
  12.  
  13. Write-Host 'Get Lists'
  14. $list = $hrWeb.Lists[$listName]
  15. $taskList = $hrWeb.Lists[$taskListName]
  16. $workflowHistoryList = $hrWeb.Lists[$historyListName]
  17.  
  18. Write-Host 'Get base template'
  19. $basetemplate = $hrWeb.WorkflowTemplates.GetTemplateByName($workflowTemplateName,$culture);
  20. Write-Host $basetemplate.Id
  21.  
  22. #set up variable to hold workflow instance if we find it
  23. $existingWorkflow = $null
  24.  
  25. Write-Host 'Get workflow association'
  26. #loop through all associations on list
  27. foreach($tempWorkflowAssociation in $list.WorkflowAssociations)
  28. {
  29. #check if the base template id matches the base template of the current WF associaton
  30. #in additon check the name of the current WF association against the one we are interested in
  31. if($tempWorkflowAssociation.BaseTemplate.Id -eq $basetemplate.Id -and $tempWorkflowAssociation.Name -like $workflowName +"*" )
  32. {
  33. $existingWorkflow = $tempWorkflowAssociation
  34. break
  35. }
  36. }
  37. #check we have a workflow
  38. if($existingWorkflow -ne $null)
  39. {
  40. Write-Host 'Got workflow associated with list'
  41. if($existingWorkflow.RunningInstances -ge 0)
  42. {
  43. Write-Host 'There are running instances so set to allow no new running instances'
  44. $existingWorkflow.set_Enabled($false)
  45. $list.UpdateWorkflowAssociation($existingWorkflow)
  46. }
  47. else
  48. {
  49. Write-Host 'No running instances so remove'
  50. $list.RemoveWorkflowAssociation($existingWorkflow)
  51. }
  52. }
  53. else
  54. {
  55. Write-Host 'No workflow associated with list'
  56. }
  57.  
  58. Write-Host 'Create workflow association details'
  59. $date = Get-Date
  60. $workflowName = $workflowName + " " + $date.ToShortDateString()
  61. $newWorkflow=[Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation($basetemplate, $workflowName,$taskList,$workflowHistoryList)  
  62.  
  63. $newWorkflow.AllowManual = $allowManualStart
  64. $newWorkflow.AutoStartChange = $autoStartChange
  65. $newWorkflow.AutoStartCreate = $autoStartCreate
  66.  
  67. Write-Host 'Add workflow to list'
  68. $list.AddWorkflowAssociation($newWorkflow)
  69. }

 

You can then call this in the standard way i.e.

Call function
  1. AssocaiteWorkflow "http://sharepoint" "Test List Name" "Tasks List Name" "Workflow History List Name" "Visual Studio Workflow Name" "Workflow Instance Name" $true $true $true

 

As with all PowerShell scripts while they are very useful please always test them on a development environment before running them on live. As always I can’t be responsible for any issues that arise so if you use this you do so at your own risk.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: