Introduction

You may come into work one day and find that your workflows are failing.  Examining the workflow details will reveal "Failed On Start" errors.  Most likely this is due to a patch.  The fix for the patch is to manually add several lines to  the web.config file of your SharePoint web application servers.  In my case, I was replacing our SharePoint 2010 servers that were running Windows Server 2008 R2 with servers running Windows Server 2012 R2.  All patches were applied before the server was added to the SharePoint farm.  As such, the relevant web.config modifications were not in the web.config file.  The end result was the same, workflows failing to start on the new 2012 R2 front end servers.

Cause & Solution

Workflow Foundation (WF) will only run workflows when all the dependent types and assemblies are authorized in the .NET config file (or added explicitly via code) under this tree:

<configuration>
  <System.Workflow.ComponentModel.WorkflowCompiler>
    <authorizedTypes>
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodeBinaryOperatorExpression" Authorized="True" />
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodePrimitiveExpression" Authorized="True" />
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodeMethodInvokeExpression" Authorized="True" />
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodeMethodReferenceExpression" Authorized="True" />
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodeFieldReferenceExpression" Authorized="True" />
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodeThisReferenceExpression" Authorized="True" />
      <authorizedType Assembly="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" NameSpace="System.CodeDom" TypeName="CodePropertyReferenceExpression" Authorized="True" />

The above authorized types are the lines necessary to make SharePoint 2007 and 2010 start running workflows correctly again.  They should be applied to every SharePoint farm server that is running the Microsoft SharePoint Foundation Web Application service.

Post Fix

Once you've fixed your SharePoint farm servers, the next step is to find all the workflows that have failed on start.  The following PowerShell script will crawl your entire portal and report back on "Failed On Start" workflows

# Title:   FindFailedOnStartWorkflows.ps1
# Version: 1.0, 22 APR 2020
# Author:  James Sanders
# Purpose: Find all workflows that failed on start

# Add the PowerShell Snap-In
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue

# Set Variables
$wfFailedOnStart = 0
$wfDetails = ""
$timeStarted = Get-Date

# Enumerate portal looking for workflows that have failed on start
ForEach ($site in (Get-SPSite -Limit All)) {
  Write-Host
  Write-Host "Processing site:" $site.Url
  ForEach ($web in $site.AllWebs) {
    Write-Host "Processing web: " $web.Url
    ForEach ($list in $web.Lists) {
      If ((!($list.Hidden)) -And $list.WorkflowAssociations.Count -ge 1) {
        Write-Host "Processing list:" $list.Title
        ForEach ($item in $list.items) {
          # Find item workflows that have ran but are not in a "Completed" state
          $BadWorkflows = $item.workflows | Where {$_.InternalStatus -ne "Completed"}
          If ($BadWorkflows) {
            # Enumerate problematic workflows
            ForEach ($BadWorkflow in $BadWorkflows) {
              # Only looking for "Failed On Start" workflows, which is determined by Status1 field = 1
              If ($BadWorkflow.xml -match "Status1=""1""") {
                $wfa = $list.WorkflowAssociations | Where {$_.ID -eq $BadWorkflow.AssociationID}
                # Make sure there is not a newer run of the workflow that completed
                If ($item[$wfa.Name] -ne 5) {
                  Write-Host $Badworkflow.ParentItem.ID $wfa.Name "Failed On Start"
                  $wfStatus  = "<a href='$($web.URL)/_layouts/Workflow.aspx?ID=$($BadWorkflow.ParentItem.ID)&List={$($list.ID)}'>Status</a>"
                  $wfDetails += "$($site.RootWeb.Title),$($web.Title),$($list.Title),$($Badworkflow.ParentItem.ID),$($wfa.Name),$wfStatus|"
                  $wfFailedOnStart++
                }
              }
            }
          }
        }
      }
    }
    $web.Dispose()
  }
  $site.Dispose()
}

# Finalize script statistics
$timeFinished = Get-Date
$timeDuration = ("{0:hh\:mm\:ss}" -f ($timeFinished - $timeStarted)).Substring(0,12)

