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