サンプル コード : SharePoint 2013 形式ワークフローの状態出力

今回の投稿では、特定のリストに関連付けられた SharePoint 2013 形式ワークフローの各インスタンスの状態を出力する SharePoint Online用のサンプル コードをご紹介します。

SharePoint 2013 形式のワークフローの状態列は、ワークフローの状態列をクリックすれば、下記のように表示されます。

しかし、列自体に、ワークフローの実際の内部状態を表示しないため、リスト ビューなどでワークフローの状況が把握できないというご質問を受けます。

SharePoint 2013 形式ワークフローは、SharePoint 2010 形式ワークフローよりも、リトライ処理が堅牢に実装されております。

そのため、ワークフローの処理が失敗する可能性は低いですが、リトライしても成功しない処理 (例. 削除済みアイテムを削除する) をリトライし続け、リトライ回数を枯渇して一旦停止になる場合もあります。

今回の投稿では、このようなシナリオにおいて SharePoint 2013 形式ワークフローの内部状態を確認し、エラーとなったワークフローを検出するサンプル スクリプトをご紹介します。

事前準備

下記の内容を実施済みのクライアント環境においては、事前準備の項目を再度実施する必要はございません。

1 : SharePoint Online Client Components SDK のダウンロード

サンプル スクリプトを実行するための実行環境として SharePoint Online Client Components SDK をダウンロードします。

以下のリンクよりダウンロード可能です。

タイトル : SharePoint Online Client Components SDK
アドレス : https://www.microsoft.com/en-us/download/details.aspx?id=42038

 

2 : スクリプトの実行ポリシーを変更する

1)PowerShell を管理者として起動します。
2) 以下のコマンドを実行し、現在の PowerShell の実行ポリシーを確認します。

 Get-ExecutionPolicy

3) 以下のコマンドを実行し、PowerShell の実行ポリシーを変更します。

 Set-ExecutionPolicy RemoteSigned

補足 : RemoteSigned 以上の実行ポリシー (例. Unrestricted) が指定されている場合は、指定の必要はありません。

  

コードの実行

1) 以下のコードを GetWfStatus.ps1 として保存します。

 param(
 $siteUrl,
 $listName,
 $username,
 $password,
 $outfile,
 $resume = $false
)

Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.WorkflowServices.dll"

function ExecuteQueryWithIncrementalRetry($retryCount, $delay)
{
  $retryAttempts = 0;
  $backoffInterval = $delay;
  if ($retryCount -le 0)
  {
    throw "Provide a retry count greater than zero."
  }
  if ($delay -le 0)
  {
    throw "Provide a delay greater than zero."
  }
  while ($retryAttempts -lt $retryCount)
  {
    try
    {
      $script:context.ExecuteQuery();
      return;
    }
    catch [System.Net.WebException]
    {
      $response = $_.Exception.Response
      if ($response -ne $null -and $response.StatusCode -eq 429)
      {
        Write-Host ("CSOM request exceeded usage limits. Sleeping for {0} seconds before retrying." -F ($backoffInterval/1000))
        #Add delay.
        Start-Sleep -m $backoffInterval
        #Add to retry count and increase delay.
        $retryAttempts++;
        $backoffInterval = $backoffInterval * 2;
      }
      else
      {
        throw;
      }
    }
  }
  throw "Maximum retry attempts {0}, have been attempted." -F $retryCount;
}

function EnumWorkflowsInFolder($list, $ServerRelativeUrl)
{
  do
  {
    $camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
    $camlQuery.ListItemCollectionPosition = $position
    $camlQuery.ViewXml = "<View><RowLimit>5000</RowLimit></View>";
    if ($serverRelativeUrl -ne $null)
    {
       $camlQuery.FolderServerRelativeUrl = $ServerRelativeUrl
    }
    $listItems = $list.GetItems($camlQuery);
    $script:context.Load($listItems);
    ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000

    foreach($listItem in $listItems)
    {
      if ($listItem.FileSystemObjectType -eq [Microsoft.SharePoint.Client.FileSystemObjectType]::Folder)
      {
         $script:context.Load($listItem.Folder)
         ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000
         EnumWorkflowsInFolder -List $list -ServerRelativeUrl $listItem.Folder.ServerRelativeUrl
      }

      $wfic = $script:wfis.EnumerateInstancesForListItem($list.Id, $listItem.Id);
      $script:context.Load($wfic);
      ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000
 
      foreach ($wfi in $wfic)
      {
        WriteOut -text (($listItem.Id.ToString()) + "," + (GetWorkflowSubscription -subid $wfi.WorkflowSubscriptionId) + "," + $wfi.Status) -append $true
        if ($resume)
        {
            if ($wfi.Status -eq "Suspended")
            {
                $script:wfis.ResumeWorkflow($wfi)
                ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000
                Write-Output ("Resumed workflow on Item ID = " + $listItem.Id.ToString())
            }
        }
      }
    } 
    $position = $listItems.ListItemCollectionPosition
  }
  while($position -ne $null)
}

function GetWorkflowSubscription($subid)
{
  if ($script:wfsubhash[$subid.ToString()] -eq $null)
  {    
    $sub = $script:wfss.GetSubscription($subid)
    $script:context.Load($sub)
    ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000

    $script:wfsubhash[$subid.ToString()] = $sub.Name
    return $sub.Name
  }
  else
  {
    return $script:wfsubhash[$subid.ToString()]
  }
}

function WriteOut($text, $append)
{
  if ($outfile -eq $null)
  {
    Write-Output $text
  }
  else
  {
    if ($append)
    {
      $text | Out-File $outfile -Append -Encoding UTF8
    }
    else
    {
      $text | Out-File $outfile -Encoding UTF8
    }
  }
}

$script:context = new-object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$pwd = convertto-securestring $password -AsPlainText -Force
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $pwd)
$script:context.Credentials = $credentials

$wfsm = new-object Microsoft.SharePoint.Client.WorkflowServices.WorkflowServicesManager($script:context, $script:context.Web)
$script:wfss = $wfsm.GetWorkflowSubscriptionService();
$script:wfsubhash = @{}
$script:wfis = $wfsm.GetWorkflowInstanceService();

$list = $script:context.Web.Lists.GetByTitle($listName)
$script:context.Load($list)
ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000

$wfSubs = $wfss.EnumerateSubscriptionsByList($list.Id);
$script:context.Load($wfSubs);
ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000

WriteOut -text "ItemId,WorkflowName,Status" 
EnumWorkflowsInFolder -List $list -serverRelativeUrl $null

 

2) PowerShell  を起動します。
3) スクリプトを配置したフォルダーに移動し、作成した .ps1 ファイルを以下のように実行します。

 .\getwfstatus.ps1 -siteUrl https://tenant.sharepoint.com/sites/workflowsite -listName ドキュメント -username account@tenant.onmicrosoft.com -password password

パラメータ

-siteUrl ・・・ サイトのアドレス
-listName ・・・ リスト名
-username ・・・ 処理を実行するユーザー
-password ・・・ 上記ユーザーのパスワード
-outfile ・・・ 出力先ファイル (省略可 : 既定値はコンソール出力)
-resume ・・・ $true の場合 一旦停止したワークフローを強制開始 (省略可 : 既定値 $false)

補足

  • リスト ビューのしきい値と、HTTP 調整対策の内、増分バックオフ リトライに対応しています。
  • 大規模なリストに対して、本スクリプトを実行する場合、HTTP 要求数が増える可能性があります。万が一のことを想定し、オフピークの時間帯に実行してください。

今回の投稿は以上です。

本情報の内容は、作成日時点でのものであり、予告なく変更される場合があります。