# Display statistics
Write-Host
Write-Host "Failed On Start Workflows: $wfFailedOnStart"
Write-Host
Write-Host "Script started:            $timeStarted"
Write-Host "Script finished:           $timeFinished"
Write-Host "Duration:                  $timeDuration"

$mailMsgServer  = "<YOUR_MAIL_SERVER>"
$mailMsgSubject = "Failed On Start Workflows"
$mailMsgFrom    = "<[email protected]>"
$mailMsgTo      = "<[email protected]>"

$mailMsgBody = ""
$mailMsgBody += "<style>"
$mailMsgBody += ".TR1 {border: 0px; padding: 0px; border-collapse: collapse;}"
$mailMsgBody += ".TD1 {border: 0px; padding: 0px;}"
$mailMsgBody += ".TH2 {border: 1px solid gray; padding: 0px; border-collapse: collapse; background-color: #80ffff;}"
$mailMsgBody += ".TR2 {border: 1px solid gray; padding: 0px; border-collapse: collapse;}"
$mailMsgBody += ".TD2 {border: 1px solid gray; padding: 2px; border-collapse: collapse;}"
$mailMsgBody += "</style>"
$mailMsgBody += "<table>"
$mailMsgBody += "<tr class='TR1'><td class='TD1'>Failed On Start Workflows:</td><td class='TD1'>$wfFailedOnStart</td></tr>"
$mailMsgBody += "<tr class='TR1'><td class='TD1' colspan='2'>&nbsp;<br/></td></tr>"
$mailMsgBody += "<tr class='TR1'><td class='TD1'>Script started:</td><td class='TD1'>$timeStarted</td></tr>"
$mailMsgBody += "<tr class='TR1'><td class='TD1'>Script finished:</td><td class='TD1'>$timeFinished</td></tr>"
$mailMsgBody += "<tr class='TR1'><td class='TD1'>Duration:</td><td class='TD1'>$timeDuration</td></tr>"
$mailMsgBody += "</table><br/>"
If ($wfDetails.Length -gt 0) {
  $mailMsgBody += "<table>"
  $mailMsgBody += "<tr class='TH2'><td class='TD2'>Site Collection</td><td class='TD2'>Web Site</td><td class='TD2'>List</td><td class='TD2'>ID</td><td class='TD2'>Workflow</td><td class='TD2'>Status</td></tr>"
  $wfDetailsLines = $wfDetails.Split("|")
  ForEach ($line in $wfDetailsLines) {
    If ($line.Length -gt 0) {
      $mailMsgBody += "<tr class='TR2'>"
      $wfDetailsItems = $line.Split(",")
      ForEach ($item in $wfDetailsItems) {
        $mailMsgBody += "<td class='TD2'>$item</td>"
      }
    }
    $mailMsgBody += "</tr>"
  }
}

$mailMsgBody += "</table><br/>"
Send-MailMessage -From $mailMsgFrom -to $MailMsgTo -subject $mailMsgSubject -Body $mailMsgBody -SmtpServer $mailMsgServer -BodyAsHTML

Modify the following lines to fit your organization:
$mailMsgServer  = "<YOUR_MAIL_SERVER>"
$mailMsgSubject = "Failed On Start Workflows"
$mailMsgFrom    = "<[email protected]>"
$mailMsgTo      = "<[email protected]>"

 

 

Curious how much data is in your SharePoint on premises site collection recycle bin?  SharePoint does not provide a way to determine this.  Fortunately, PowerShell can accomplish this with relative ease.  The following PowerShell script will process site collection first and second stage recycle bins and report back on storage space used.  Remember to modify the $SiteColl variable and point it to a valid site collection first!

# Title:   Get-RecycleBinSize.ps1
# Version: 1.0, 13 APR 2020
# Author:  James Sanders
# Purpose: Calculate and display size of site collection recycle bin (first and second stage)

# Add the PowerShell Snap-In
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA SilentlyContinue

# Site Collection to query
$SiteColl = "http://mybigfatsharepointsite.com/demo"

#Get the site collection
Try {
  $site = Get-SPSite $SiteColl -ErrorAction Stop
}
Catch {
  Write-Host
  Write-Host -ForegroundColor Red "- Unable to open site collection $SiteColl"
  Write-Host
  Break
}

