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
- 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
- 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