Use PowerShell and Visual Studio Code with the Dataverse Web API
This article expands on the Quick Start Web API with PowerShell article to describe advanced capabilities using PowerShell and Visual Studio Code with the Dataverse Web API to:
- Create reusable functions
- Handle exceptions
- Manage Dataverse service protection limits
- Debug using Fiddler
- Download the Dataverse Web API CSDL $metadata document
Note
The instructions in this article should work for Windows, Linux, and macOS, but these steps have only been tested on Windows. If changes are needed, please let us know using the Feedback section at the bottom of this article.
Prerequisites
The content of this article has the same prerequisites as the Quick Start Web API with PowerShell article.
Install or verify that the following are installed
Install Visual Studio Code. See Download Visual Studio Code
Install the PowerShell extension for Visual Studio Code. See PowerShell for Visual Studio Code
Install PowerShell 7.4 or higher. See Install PowerShell on Windows, Linux, and macOS
Install the Az PowerShell module version 11.1.0 or higher. See How to install Azure PowerShell
To update an existing installation to the latest version, use
Update-Module -Name Az -Force
Verify installation
Open Visual Studio Code.
In the Terminal menu, select New Terminal.
In Visual Studio Code navigation pane, select the icon for the PowerShell extension.
Copy and paste the following script in the Visual Studio Code terminal window:
Write-Host 'PowerShell Version:'$PSVersionTable.PSVersion.ToString() Write-Host 'PowerShell Az version:'(Get-InstalledModule Az).Version
Press Enter. The output should resemble the following:
PowerShell Version: 7.4.0 PowerShell Az version: 11.1.0
If you don't see results like this, install or update the prerequisites.
You'll also need
- A valid user account for a Dataverse environment
- The Url to the Dataverse environment you want to connect to. See View developer resources to learn how to find it. It looks something like this:
https://yourorg.crm.dynamics.com/
, whereyourorg.crm
is different. - Basic understanding of the PowerShell scripting language
Create reusable functions
Quick Start Web API with PowerShell introduced how to authenticate and call the WhoAmI function with Visual Studio Code. This approach might be all you need to for an ad-hoc test of one or more operations. However, as your scripts become more complex, you might find yourself typing the same code again and again.
In this section, we start creating a set of reusable functions in separate files that we can access using dot sourcing. Use dot sourcing to load a file containing PowerShell scripts that can contain functions and variables that become part of the local script scope.
Tip
You can find fully documented definitions of these functions and more in our GitHub PowerApps-Samples repo at PowerApps-Samples/dataverse/webapi/PS/
Create a Connect function
Let's put the code to authenticate to Dataverse in a function called Connect
inside a file named Core.ps1
so we can reuse it in a single line of code.
Create a folder. In this example, we create a folder in
C:\scripts
.Create a text file in the scripts folder named
Core.ps1
.Copy and paste the following
Connect
function into theCore.ps1
file.function Connect { param ( [Parameter(Mandatory)] [String] $uri ) ## Login interactively if not already logged in if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) { Connect-AzAccount | Out-Null } # Get an access token $token = (Get-AzAccessToken -ResourceUrl $uri).Token # Define common set of headers $global:baseHeaders = @{ 'Authorization' = 'Bearer ' + $token 'Accept' = 'application/json' 'OData-MaxVersion' = '4.0' 'OData-Version' = '4.0' } # Set baseURI $global:baseURI = $uri + 'api/data/v9.2/' }
Note
The script adds the
baseURI
andbaseHeaders
variables to the global context using the$global
scope modifier so that they are available to other scripts in the same session.Create another text file using Visual Studio Code named
test.ps1
in yourscripts
folder.Copy and paste the following script into the
test.ps1
file:. $PSScriptRoot\Core.ps1 Connect 'https://yourorg.crm.dynamics.com/' # change this # Invoke WhoAmI Function Invoke-RestMethod -Uri ($baseURI + 'WhoAmI') -Method Get -Headers $baseHeaders | ConvertTo-Json
. $PSScriptRoot\Core.ps1
at the top of the file shows using dot sourcing to direct the script to load the contents of that file.Remember to change the
https://yourorg.crm.dynamics.com/
value to match the URL for your environment.Press F5 to run the script.
The output should look something like this:
PS C:\scripts> . 'C:\scripts\test.ps1' { "@odata.context": "https://yourorg.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse", "BusinessUnitId": "3a277578-5996-ee11-be36-002248227994", "UserId": "2c2e7578-5996-ee11-be36-002248227994", "OrganizationId": "97bf0e8b-aa99-ee11-be32-000d3a106c3a" }
Create a WhoAmI function
Let's put the code to invoke the WhoAmI function in a function called Get-WhoAmI
inside a file named CommonFunctions.ps1
so we can type just 11 characters rather than 100 each time you want to use the WhoAmI function
Create a new text file named
CommonFunctions.ps1
in yourscripts
folder.Copy and paste the following function definition in the
CommonFunctions.ps1
.function Get-WhoAmI{ $WhoAmIRequest = @{ Uri = $baseURI + 'WhoAmI' Method = 'Get' Headers = $baseHeaders } Invoke-RestMethod @WhoAmIRequest }
Note
This function definition uses a technique called splatting. Splatting makes your commands shorter and easier to read because it passes a collection of parameter values to a command as a unit.
Save the
CommonFunctions.ps1
file.Edit the
test.ps1
file, change the content to look like the following script:. $PSScriptRoot\Core.ps1 . $PSScriptRoot\CommonFunctions.ps1 Connect 'https://yourorg.crm.dynamics.com/' # change this # Invoke WhoAmI Function Get-WhoAmI | ConvertTo-Json
Remember to change the
https://yourorg.crm.dynamics.com/
value to match the URL for your environment.Press F5 to run the script.
The output should look exactly like it did before.
Create table operations functions
Let's put functions to perform common table operations a file named TableOperations.ps1
so we can reuse them.
Create a new text file named
TableOperations.ps1
in yourscripts
folder.Copy and paste the following function definitions in the
TableOperations.ps1
.function Get-Records { param ( [Parameter(Mandatory)] [String] $setName, [Parameter(Mandatory)] [String] $query ) $uri = $baseURI + $setName + $query # Header for GET operations that have annotations $getHeaders = $baseHeaders.Clone() $getHeaders.Add('If-None-Match', $null) $getHeaders.Add('Prefer', 'odata.include-annotations="*"') $RetrieveMultipleRequest = @{ Uri = $uri Method = 'Get' Headers = $getHeaders } Invoke-RestMethod @RetrieveMultipleRequest } function New-Record { param ( [Parameter(Mandatory)] [String] $setName, [Parameter(Mandatory)] [hashtable] $body ) $postHeaders = $baseHeaders.Clone() $postHeaders.Add('Content-Type', 'application/json') $CreateRequest = @{ Uri = $baseURI + $setName Method = 'Post' Headers = $postHeaders Body = ConvertTo-Json $body } Invoke-RestMethod @CreateRequest -ResponseHeadersVariable rh | Out-Null $url = $rh['OData-EntityId'] $selectedString = Select-String -InputObject $url -Pattern '(?<=\().*?(?=\))' return [System.Guid]::New($selectedString.Matches.Value.ToString()) } function Get-Record { param ( [Parameter(Mandatory)] [String] $setName, [Parameter(Mandatory)] [Guid] $id, [String] $query ) $uri = $baseURI + $setName $uri = $uri + '(' + $id.Guid + ')' + $query $getHeaders = $baseHeaders.Clone() $getHeaders.Add('If-None-Match', $null) $getHeaders.Add('Prefer', 'odata.include-annotations="*"') $RetrieveRequest = @{ Uri = $uri Method = 'Get' Headers = $getHeaders } Invoke-RestMethod @RetrieveRequest } function Update-Record { param ( [Parameter(Mandatory)] [String] $setName, [Parameter(Mandatory)] [Guid] $id, [Parameter(Mandatory)] [hashtable] $body ) $uri = $baseURI + $setName $uri = $uri + '(' + $id.Guid + ')' # Header for Update operations $updateHeaders = $baseHeaders.Clone() $updateHeaders.Add('Content-Type', 'application/json') $updateHeaders.Add('If-Match', '*') # Prevent Create $UpdateRequest = @{ Uri = $uri Method = 'Patch' Headers = $updateHeaders Body = ConvertTo-Json $body } Invoke-RestMethod @UpdateRequest } function Remove-Record { param ( [Parameter(Mandatory)] [String] $setName, [Parameter(Mandatory)] [Guid] $id ) $uri = $baseURI + $setName $uri = $uri + '(' + $id.Guid + ')' $DeleteRequest = @{ Uri = $uri Method = 'Delete' Headers = $baseHeaders } Invoke-RestMethod @DeleteRequest }
For information about how to compose these requests, see the following articles:
Save the
TableOperations.ps1
file.Copy the following code and paste it into the
test.ps1
file.. $PSScriptRoot\Core.ps1 . $PSScriptRoot\CommonFunctions.ps1 . $PSScriptRoot\TableOperations.ps1 Connect 'https://yourorg.crm.dynamics.com/' # change this # Retrieve Records Write-Host 'Retrieve first three account records:' (Get-Records ` -setName accounts ` -query '?$select=name&$top=3').value | Format-Table -Property name, accountid # Create a record Write-Host 'Create an account record:' $newAccountID = New-Record ` -setName accounts ` -body @{ name = 'Example Account'; accountcategorycode = 1 # Preferred } Write-Host "Account with ID $newAccountID created" # Retrieve a record Write-Host 'Retrieve the created record:' Get-Record ` -setName accounts ` -id $newAccountID.Guid '?$select=name,accountcategorycode' | Format-List -Property name, accountid, accountcategorycode, accountcategorycode@OData.Community.Display.V1.FormattedValue # Update a record Write-Host 'Update the record:' $updateAccountData = @{ name = 'Updated Example account'; accountcategorycode = 2; #Standard } Update-Record ` -setName accounts ` -id $newAccountID.Guid ` -body $updateAccountData Write-Host 'Retrieve the updated the record:' Get-Record ` -setName accounts ` -id $newAccountID.Guid ` -query '?$select=name,accountcategorycode' | Format-List -Property name, accountid, accountcategorycode, accountcategorycode@OData.Community.Display.V1.FormattedValue # Delete a record Write-Host 'Delete the record:' Remove-Record ` -setName accounts ` -id $newAccountID.Guid Write-Host "The account with ID $newAccountID was deleted"
Remember to change the
https://yourorg.crm.dynamics.com/
value to match the URL for your environment.Press F5 to run the script.
The output should look something like this:
PS C:\scripts> . 'C:\scripts\test.ps1' Retrieve first three account records: name accountid ---- --------- Fourth Coffee (sample) d2382248-cd99-ee11-be37-000d3a9b7981 Litware, Inc. (sample) d4382248-cd99-ee11-be37-000d3a9b7981 Adventure Works (sample) d6382248-cd99-ee11-be37-000d3a9b7981 Create an account record: Account with ID a2c3ebc2-39a8-ee11-be37-000d3a8e8e07 created Retrieve the created record: name : Example Account accountid : a2c3ebc2-39a8-ee11-be37-000d3a8e8e07 accountcategorycode : 1 accountcategorycode@OData.Community.Display.V1.FormattedValue : Preferred Customer Update the record: Retrieve the updated the record: name : Updated Example account accountid : a2c3ebc2-39a8-ee11-be37-000d3a8e8e07 accountcategorycode : 2 accountcategorycode@OData.Community.Display.V1.FormattedValue : Standard Delete the record: The account with ID a2c3ebc2-39a8-ee11-be37-000d3a8e8e07 was deleted
Handle exceptions
So far in this article you have been copying and pasting code provided for you. But when you start writing your own functions and using them, you'll encounter errors. When these errors occur, they might be from Dataverse, or they could be from your script.
Let's add helper function that can help detect the source of the errors and extract relevant details from errors returned by Dataverse.
Edit the
Core.ps1
file to add the followingInvoke-DataverseCommands
function:function Invoke-DataverseCommands { param ( [Parameter(Mandatory)] $commands ) try { Invoke-Command $commands } catch [Microsoft.PowerShell.Commands.HttpResponseException] { Write-Host "An error occurred calling Dataverse:" -ForegroundColor Red $statuscode = [int]$_.Exception.StatusCode; $statusText = $_.Exception.StatusCode Write-Host "StatusCode: $statuscode ($statusText)" # Replaces escaped characters in the JSON [Regex]::Replace($_.ErrorDetails.Message, "\\[Uu]([0-9A-Fa-f]{4})", {[char]::ToString([Convert]::ToInt32($args[0].Groups[1].Value, 16))} ) } catch { Write-Host "An error occurred in the script:" -ForegroundColor Red $_ } }
The
Invoke-DataverseCommands
function uses the Invoke-Command cmdlet to process a set of commands within a try/catch block. Any errors returned from Dataverse are HttpResponseException errors, so the firstcatch
block writes aAn error occurred calling Dataverse:
message to the terminal with the JSON error data.The JSON data in
$_.ErrorDetails.Message
contains some escaped unicode characters. For example:\u0026
instead of&
and\u0027
instead of'
. This function includes some code that replaces those characters with the unescaped characters so that they exactly match errors you see elsewhere.Otherwise, the errors are written back to the terminal window with a message:
An error occurred in the script:
Save the
Core.ps1
file.Edit the
test.ps1
file to use the following script that uses an invalidsetName
parameter value.account
should beaccounts
. This is a common error. $PSScriptRoot\Core.ps1 . $PSScriptRoot\CommonFunctions.ps1 . $PSScriptRoot\TableOperations.ps1 Connect 'https://yourorg.crm.dynamics.com/' # change this Invoke-DataverseCommands { # Retrieve Records Write-Host 'Retrieve first three account records:' (Get-Records ` -setName account ` -query '?$select=name&$top=3').value | Format-Table -Property name, accountid }
Remember to change the
https://yourorg.crm.dynamics.com/
value to match the URL for your environment.Press F5 to run the script.
The output should look something like this:
PS C:\scripts> . 'C:\scripts\test.ps1' Retrieve first three account records: An error occurred calling Dataverse: StatusCode: 404 (NotFound) { "error": { "code": "0x80060888", "message": "Resource not found for the segment 'account'." } }
Edit the
test.ps1
file to throw a script error within theInvoke-DataverseCommands
block:Invoke-DataverseCommands { throw 'A script error' }
Press F5 to run the script.
The output should be almost the same as if it wasn't included in the
Invoke-DataverseCommands
block:PS C:\scripts> . 'C:\scripts\test.ps1' An error occurred in the script: Exception: C:\scripts\test.ps1:8:4 Line | 8 | throw 'A script error' | ~~~~~~~~~~~~~~~~~~~~~~ | A script error
Manage Dataverse service protection limits
Dataverse Service protection API limits help ensure that Dataverse provides consistent availability and performance. When client applications make extraordinary demands on server resources using the Web API, Dataverse returns 429 Too Many Requests errors and client application must pause operations for the duration specified in the Retry-After header.
The PowerShell Invoke-RestMethod cmdlet MaximumRetryCount parameter specifies how many times PowerShell retries a request when a failure code is between 400 and 599, inclusive or 304 is received. This means PowerShell retries Dataverse service protection 429 errors when you include a value for this parameter. The MaximumRetryCount
parameter can be used with the RetryIntervalSec to specify the number of seconds to wait. The default is 5 seconds. If the error response includes a Retry-After
header for a 429 error, as Dataverse service protection errors do, that value is used instead.
You might never encounter a service protection limit error while you're learning how to use the Dataverse Web API with PowerShell. Scripts you write might be used to send the large number of requests necessary to encounter these errors, so you should know they can occur and how you can manage them using PowerShell.
If you add the MaximumRetryCount
parameter to every Dataverse call using Invoke-RestMethod
, PowerShell retries a broad range of errors. Retrying every error makes your scripts slow, especially when developing and testing. You would need to wait 10 to 15 seconds each time an error occurs, depending on how many retries you specify. An alternative approach is to encapsulate the Invoke-RestMethod
in your own method that manages retries for specific errors.
The following Invoke-ResilientRestMethod
function takes a request
hashtable object as a mandatory parameter and a boolean returnHeader
flag to indicate whether to return the response header or not. When $returnHeader
is true, it sends the request using the the Invoke-RestMethod
command with the ResponseHeadersVariable parameter to capture the headers returned, and uses Out-Null so the output that represents the empty response body isn't returned with the the function. Otherwise, the function sends the request using Invoke-RestMethod
using the request
object and returns the response body.
If the Invoke-RestMethod
fails with a 429 error, it checks if the request
object has a MaximumRetryCount
property. If not, it adds one with a value of 3. It then retries the Invoke-RestMethod
using the request object and the Retry-After
response header value. If the returnHeader
flag is true, it returns the response header. If the Invoke-RestMethod
fails with any other error, it rethrows the exception.
function Invoke-ResilientRestMethod {
param (
[Parameter(Mandatory)]
$request,
[bool]
$returnHeader
)
try {
if ($returnHeader) {
Invoke-RestMethod @request -ResponseHeadersVariable rhv | Out-Null
return $rhv
}
Invoke-RestMethod @request
}
catch [Microsoft.PowerShell.Commands.HttpResponseException] {
$statuscode = $_.Exception.Response.StatusCode
# 429 errors only
if ($statuscode -eq 'TooManyRequests') {
if (!$request.ContainsKey('MaximumRetryCount')) {
$request.Add('MaximumRetryCount', 3)
# Don't need - RetryIntervalSec
# When the failure code is 429 and the response includes the Retry-After property in its headers,
# the cmdlet uses that value for the retry interval, even if RetryIntervalSec is specified
}
# Will attempt retry up to 3 times
if ($returnHeader) {
Invoke-RestMethod @request -ResponseHeadersVariable rhv | Out-Null
return $rhv
}
Invoke-RestMethod @request
}
else {
throw $_
}
}
catch {
throw $_
}
}
You can use a function like this in your reusable functions. When functions need to return values from the header of the response, they need to set the returnHeader
value to $true
. For example, the following New-Record
function modifies the example function in Create table operations functions to use Invoke-ResilientRestMethod
instead of Invoke-RestMethod
directly.
function New-Record {
param (
[Parameter(Mandatory)]
[String]
$setName,
[Parameter(Mandatory)]
[hashtable]
$body
)
$postHeaders = $baseHeaders.Clone()
$postHeaders.Add('Content-Type', 'application/json')
$CreateRequest = @{
Uri = $environmentUrl + 'api/data/v9.2/' + $setName
Method = 'Post'
Headers = $postHeaders
Body = ConvertTo-Json $body
}
# Before:
# Invoke-RestMethod @CreateRequest -ResponseHeadersVariable rh | Out-Null
# After:
$rh = Invoke-ResilientRestMethod -request $CreateRequest -returnHeader $true
$url = $rh['OData-EntityId']
$selectedString = Select-String -InputObject $url -Pattern '(?<=\().*?(?=\))'
return [System.Guid]::New($selectedString.Matches.Value.ToString())
}
Otherwise, Invoke-ResilientRestMethod
can replace the Invoke-RestMethod
as shown in this Get-Record
example:
function Get-Record {
param (
[Parameter(Mandatory)]
[String]
$setName,
[Parameter(Mandatory)]
[Guid]
$id,
[String]
$query
)
$uri = $environmentUrl + 'api/data/v9.2/' + $setName
$uri = $uri + '(' + $id.Guid + ')' + $query
$getHeaders = $baseHeaders.Clone()
$getHeaders.Add('If-None-Match', $null)
$getHeaders.Add('Prefer', 'odata.include-annotations="*"')
$RetrieveRequest = @{
Uri = $uri
Method = 'Get'
Headers = $getHeaders
}
# Before:
# Invoke-RestMethod @RetrieveRequest
# After:
Invoke-ResilientRestMethod $RetrieveRequest
}
The only difference is that you pass the hashtable ($RetrieveRequest
) to the method instead of using splatting (@RetrieveRequest
). Otherwise you'll get a script error: A parameter cannot be found that matches parameter name 'Headers'.
Debug using Fiddler
Fiddler is a web debugging proxy used to view HTTP traffic on your computer. Viewing this data is useful when debugging scripts. By default, HTTP requests and responses sent using Invoke-RestMethod cmdlet won't be visible using Fiddler.
To view HTTP traffic using Fiddler, set the Invoke-RestMethod
Proxy parameter to the URL configured as the Fiddler proxy on your local computer. By default, the URL is http://127.0.0.1:8888
. Yours may be different.
For example, if you invoke the WhoAmI function with the -Proxy
parameter set while Fiddler is capturing traffic:
Invoke-RestMethod `
-Uri ($environmentUrl + 'api/data/v9.2/WhoAmI') `
-Method Get `
-Headers $baseHeaders `
-Proxy 'http://127.0.0.1:8888'
In Fiddler, you'll be able to see all the details:
GET https://yourorg.api.crm.dynamics.com/api/data/v9.2/WhoAmI HTTP/1.1
Host: yourorg.api.crm.dynamics.com
OData-MaxVersion: 4.0
Accept: application/json
Authorization: Bearer [REDACTED]
OData-Version: 4.0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.22631; en-US) PowerShell/7.4.0
Accept-Encoding: gzip, deflate, br
HTTP/1.1 200 OK
Cache-Control: no-cache
Allow: OPTIONS,GET,HEAD,POST
Content-Type: application/json; odata.metadata=minimal
Expires: -1
Vary: Accept-Encoding
x-ms-service-request-id: 7341c0c1-3343-430b-98ea-292567ed4776
Set-Cookie: ARRAffinity=f60cbee43b7af0a5f322e7ce57a018546ed978f67f0c11cbb5e15b02ddb091a915134d20c556b0b34b9b6ae43ec3f5dcdad61788de889ffc592af7aca85fc1c508DC0FC94CB062A12107345846; path=/; secure; HttpOnly
Set-Cookie: ReqClientId=4fc95009-0b3d-4a19-b223-0d80745636ac; expires=Sun, 07-Jan-2074 21:10:42 GMT; path=/; secure; HttpOnly
Set-Cookie: orgId=648e8efd-db86-466e-a5bc-a4d5eb9c52d4; expires=Sun, 07-Jan-2074 21:10:42 GMT; path=/; secure; HttpOnly
x-ms-service-request-id: 1ee13aa7-47f3-4a75-95fa-2916775a1f79
Strict-Transport-Security: max-age=31536000; includeSubDomains
REQ_ID: 1ee13aa7-47f3-4a75-95fa-2916775a1f79
CRM.ServiceId: framework
AuthActivityId: 0b562cc3-56f6-44f0-a26e-4039cfc4be6a
x-ms-dop-hint: 48
x-ms-ratelimit-time-remaining-xrm-requests: 1,200.00
x-ms-ratelimit-burst-remaining-xrm-requests: 5999
OData-Version: 4.0
X-Source: 110212218438874147222728177124203420477168182861012399121919014511175711948418152
Public: OPTIONS,GET,HEAD,POST
Set-Cookie: ARRAffinity=f60cbee43b7af0a5f322e7ce57a018546ed978f67f0c11cbb5e15b02ddb091a915134d20c556b0b34b9b6ae43ec3f5dcdad61788de889ffc592af7aca85fc1c508DC0FC94CB062A12107345846; path=/; secure; HttpOnly
X-Source: 2302101791355821068628523819830862152291172232072372448021147103846182145238216119
Date: Sun, 07 Jan 2024 21:10:42 GMT
Content-Length: 277
{"@odata.context":"https://yourorg.api.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse","BusinessUnitId":"1647bf36-e90a-4c4d-9b61-969d57ce7a66","UserId":"24e34f5e-7f1a-43fe-88da-7e4b862d51ad","OrganizationId":"648e8efd-db86-466e-a5bc-a4d5eb9c52d4"}
If Fiddler isn't running, you'll get an error like this:
Invoke-RestMethod: C:\scripts\test.ps1:8:1
Line |
8 | Invoke-RestMethod `
| ~~~~~~~~~~~~~~~~~~~
| No connection could be made because the target machine actively refused it.
If you choose to route all your Invoke-RestMethod
calls through a single function, such as the Invoke-ResilientRestMethod
described in Manage Dataverse service protection limits, you might set some variables in the Core.ps1
file to configuring this option in a single location.
# Set to true only while debugging with Fiddler
$debug = $true
# Set this value to the Fiddler proxy URL configured on your computer
$proxyUrl = 'http://127.0.0.1:8888'
Then, within your centralized function you can set the -Proxy
parameter using splatting with the $request
hash table only when debugging with Fiddler.
function Invoke-ResilientRestMethod {
param (
[Parameter(Mandatory)]
$request,
[bool]
$returnHeader
)
if ($debug) {
$request.Add('Proxy', $proxyUrl)
}
...
Learn about capturing web traffic with Fiddler
Download the Dataverse Web API CSDL $metadata document
The CSDL $metadata is the source of truth about Dataverse Web API capabilities. You can view it in a browser, but you might find it easier to download the file and view it within Visual Studio Code. The following script is a modified version of the script introduced in Quick Start Web API with PowerShell. The difference is that it uses the Invoke-WebRequest cmdlet, which is more appropriate for downloading an XML document.
$environmentUrl = 'https://yourorg.crm.dynamics.com/' # change this
$writeFileTo = 'C:\temp\yourorg.xml' # change this
## Login if not already logged in
if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
Connect-AzAccount | Out-Null
}
# Get an access token
$token = (Get-AzAccessToken -ResourceUrl $environmentUrl).Token
# Common headers
$xmlHeaders = @{
'Authorization' = 'Bearer ' + $token
'Accept' = 'application/xml'
'OData-MaxVersion' = '4.0'
'OData-Version' = '4.0'
}
$doc = [xml](Invoke-WebRequest `
-Uri ($environmentUrl + 'api/data/v9.2/$metadata?annotations=true') `
-Method 'Get' `
-Headers $xmlHeaders ).Content
$StringWriter = New-Object System.IO.StringWriter
$XmlWriter = New-Object System.XMl.XmlTextWriter $StringWriter
$xmlWriter.Formatting = 'indented'
$xmlWriter.Indentation = 2
$doc.WriteContentTo($XmlWriter)
$XmlWriter.Flush()
$StringWriter.Flush()
Set-Content -Path $writeFileTo -Value $StringWriter.ToString()
code $writeFileTo
- Copy the script.
- Edit the
$environmentUrl
and$writeFileTo
variables to match your need. - Run the script in Visual Studio Code.
The Dataverse Web API CSDL $metadata document will open in Visual Studio code.
You'll probably get a notification saying:
For performance reasons, document symbols have been limited to 5000 items. If a new limit is set, please close and reopen this file to recompute document symbols.
The notification gives the option to change the Visual Studio XML extension xml.symbols.maxItemsComputed
limit. For most Dataverse Web API CSDL $metadata documents, setting the limit to 500000 should be sufficient.
Troubleshooting
This section contains some guidance for issues you might encounter.
Error dialog: connect ENOENT\\.\pipe\<RANDOM_text> with Open 'launch.json' button
This error might occur at times when debugging using Visual Studio Code. To resolve:
- Select View > Command Palette... from the Visual Studio Code menu, or press Ctrl+Shift+P.
- Type
restart
and selectPowershell: Restart session
. See PowerShell/vscode-powershell GitHub Issue 4332 for more information.
Next steps
Learn more about Dataverse Web API capabilities by understanding the service documents.
Review and run sample code.
Feedback
https://aka.ms/ContentUserFeedback.
Binnenkort: Gedurende 2024 worden GitHub Issues uitgefaseerd als het feedbackmechanisme voor inhoud. Dit wordt vervangen door een nieuw feedbacksysteem. Ga voor meer informatie naar:Feedback verzenden en bekijken voor