# FIRST STAGE RECYCLE BIN

# Create SPRecycleBinQuery object to query the first stage recycle bin
$SPRecycleBinQuery = New-Object -TypeName Microsoft.SharePoint.SPRecycleBinQuery
$SPRecycleBinQuery.ItemState = [Microsoft.SharePoint.SPRecycleBinItemState]::FirstStageRecycleBin
$SPRecycleBinQuery.RowLimit = [int]::MaxValue-1

# Get the sum of 'Size' property of all recycle bin items
$RecycleBinSize = $site.GetRecycleBinItems($SPRecycleBinQuery) | Measure-Object -Property Size -Sum | Select-Object -ExpandProperty Sum

# Calculate the value in MB
$size = ([System.Math]::Round(($RecycleBinSize/1Mb),2))
write-host "First Stage Recycle Bin Size (MB):" $size

# SECOND STAGE RECYCLE BIN

# Create SPRecycleBinQuery object to query the second stage recycle bin
$SPRecycleBinQuery=new-object Microsoft.SharePoint.SPRecycleBinQuery
$SPRecycleBinQuery.ItemState = [Microsoft.SharePoint.SPRecycleBinItemState]::SecondStageRecycleBin
$SPRecycleBinQuery.RowLimit = 2000
$size=0

DO {
  $SPRecycleBinItemCollection = $site.GetRecycleBinItems($SPRecycleBinQuery)
  $SPRecycleBinQuery.ItemCollectionPosition = $SPRecycleBinItemCollection.ItemCollectionPosition

  For($i=$SPRecycleBinItemCollection.Count-1; $i -GE 0; $i--) {
    $Size += $SPRecycleBinItemCollection[$i].Size
  }
}
While ($SPRecycleBinQuery.ItemCollectionPosition -Ne $Null)

$size = ([System.Math]::Round(($size/1Mb),2))
Write-Host "Second Stage Recycle Bin Size (MB)" $size

 

Sometimes you just need to rebuild SharePoint 2010 search.  It happens.  Life happens.  SharePoint 2010 Search poops the bed.  This article will discuss how to remove a SharePoint 2010 search service application and clean up leftover search components.

Search Service Application Removal

So the first thing we need to do is remove the search service application.  This can be done using Central Administration or PowerShell.

Remove With Central Administration

To remove the search service application using Central Administration:

Remove With PowerShell

To remove the search service application using PowerShell:

$ssa = Get-SPServiceApplication -Name "Your Search Service Application Name"
Remove-SPServiceApplication $sa - RemoveData

Die Search Die

Sometimes neither of the above methods work and result in timeout and/or error(s).  In this case, the following PowerShell can be used as a final resort:

# Method One
Get-SPEnterpriseSearchServiceApplication
# Take note of the search service application GUID
$ssa = Get-SPEnterpriseSearchServiceApplication -id "<search_guid>"
$ssa.unprovision(1)

# Method Two - In Case Method One Fails
Get-SPEnterpriseSearchServiceApplication
# Take note of the search service application GUID
$ssa = Get-SPEnterpriseSearchServiceApplication -id "<search_guid>"
$ssa.delete()

Stopping Search Components

Once the search service application has been removed, there may be leftover components running the search server(s).

SharePoint Server Search

To ensure this service has stopped on all farm servers:

Get-SPEnterpriseSearchServiceInstance -Local | Stop-SPEnterpriseSearchServiceInstance

Get-SPEnterpriseSearchServiceInstance | Stop-SPEnterpriseSearchServiceInstance

Search Query and Site Settings Service

To ensure this service has stopped on all farm servers:

Search Administration Web Service

To ensure this service has stopped on all farm servers:

$srv = Get-SPServer -Identity <SEARCH_ADMIN_SERVER>
$si = $srv.ServiceInstances | Where {$_.TypeName -eq "Search Administration Web Service"}
Stop-SPServiceInstance $si

 

Copyright 2011 - 2021 The Lazy SharePoint Admin | All Rights Reserved
menu-circlecross-circle linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram