Home
%3CLINGO-SUB%20id%3D%22lingo-sub-177998%22%20slang%3D%22en-US%22%3EDecorating%20CSOM%20calls%20in%20PowerShell%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-177998%22%20slang%3D%22en-US%22%3E%3CP%3ETo%20ensure%20service%20availability%20and%20maintain%20service%20health%2C%20some%20HTTP%20requests%20may%20be%20prioritized%20over%20others.%20Our%20documentation%20recommends%20decorating%20the%20%3CEM%3EUser-Agent%20%3C%2FEM%3Eheader%20of%20the%20HTTP%20request%20as%20a%20design%20practice%20for%20reducing%20or%20avoiding%20SPO%20request%20throttling%20(%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsharepoint%2Fdev%2Fgeneral-development%2Fhow-to-avoid-getting-throttled-or-blocked-in-sharepoint-online%23how-to-decorate-your-http-traffic-to-avoid-throttling%22%20target%3D%22_blank%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%22%3Elink%3C%2FA%3E).%20Although%20there%20are%20many%20examples%20of%20decorating%20CSOM%20calls%20in%20the%20context%20of%20a%20C%23%20application%2C%20this%20article%20demonstrates%20how%20to%20decorate%20CSOM%20calls%20within%20a%20PowerShell%20script.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH1%20id%3D%22toc-hId-1819147624%22%20id%3D%22toc-hId-1902050920%22%3E%3CSTRONG%3ESetting%20%3CEM%3EUser-Agent%20%3C%2FEM%3Eat%20runtime%3C%2FSTRONG%3E%3C%2FH1%3E%0A%3CP%3EEach%20call%20to%20the%20%3CEM%3EClientRuntimeContext.ExecuteQuery%3C%2FEM%3E%20method%20triggers%20an%20%3CEM%3EExecutingWebRequest%20%3C%2FEM%3Eevent.%20When%20your%20script%20first%20constructs%20the%20%3CEM%3EClientRuntimeContext%20%3C%2FEM%3Eobject%2C%20you%20can%20pass%20a%20function%20to%20%3CEM%3EClientRuntimeContext.add_ExecutingWebRequest%3C%2FEM%3E%20method.%20The%20function%20passed%20will%20be%20called%20at%20runtime%20as%20an%20event%20handler%20for%20the%20%3CEM%3EExecutingWebRequest%20%3C%2FEM%3Eevent.%20This%20event%20handler%20sets%20the%20%3CEM%3EUser-Agent%20%3C%2FEM%3Eheader%20of%20the%20HTTP%20request.%20The%20following%20code%20is%20adapted%20from%20%3CA%20href%3D%22https%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2FSharePoint-Developer%2FUpdated-Guidance-around-Identity-and-SharePoint-web-service%2Ftd-p%2F119889%22%20target%3D%22_blank%22%3Ehttps%3A%2F%2Ftechcommunity.microsoft.com%2Ft5%2FSharePoint-Developer%2FUpdated-Guidance-around-Identity-and-SharePoint-web-service%2Ftd-p%2F119889%3C%2FA%3E.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CPRE%3E%24ctx.add_ExecutingWebRequest(%7B%0Aparam(%24Source%2C%20%24EventArgs)%0A%24request%20%3D%20%24EventArgs.WebRequestExecutor.WebRequest%0A%24request.UserAgent%20%3D%20%22NONISV%7CCsomPs%7CTestDecorate%2F1.0%22%0A%7D)%0A%24ctx.ExecuteQuery()%0A%3C%2FPRE%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CH1%20id%3D%22toc-hId--733009337%22%20id%3D%22toc-hId--650106041%22%3E%3CSTRONG%3ETesting%20CSOM%20request%20header%20decoration%3C%2FSTRONG%3E%3C%2FH1%3E%0A%3CP%3EThe%20following%20script%20demonstrates%20CSOM%20HTTP%20request%20header%20decoration.%20The%20script%20reads%20and%20prints%20the%20title%20of%20the%20given%20Web%20multiple%20times.%20Each%20iteration%20of%20the%20loop%20sleeps%20for%20one%20second%20to%20avoid%20SPO%20service%20throttling.%3C%2FP%3E%0A%3CPRE%3EPARAM%0A(%0A%20%20%20%20%20%20%20%5BParameter(Mandatory%3D%24true)%5D%0A%20%20%20%20%20%20%20%5BString%5D%24WebUrl%0A)%0AAdd-Type%20-Path%20'.%5CMicrosoft.SharePointOnline.CSOM.16.1.7206.1200%5Clib%5Cnet40-full%5CMicrosoft.SharePoint.Client.dll'%0AAdd-Type%20-Path%20'.%5CMicrosoft.SharePointOnline.CSOM.16.1.7206.1200%5Clib%5Cnet40-full%5CMicrosoft.SharePoint.Client.Runtime.dll'%0AImport-Module%20Microsoft.Online.SharePoint.PowerShell%20-DisableNameChecking%0A%24username%20%3D%20Read-Host%20-Prompt%20%22Enter%20or%20paste%20a%20Site%20Owner%20or%20Site%20Collection%20Administrator%20for%20%24(%24WebUrl)%22%0A%24password%20%3D%20Read-Host%20-Prompt%20%22Enter%20Password%20for%20%24(%24username)%22%20-AsSecureString%0A%24ctx%20%3D%20New-Object%20Microsoft.SharePoint.Client.ClientContext(%24WebUrl)%0A%24ctx.Credentials%20%3D%20New-Object%20Microsoft.SharePoint.Client.SharePointOnlineCredentials(%24username%2C%20%24password)%0A%24ctx.add_ExecutingWebRequest(%7B%0Aparam(%24Source%2C%20%24EventArgs)%0A%24request%20%3D%20%24EventArgs.WebRequestExecutor.WebRequest%0A%24request.UserAgent%20%3D%20%22NONISV%7CCsomPs%7CTestDecorate%2F1.0%22%0A%7D)%0A%24ctx.ExecuteQuery()%0A1..5%20%7C%20%25%7B%0A%20%20%20%24web%20%3D%20%24ctx.Web%0A%20%20%20%24ctx.Load(%24web)%0A%20%20%20%24ctx.ExecuteQuery()%0A%20%20%20%24title%20%3D%20%24web.Title%0A%20%20%20Write-Host%20%22Iteration%20%24(%24_)%20-%20Web%20Title%3A%20%24(%24web.Title)%22%0A%20%20%20Start-Sleep%20-Milliseconds%201000%0A%7D%3C%2FPRE%3E%0A%3CP%3ERecording%20a%20Fiddler%20trace%20while%20the%20script%20runs%20shows%20the%20decorated%20%3CEM%3EUser-Agent%20%3C%2FEM%3Erequest%20header.%3C%2FP%3E%0A%3CP%3E%3CSPAN%20class%3D%22lia-inline-image-display-wrapper%20lia-image-align-inline%22%20style%3D%22width%3A%20429px%3B%22%3E%3CIMG%20src%3D%22https%3A%2F%2Fgxcuf89792.i.lithium.com%2Ft5%2Fimage%2Fserverpage%2Fimage-id%2F31282i81C71BC8041E8F73%2Fimage-size%2Flarge%3Fv%3D1.0%26amp%3Bpx%3D999%22%20alt%3D%22ft.png%22%20title%3D%22ft.png%22%20%2F%3E%3C%2FSPAN%3E%3C%2FP%3E%0A%3CH1%20id%3D%22toc-hId-1009800998%22%20id%3D%22toc-hId-1092704294%22%3E%3CSTRONG%3EFurther%20guidance%3C%2FSTRONG%3E%3C%2FH1%3E%0A%3CP%3EThe%20HTTP%20protocol%20has%20no%20mechanism%20for%20verifying%20the%20%3CEM%3EUser-Agent%20%3C%2FEM%3Eheader%2C%20so%20this%20header%20can%20be%20set%20to%20any%20value%20that%20conforms%20to%20%3CA%20href%3D%22https%3A%2F%2Fwww.w3.org%2FProtocols%2Frfc2616%2Frfc2616-sec14.html%22%20target%3D%22_blank%22%20rel%3D%22nofollow%20noopener%20noreferrer%20noopener%20noreferrer%22%3Ehttps%3A%2F%2Fwww.w3.org%2FProtocols%2Frfc2616%2Frfc2616-sec14.html%3C%2FA%3E.%3C%2FP%3E%0A%3CP%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%3ETo%20avoid%20throttling%2C%20it%E2%80%99s%20best%20to%20format%20the%20User-Agent%20in%20accordance%20with%20our%20published%20guidance%20linked%20above.%20In%20addition%20to%20decorating%20HTTP%20traffic%2C%20adding%20%3CEM%3EStart-Sleep%3C%2FEM%3E%20statements%20helps%20to%20ensure%20a%20steady-state%20request%20rate%20of%201%20request%20per%20second.%20It%20may%20be%20helpful%20to%20pair%20%3CEM%3EExecuteQuery%20%3C%2FEM%3Ecalls%20with%20%3CEM%3EStart-Sleep%20-Milliseconds%201000%3C%2FEM%3E%20statements%2C%20especially%20if%20calling%20%3CEM%3EExecuteQuery%3C%2FEM%3E%20in%20a%20loop.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-TEASER%20id%3D%22lingo-teaser-177998%22%20slang%3D%22en-US%22%3E%3CP%3ETo%20ensure%20service%20availability%20and%20maintain%20service%20health%2C%20some%20HTTP%20requests%20may%20be%20prioritized%20over%20others.%20Our%20documentation%20recommends%20decorating%20the%20%3CEM%3EUser-Agent%20%3C%2FEM%3Eheader%20of%20the%20HTTP%20request%20as%20a%20design%20practice%20for%20reducing%20or%20avoiding%20SPO%20request%20throttling%20(%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fsharepoint%2Fdev%2Fgeneral-development%2Fhow-to-avoid-getting-throttled-or-blocked-in-sharepoint-online%23how-to-decorate-your-http-traffic-to-avoid-throttling%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%22%20target%3D%22_blank%22%3Elink%3C%2FA%3E).%20Although%20there%20are%20many%20examples%20of%20decorating%20CSOM%20calls%20in%20the%20context%20of%20a%20C%23%20application%2C%20this%20article%20demonstrates%20how%20to%20decorate%20CSOM%20calls%20within%20a%20PowerShell%20script.%3C%2FP%3E%3C%2FLINGO-TEASER%3E
Microsoft

