Determine invalid Matrix42 workflows

Within this guide you are going to learn how to determine and resolve invalid Matrix42 workflows.

Context

  • As a result of e.g. a worker running into resource problems, Matrix42 may not inform workflows about a multitude of things. This includes resuming them after a sleep operation.

FIX

  1. Determine the suspended workflows that require continuation by using the following PowerShell script.
$result = @()# A) Obtain all currently suspended workflows$workflowColumns = @(    "T(PLSLWorkflowProcessInstanceClassBase).[Expression-ObjectID] AS WorkflowEOID",    "T(PLSLWorkflowProcessInstanceClassBase).T(PLSLProcessInstanceClassBase).State.DisplayString AS State",    "T(PLSLWorkflowProcessInstanceClassBase).T(PLSLProcessInstanceClassBase).LastUpdate AS LastUpdate",    "T(PLSLWorkflowProcessInstanceClassBase).Workflow.T(PLSLComponentClassBase).Title AS Title",    "ObjectID AS BookingEOID")$workflowWhere = "TypeID='6f3ed39c-0aab-4606-b44e-d646d077b361' AND T(PLSLWorkflowProcessInstanceClassBase).Workflow.T(PLSLComponentClassBase).Title='Provisioning - RootITUp Booking Processing' AND (T(PLSLWorkflowProcessInstanceClassBase).T(PLSLProcessInstanceClassBase).State=32 OR T(PLSLWorkflowProcessInstanceClassBase).T(PLSLProcessInstanceClassBase).State=128)"$suspended = Get-Fragments -DataDefinition "PLSLProcessInstanceClassRelatedObject" -Columns $workflowColumns -Where $workflowWhere# B) For each workflow, determine booking and ticket status$bookingColumns = @(    "T(SPSCommonClassBase).State.DisplayString AS State",    "BookingID AS BookingNumber",    "Order.TicketNumber AS OrderNumber",    "ID",    "[Expression-ObjectID] AS EOID",    "Ud_ExternalBookingStatus.DisplayString AS ExternalState")$activitiesColumns = @(    "T(SPSCommonClassBase).State.DisplayString AS TicketState",    "TicketNumber",    "Subject")for ($i = 0; $i -lt $suspended.Count; $i++) {    $percent = $i / $suspended.Count * 100    Write-Progress -Activity "Looking up related workflow information." -Status "$percent% Complete:" -PercentComplete $percent    $workflow = $suspended[$i]    # Booking is in a state, where workload should continue or be finished within Matrix42    $bookingsWhere = "[Expression-ObjectID]='{0}'" -f $workflow.BookingEOID    $bookings = @()    $bookings += Get-Fragments -DataDefinition "SVCServiceBookingClassBase" -Columns $bookingColumns -Where $bookingsWhere    if ($bookings.Count -eq 1) {        $booking = $bookings[0]        $activitiesWhere = "Ud_RelatedBooking.ID='{0}'" -f $booking.ID        $activities = @()        $activities += Get-Fragments -DataDefinition "SPSActivityClassBase" -Columns $activitiesColumns -Where $activitiesWhere        if ($activities.Count -eq 0) {            Write-Warning -Message ("{0} spawned no tickets. $activitiesWhere" -f $booking.BookingNumber)            $result += @{                WorkflowEOID         = $workflow.WorkflowEOID                WorkflowState        = $workflow.State                WorkflowTitle        = $workflow.Title                WorkflowLastUpdate   = [datetime] $workflow.LastUpdate                BookingEOID          = $booking.EOID                BookingState         = $booking.State                BookingExternalState = $booking.ExternalState                BookingNumber        = $booking.BookingNumber                OrderNumber          = $booking.OrderNumber                TicketState          = ""                TicketNumber         = ""                TicketSubject        = ""            }        }        else {            foreach ($activity in $activities) {                $result += @{                    WorkflowEOID         = $workflow.WorkflowEOID                    WorkflowState        = $workflow.State                    WorkflowTitle        = $workflow.Title                    WorkflowLastUpdate   = [datetime] $workflow.LastUpdate                    BookingEOID          = $booking.EOID                    BookingState         = $booking.State                    BookingExternalState = $booking.ExternalState                    BookingNumber        = $booking.BookingNumber                    OrderNumber          = $booking.OrderNumber                    TicketState          = $activity.TicketState                    TicketNumber         = $activity.TicketNumber                    TicketSubject        = $activity.Subject                }            }        }    }    else {        Write-Error -Message ("Workflow {0} has no booking" -f $workflow.WorkflowEOID)    }}return $result
  1. Based on the information obtained earlier, run the following evaluation script.
