2018/02/16

Office 365: PowerShell から CSOM で SharePoint Online のリミット (429) を回避して操作する

最近、CSOM を使って SharePoint Online へ接続する際に、SharePoint Online 側の制限が厳しくなったのか、Limit に引っかかり 429 の HTTP Response エラーとなる確率が高くなってきたようです。

回避方法としては、UserAgent を付けるのと、リトライを行う事とのこと。
あくまでも 100% 回避できる訳ではない事に注意が必要です。

SharePoint Online で調整またはブロックを回避する
https://docs.microsoft.com/ja-jp/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online

C#版のサンプルコードは Microsoft から出ている(一部Bugってるが・・・)ものの、両方の対策に対応した PowerShell 版のサンプルが Microsoft から出ていないので、作ったものを自分の備忘を兼ねて書いておきます。

ちなみに、リトライを行う PowerShell のサンプルは Microsoft のサポートの森さんが記載されているので、リトライを行う部分は 森さんのコードをそのまま拝借しています。

PowerShell サンプル : SharePoint Online HTTP 調整 (応答コード : 429) 対策の増分バックオフ リトライ
https://blogs.technet.microsoft.com/sharepoint_support/2016/10/08/powershell-csom-sample-code-for-spo-http-429-incremental-backoff-retry/

以下、PowerShell の Sample コード (DLLのURLは適宜変更してください)
指定したサイト($siteUrl)内のリスト一覧を取得します。
#==========================================

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"

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
      {
        $retryAttempts++;
        throw;
      }
    }
  }
  throw "Maximum retry attempts {0}, have been attempted." -F $retryCount;
}

function AddUserAgent()
{
param([Parameter(Mandatory=$true)][object]$Context)

    $Assemblies = (
        "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll", `
        "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
    )

    $CSharpSource=@"
using System;
using Microsoft.SharePoint.Client;

public static class CSOMContexChanger
{
public static void CSOMAddUserAgent(ClientContext context)
{
            context.ExecutingWebRequest += delegate (object sender, WebRequestEventArgs e)
            {
                e.WebRequestExecutor.WebRequest.UserAgent = "NONISV|Contoso|PowerShellScript/1.0";
            };
}
}
"@

Add-Type -TypeDefinition $CSharpSource -ReferencedAssemblies $Assemblies -Language CSharp
    [CSOMContexChanger]::CSOMAddUserAgent($Context);
}

# Set Site URL
$siteUrl = "https://<TenantName>.sharepoint.com/sites/<SiteName>/"
# Set Credential
$Credentials = Get-Credential

$script:context = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$script:context.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Credentials.UserName,$Credentials.Password)

# Call UserAgent to SPContext Method
AddUserAgent $script:context

$lists = $script:context.Web.Lists
$script:context.Load($lists)

# Call ExecuteQuery with Retry Method
ExecuteQueryWithIncrementalRetry -retryCount 5 -delay 30000

$lists | ForEach-Object{ "Title: {0}" -f $_.Title }

#==========================================

0 件のコメント:

コメントを投稿