To ensure service availability and maintain service health, some HTTP requests may be prioritized over others. Our documentation recommends decorating the User-Agent header of the HTTP request as a design practice for reducing or avoiding SPO request throttling (link). Although there are many examples of decorating CSOM calls in the context of a C# application, this article demonstrates how to decorate CSOM calls within a PowerShell script.

 

Setting User-Agent at runtime

Each call to the ClientRuntimeContext.ExecuteQuery method triggers an ExecutingWebRequest event. When your script first constructs the ClientRuntimeContext object, you can pass a function to ClientRuntimeContext.add_ExecutingWebRequest method. The function passed will be called at runtime as an event handler for the ExecutingWebRequest event. This event handler sets the User-Agent header of the HTTP request. The following code is adapted from https://techcommunity.microsoft.com/t5/SharePoint-Developer/Updated-Guidance-around-Identity-and-Sha....

 

$ctx.add_ExecutingWebRequest({
param($Source, $EventArgs)
$request = $EventArgs.WebRequestExecutor.WebRequest
$request.UserAgent = "NONISV|CsomPs|TestDecorate/1.0"
})
$ctx.ExecuteQuery()

 

Testing CSOM request header decoration

The following script demonstrates CSOM HTTP request header decoration. The script reads and prints the title of the given Web multiple times. Each iteration of the loop sleeps for one second to avoid SPO service throttling.

PARAM
(
       [Parameter(Mandatory=$true)]
       [String]$WebUrl
)
Add-Type -Path '.\Microsoft.SharePointOnline.CSOM.16.1.7206.1200\lib\net40-full\Microsoft.SharePoint.Client.dll'
Add-Type -Path '.\Microsoft.SharePointOnline.CSOM.16.1.7206.1200\lib\net40-full\Microsoft.SharePoint.Client.Runtime.dll'
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
$username = Read-Host -Prompt "Enter or paste a Site Owner or Site Collection Administrator for $($WebUrl)"
$password = Read-Host -Prompt "Enter Password for $($username)" -AsSecureString
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($WebUrl)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
$ctx.add_ExecutingWebRequest({
param($Source, $EventArgs)
$request = $EventArgs.WebRequestExecutor.WebRequest
$request.UserAgent = "NONISV|CsomPs|TestDecorate/1.0"
})
$ctx.ExecuteQuery()
1..5 | %{
   $web = $ctx.Web
   $ctx.Load($web)
   $ctx.ExecuteQuery()
   $title = $web.Title
   Write-Host "Iteration $($_) - Web Title: $($web.Title)"
   Start-Sleep -Milliseconds 1000
}

Recording a Fiddler trace while the script runs shows the decorated User-Agent request header.

ft.png

Further guidance

The HTTP protocol has no mechanism for verifying the User-Agent header, so this header can be set to any value that conforms to https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html.

 

To avoid throttling, it’s best to format the User-Agent in accordance with our published guidance linked above. In addition to decorating HTTP traffic, adding Start-Sleep statements helps to ensure a steady-state request rate of 1 request per second. It may be helpful to pair ExecuteQuery calls with Start-Sleep -Milliseconds 1000 statements, especially if calling ExecuteQuery in a loop.