function Resume-SuspendedWorkflow {    param([Parameter(Mandatory)] $EOID)    try {        # 1.) We resume the workflows and pray        $body = '{"Objects":[{"ObjectIds":["' + $item.WorkflowEOID + '"],"TypeName":"PLSLWorkflowProcessInstanceType"}]}'        Get-AuthenticatedRequest -Call "workflowinstance/resume" -RawBody $body -Method PUT | Out-Null        return $true    }    catch {        return $false    }}$errors = @()for ($i = 0; $i -lt $result.Count; $i++) {    $percent = $i / $result.Count * 100    Write-Progress -Activity "Evaluating Workflow information." -Status "$percent% Complete:" -PercentComplete $percent    $item = $result[$i]    $isSuspended = $item.WorkflowState -eq "Angehalten" -or $item.WorkflowState -eq "Suspended"    $isExecuting = $item.WorkflowState -eq "Ausführen" -or $item.WorkflowState -eq "Executing"    $isTicketClosed = $item.TicketState -eq "Geschlossen" -or $item.TicketState -eq "Closed"    # A) Missing Ticket    if ([string]::IsNullOrWhiteSpace($item.TicketSubject)) {        $errors += New-Object -TypeName PSObject -Property @{            Error                = "Missing Ticket"            BookingNumber        = $item.BookingNumber            WorkflowEOID         = $item.WorkflowEOID            BookingState         = $item.BookingState            BookingExternalState = $item.BookingExternalState            Operation            = $false        }    }    # B) Workflow is wrongfully suspended, order is already completed    elseif (        $isSuspended -and         $item.BookingExternalState -in @("ApprovalDeclined", "Delivered")    ) {        $errors += New-Object -TypeName PSObject -Property @{            Error                = "External Order Completed"            BookingNumber        = $item.BookingNumber            WorkflowEOID         = $item.WorkflowEOID            BookingState         = $item.BookingState            BookingExternalState = $item.BookingExternalState            Operation            = (Resume-SuspendedWorkflow -EOID $item.WorkflowEOID)        }    }    # C) Workflow is wrongfully suspended, related issue was already closed    elseif (        $isSuspended -and        $item.BookingExternalState -in @("InDelivery", "Ordered") -and        $isTicketClosed -and        -not $item.TicketSubject.StartsWith("Software Bestandsprüfung")    ) {        $errors += New-Object -TypeName PSObject -Property @{            Error                = "Closed Ticket"            BookingNumber        = $item.BookingNumber            WorkflowEOID         = $item.WorkflowEOID            BookingState         = $item.BookingState            BookingExternalState = $item.BookingExternalState            Operation            = (Resume-SuspendedWorkflow -EOID $item.WorkflowEOID)        }    }    # D) ExternalBookingStatus >=Ordered and TicketRole = Administration and Catgeory "Sonstiges"    elseif (        $isSuspended -and        $item.BookingExternalState -in @("InDelivery", "Ordered", "Delivered") -and($item.RecipientRole = "b9f569f3-8ef5-e011-ace1-0021974c3aef" -and $item.Category -eq "f122e1bd-a5d7-4f18-8f65-dbfe65a4360c") -and        -not $item.TicketSubject.StartsWith("Software Bestandsprüfung")    ) {        $errors += New-Object -TypeName PSObject -Property @{             Error                = "Ticket not visible"            BookingNumber        = $item.BookingNumber            WorkflowEOID         = $item.WorkflowEOID            BookingState         = $item.BookingState            BookingExternalState = $item.BookingExternalState            Operation            = (Resume-SuspendedWorkflow -EOID $item.WorkflowEOID)        }    }    # E) Workflow had no changes for a long time (21 days)    elseif (        $isSuspended -and        $item.LastUpdate -le [datetime]::Now.AddDays(-21)    ) {        $errors += New-Object -TypeName PSObject -Property @{             Error                = "Workflow had no changes for more than 21 days"            BookingNumber        = $item.BookingNumber            WorkflowEOID         = $item.WorkflowEOID            BookingState         = $item.BookingState            BookingExternalState = $item.BookingExternalState            Operation            = (Resume-SuspendedWorkflow -EOID $item.WorkflowEOID)        }    }    # F) Workflow is stuck in executing. This can be the result of B) - D)    elseif (        $isExecuting    ) {        $errors += New-Object -TypeName PSObject -Property @{            Error                = "Workflow stuck in execution"            BookingNumber        = $item.BookingNumber            WorkflowEOID         = $item.WorkflowEOID            BookingState         = $item.BookingState            BookingExternalState = $item.BookingExternalState        }    }}$errors | Export-Csv -Path "workflow_errors.csv" -NoTypeInformation -Force -Encoding UTF8