powershell cancel and restart site collection reusable workflow template


Badge +5

I have a Nintex 2010 Standard Site Collection Reusable workflow template called 'reminders' that runs on a content type called 'requirements' which is basically a task list with a Due Date. I have just made a very important improvement to it. I'd like to stop all instances of it that have not completed and restart it. my architecture is something like this:

 

site collection

  site

     project sites

         project site 1

             requirements list 1

         project site 2

             requirements list 2

         project site 3

             requirements list 3

         . . .

 

So I want to iterate over all of the project sites, and then over all of the requirements list items and stop the old version of 'site n reminders' and then start the new version if the Reminder workflow has not completed and the due date is more than 14 days out.

 

Here are the problems:

  1. The $list.WorkflowAssociations only gives you the associated list workflows - not the site collection re-usable template (SCRUT) workflows that are associated with the list because of the content type, even though you have to make the association in order to be able to run the SCRUT workflow. This means the association and association data are null and the  .StartWorkflow method fails.
  2. The Cancel workflow portion does not work, but does not throw an error either. The next time you run the workflow, it tries to cancel the same items' workflows all over again.

 

Here is my powershell (I've attached it as well.

------------------------------

cls

 

$deadline = (get-date).AddDays(14)

Write-Host "Deadline  $deadline"

$site = Get-SPSite "http://sharepoint/site collection"

 

#Your SharePoint Site URL

$web = Get-SPWeb "http://sharepoint/site collection/site/project sites/";

$web.AllowUnsafeUpdates = $true;   

foreach ($subsite in $web.Webs)

{

  Write-Host $subsite.Name

  $list = $subsite.Lists["Requirements"];

  $count = 0

  #Workflow Manager

  $wfToStart = $subsite.Name

  $wfToStart = "$wfToStart Requirement Reminders"

  $manager = $subsite.Site.WorkFlowManager

  Write-Host "manager: " $manager

  $wfas = $site.WorkFlowAssociations

  foreach ($wfa in $wfas)

  {

  Write-Host "associated workflow " $wfa.Name

  }

 

 

  $association = $list.WorkFlowAssociations.GetAssociationByName($wfToStart,"en-US")

  Write-Host "association: " $association

  $data = $association.AssociationData

  Write-Host "Data: " $data

 

 

  #Loop through all Items in List then loop through all Workflows on each List Items.        

  foreach ($listItem in $list.Items)

  {

  $duedate = [datetime]$listItem["Due Date"]

  Write-Host "   " $listItem["Title"] " " $duedate

  if ($duedate -gt $deadline)

  {

  foreach ($wf in $listItem.Workflows)

  {

  if(($wf.ParentAssociation.InternalName.contains("Reminders")) -and ($wf.IsCompleted  -ne $true))

  {

  #Cancel Workflow  

  [Microsoft.SharePoint.Workflow.SPWorkflowManager]::CancelWorkflow($wf);

  write-host $wf.ParentAssociation.InternalName" stopped on " $listItem["Name"]

  }

  }

  $wf = $manager.StartWorkFlow($item, $assoc, $data, $true)

  }

  }

  $manager.Dispose()

}

$web.Dispose();

-----------------------------------------------

 

 

I've also tried to start the workflow using the webservice Emily Billing, but that does not work either

Start a Workflow using a Web Service

 

Vadim Tabakman

PowerShell - Cancel all Running Workflows - Vadim Tabakman

 

Aaron Labiosa
How to bulk cancel list item workflows with a status of "Error Occurred"


5 replies

Badge +5

It is possible this is not working for me because my user profile service is not working. I found a post somewhere on the web that suggested that might be an issue. I will keep you up to date.

Badge +5

I am back trying to solve this again.

Userlevel 1
Badge +5

Hi Joshua,

Since you are using the (SCRUT)  see MSDN : Workflows overview (SharePoint Server 2010) i believe  you should use something like this in your script to locate the Workflow that is associated to a Content type and not to a list, see this example from MSDN it should be easy to convert to powershell script : SPContentType.WorkflowAssociations property (Microsoft.SharePoint)

Please let me know if that help you ... as i can drill a quick script for that will do the job

 

Note: in your script you are using :

$wfas = $site.WorkFlowAssociations  -- thats for SPSite WorkFlowAssociations 

and

$association = $list.WorkFlowAssociations --  thats for List WorkFlowAssociations 

So you need a Content Type WorkFlowAssociations 

ex:

$ctName = "requirements";

$ct = $web.ContentTypes[$ctName]

# Workflow Association with Content Type

$culture = New-Object System.Globalization.CultureInfo("en-US")

$association = [Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::$ct.WorkflowAssociations.GetAssociationByName($WfName, $culture);

 

Hope that help

Thanks,

Majid

Badge +5

It took a bit more work but I got it all working.

$parent = Get-SPWeb $url

$wfName = "Requirement Reminders"

foreach ($web in $parent.Webs)

{

    Write-Host "`n---------------------------------------------`nworking on" $web

    $list = $web.Lists["Requirements"]

    $cTypes = $list.ContentTypes

    $cType = $cTypes["Contract Requirement"]

    $deadline = (get-date).AddDays(7)

    $manager = $web.Site.WorkFlowManager

    $pcwfName = $web.Url

    $pcwfName = $pcwfName.Substring($pcwfName.Length-9, 9)

    $pcwfName = $pcwfName + " " + $wfName

    # Write-Host "pcwfName '$pcwfName'"

   

    # Find the reminder WF

    foreach($wf in $cType.WorkflowAssociations)

    {

       $thisName = $wf.Name

       # Write-Host "this Name '$thisName'"

       if($thisName -eq $wfName -Or $thisName -eq $pcwfName)

       {

           Write-Host "Got the WFA!"

           $reminderWFA = $wf

           $data = $reminderWFA.data

           Break

       }

          

    }

    #Loop through the list items and the items' workflows and stop the Reminders

    foreach($i in $list.Items)

    {

        if ($i["Due Date"] -gt $deadline)

        {

            Write-Host "Due Later:" $i["Title"] "is due on" $i["Due Date"]

            foreach ($wf in $i.Workflows)

            {

                # Kill any running workflows

                [Microsoft.SharePoint.Workflow.SPWorkflowManager]::CancelWorkflow($wf)  

            }

           

            # Start Reminders Workflow

            $w = $manager.StartWorkFlow($i, $reminderWFA, $data, $true)

       

        }

    } 

    $web.Dispose()

}

$parent.dispose()

Userlevel 1
Badge +5

Great stuff happy.png

Your script will be much help for others in the community ..Well done Joshua

Reply