今回の投稿では、特定のリストに関連付けられた 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 要求数が増える可能性があります。万が一のことを想定し、オフピークの時間帯に実行してください。
今回の投稿は以上です。
本情報の内容は、作成日時点でのものであり、予告なく変更される場合があります。