Partager via


Utiliser PowerShell et Visual Studio Code avec l’API Web Dataverse

Cet article s’étend sur l’API web de démarrage rapide avec l’article PowerShell pour décrire les fonctionnalités avancées à l’aide de PowerShell et de Visual Studio Code avec l’API Web Dataverse pour :

Note

Les instructions de cet article doivent fonctionner pour Windows, Linux et macOS, mais ces étapes n’ont été testées que sur Windows. Si des modifications sont nécessaires, faites-nous savoir à l’aide de la section Commentaires en bas de cet article.

Prerequisites

Le contenu de cet article présente les mêmes prérequis que l’API web de démarrage rapide avec l’article PowerShell .

Installer ou vérifier que les éléments suivants sont installés

Vérifier l’installation

  1. Ouvrez Visual Studio Code.

  2. Dans le menu Terminal, sélectionnez Nouveau terminal.

  3. Dans le volet de navigation de Visual Studio Code, sélectionnez l’icône de l’extension PowerShell.

  4. Copiez et collez le script suivant dans la fenêtre de terminal Visual Studio Code :

    Write-Host 'PowerShell Version:'$PSVersionTable.PSVersion.ToString()
    Write-Host 'PowerShell Az version:'(Get-InstalledModule Az).Version
    
  5. Appuyez sur Entrée. La sortie doit ressembler à ce qui suit :

    PowerShell Version: 7.4.0
    PowerShell Az version: 11.1.0
    

Si vous ne voyez pas les résultats comme celui-ci, installez ou mettez à jour les prérequis.

Vous aurez également besoin de

  • Un compte d’utilisateur valide pour un environnement Dataverse
  • URL de l’environnement Dataverse auquel vous souhaitez vous connecter. Consultez Afficher les ressources du développeur pour découvrir comment le trouver. Il semble quelque chose comme ceci : https://yourorg.crm.dynamics.com/, où yourorg.crm est différent.
  • Compréhension de base du langage de script PowerShell

Créer des fonctions réutilisables

L’API web de démarrage rapide avec PowerShell a introduit comment authentifier et appeler la fonction WhoAmI avec Visual Studio Code. Cette approche peut être tout ce dont vous avez besoin pour un test ad hoc d’une ou plusieurs opérations. Toutefois, à mesure que vos scripts deviennent plus complexes, vous pouvez vous retrouver à taper le même code à nouveau et à nouveau.

Dans cette section, nous commençons à créer un ensemble de fonctions réutilisables dans des fichiers distincts auxquels nous pouvons accéder à l’aide du dot sourcing. Utilisez le dot-sourcing pour charger un fichier contenant des scripts PowerShell, qui peuvent inclure des fonctions et des variables faisant partie de l'étendue du script local.

Conseil / Astuce

Vous trouverez des définitions entièrement documentées de ces fonctions et bien plus encore dans notre dépôt GitHub PowerApps-Samples sur PowerApps-Samples/dataverse/webapi/PS/

Créer une fonction Connect

Nous allons placer le code pour l’authentification auprès de Dataverse dans une fonction appelée Connect à l’intérieur d’un fichier nommé Core.ps1 afin de pouvoir la réutiliser dans une seule ligne de code.

  1. Créez un dossier. Dans cet exemple, nous créons un dossier dans C:\scripts.

  2. Ouvrez le dossier scripts dans Visual Studio Code.

  3. Créez un fichier texte dans le dossier scripts nommé Core.ps1.

  4. Copiez et collez la fonction suivante Connect dans le Core.ps1 fichier.

    function Connect {
       param (
          [Parameter(Mandatory)] 
          [String] 
          $environmentUrl
       )
    
       ## Login interactively if not already logged in
       if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
          Connect-AzAccount | Out-Null
       }
    
       # Get an access token
       $secureToken = (Get-AzAccessToken `
          -ResourceUrl $environmentUrl `
          -AsSecureString).Token
    
       # Convert the secure token to a string
       $token = ConvertFrom-SecureString `
          -SecureString $secureToken `
          -AsPlainText
    
       # 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 = $environmentUrl + 'api/data/v9.2/'
    }
    

    Note

    Le script ajoute les variables baseURI et baseHeaders au contexte global à l’aide du $globalmodificateur de portée afin qu'elles soient disponibles pour d'autres scripts dans la même session.

  5. Créez un autre fichier texte dans Visual Studio Code nommé test.ps1 dans votre scripts dossier.

  6. Copiez et collez le script suivant dans le test.ps1 fichier :

    . $PSScriptRoot\Core.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change to your organization
    # Invoke WhoAmI Function
    Invoke-RestMethod -Uri ($baseURI + 'WhoAmI') -Method Get -Headers $baseHeaders
    | ConvertTo-Json
    

    . $PSScriptRoot\Core.ps1 en haut du fichier utilise le dot sourcing pour indiquer au script de charger le contenu de ce fichier.

    N’oubliez pas de changer https://yourorg.crm.dynamics.com/ pour correspondre à l’URL de votre environnement.

  7. Pour exécuter le script, appuyez sur F5.

    La sortie peut ressembler à cette sortie :

    PS C:\scripts> . 'C:\scripts\test.ps1'
    {
    "@odata.context": "https://yourorg.crm.dynamics.com/api/data/v9.2/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse",
    "BusinessUnitId": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
    "UserId": "22cc22cc-dd33-ee44-ff55-66aa66aa66aa",
    "OrganizationId": "00aa00aa-bb11-cc22-dd33-44ee44ee44ee"
    }
    

Créer une fonction WhoAmI

Nous allons placer le code pour appeler la fonction WhoAmI dans une fonction appelée Get-WhoAmI à l’intérieur d’un fichier nommé CommonFunctions.ps1 afin que nous puissions taper seulement 11 caractères plutôt que 100 chaque fois que vous souhaitez utiliser la fonction WhoAmI

  1. Créez un fichier texte nommé CommonFunctions.ps1 dans votre scripts dossier.

  2. Copiez et collez la définition de fonction suivante dans le CommonFunctions.ps1.

    function Get-WhoAmI{
    
       $WhoAmIRequest = @{
          Uri = $baseURI + 'WhoAmI'
          Method = 'Get'
          Headers = $baseHeaders
       }
    
       Invoke-RestMethod @WhoAmIRequest
    }
    

    Note

    Cette définition de fonction utilise une technique appelée Splatting. Le Splatting rend vos commandes plus courtes et plus faciles à lire, car il transmet une collection de valeurs de paramètres à une commande en tant qu’unité.

  3. Enregistrez le fichier CommonFunctions.ps1.

  4. Modifiez le fichier pour qu’il test.ps1 ressemble au script suivant :

    . $PSScriptRoot\Core.ps1
    . $PSScriptRoot\CommonFunctions.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change to your organization
    # Invoke WhoAmI Function
    Get-WhoAmI | ConvertTo-Json
    

    N’oubliez pas de modifier la https://yourorg.crm.dynamics.com/ valeur pour qu’elle corresponde à l’URL de votre environnement.

  5. Pour exécuter le script, appuyez sur F5.

    La sortie doit être exactement comme avant.

Créer des fonctions d’opérations de table

Nous allons placer des fonctions pour effectuer des opérations de table courantes, un fichier nommé TableOperations.ps1 afin que nous puissions les réutiliser.

  1. Créez un fichier texte nommé TableOperations.ps1 dans votre scripts dossier.

  2. Copiez et collez les définitions de fonction suivantes dans le 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
    }
    
    

    Pour plus d’informations sur la rédaction de ces demandes, consultez les articles suivants :

  3. Enregistrez le fichier TableOperations.ps1.

  4. Copiez le code suivant et collez-le dans le test.ps1 fichier.

    . $PSScriptRoot\Core.ps1
    . $PSScriptRoot\CommonFunctions.ps1
    . $PSScriptRoot\TableOperations.ps1
    
    Connect 'https://yourorg.crm.dynamics.com/' # change to your organization
    
    # 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"
    

    N’oubliez pas de modifier la https://yourorg.crm.dynamics.com/ valeur pour qu’elle corresponde à l’URL de votre environnement.

  5. Pour exécuter le script, appuyez sur F5.

    La sortie peut ressembler à cette sortie :

    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
    

Gérer les exceptions

Jusqu’à présent, dans cet article, vous avez copié et collé du code fourni pour vous. Mais lorsque vous commencez à écrire et à utiliser vos propres fonctions, vous pouvez rencontrer des erreurs. Lorsque ces erreurs se produisent, elles peuvent provenir de Dataverse ou de votre script.

Ajoutez une fonction d’assistance qui peut aider à détecter la source des erreurs et à extraire les détails pertinents des erreurs retournées par Dataverse.

  1. Ajoutez la fonction suivante Invoke-DataverseCommands au Core.ps1 fichier :

    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
          $_
       }
    }
    

    La Invoke-DataverseCommands fonction utilise l’applet de commande Invoke-Command pour traiter un ensemble de commandes dans un bloc try/catch. Toutes les erreurs retournées par Dataverse sont des erreurs de type HttpResponseException. Par conséquent, le premier bloc catch écrit un message An error occurred calling Dataverse: dans le terminal avec les données d’erreur JSON.

    Les données JSON dans $_.ErrorDetails.Message contiennent des caractères Unicode échappés. Par exemple : \u0026 au lieu de & et \u0027 au lieu de '. Cette fonction comprend du code qui remplace ces caractères par les caractères non échappés afin qu'ils correspondent exactement aux erreurs que vous identifiez ailleurs.

    Sinon, les erreurs sont réécrites dans la fenêtre de terminal avec un message : An error occurred in the script:

  2. Enregistrez le fichier Core.ps1.

  3. Modifiez le test.ps1 fichier pour ajouter le script suivant qui utilise une valeur de paramètre non valide setName . Le account paramètre doit être accounts. Cette erreur est courante.

    . $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
    
    }
    

    N’oubliez pas de modifier la https://yourorg.crm.dynamics.com/ valeur pour qu’elle corresponde à l’URL de votre environnement.

  4. Pour exécuter le script, appuyez sur F5.

    La sortie peut ressembler à cette sortie :

    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'."
       }
    }
    
  5. Modifiez le test.ps1 fichier pour lever une erreur de script dans le Invoke-DataverseCommands bloc :

    Invoke-DataverseCommands {
    
       throw 'A script error'
    
    }
    
  6. Pour exécuter le script, appuyez sur F5.

    La sortie doit être presque la même que si elle n’était pas incluse dans le Invoke-DataverseCommands bloc :

    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
    

Gérer les limites de protection des services Dataverse

Les limites de l’API de protection du service Dataverse permettent de s’assurer que Dataverse offre une disponibilité et des performances cohérentes. Lorsque les applications clientes font des demandes excessives sur les ressources serveur à l’aide de l’API Web, Dataverse retourne une erreur 429 trop de requêtes, et les applications clientes doivent suspendre les opérations pendant la durée spécifiée dans l’en-tête Retry-After.

Le paramètre MaximumRetryCount de l’applet de commande PowerShellInvoke-RestMethod spécifie le nombre de tentatives d’une requête lorsqu’un code d’échec est compris entre 400 et 599, inclus ou 304 est reçu. Cela signifie que PowerShell retente les erreurs de protection du service Dataverse 429 lorsque vous incluez une valeur pour ce paramètre. Le MaximumRetryCount paramètre peut être utilisé avec RetryIntervalSec pour spécifier le nombre de secondes à attendre. La valeur par défaut est 5 secondes. Si la réponse d’erreur inclut un Retry-After en-tête pour une erreur 429, comme les erreurs de protection du service Dataverse le font, cette valeur est utilisée à la place.

Vous ne rencontrerez peut-être jamais d’erreur de limite de protection du service pendant que vous apprenez à utiliser l’API Web Dataverse avec PowerShell. Toutefois, les scripts que vous écrivez peuvent envoyer un grand nombre de requêtes qui produisent des erreurs. Découvrez comment les gérer le mieux à l’aide de PowerShell.

Si vous ajoutez le paramètre MaximumRetryCount à chaque appel Dataverse à l’aide de Invoke-RestMethod, PowerShell retente un large éventail d’erreurs. Une nouvelle tentative de chaque erreur ralentit vos scripts, en particulier lors du développement et du test. Vous devez attendre 10 à 15 secondes chaque fois qu’une erreur se produit, en fonction du nombre de nouvelles tentatives que vous spécifiez. Une autre approche consiste à encapsuler dans Invoke-RestMethod votre propre méthode qui gère les nouvelles tentatives pour des erreurs spécifiques.

La fonction suivante Invoke-ResilientRestMethod prend un request objet de table de hachage comme paramètre obligatoire et un indicateur booléen returnHeader pour indiquer si l’en-tête de réponse doit être retourné ou non. Lorsque $returnHeader est true, elle envoie la requête en utilisant la commande Invoke-RestMethod avec le paramètre ResponseHeadersVariable pour capturer les en-têtes retournés. La fonction utilise Out-Null pour que la sortie qui représente le corps de la réponse vide ne soit pas retournée avec la fonction. Sinon, la fonction envoie la requête à l’aide de Invoke-RestMethod avec l’objet request et retourne le corps de la réponse.

Si Invoke-RestMethod échoue avec une erreur 429, il vérifie si l’objet request possède une propriété MaximumRetryCount. Si la fonction réussit, elle crée une MaximumRetryCount propriété définie sur 3. On réessaie Invoke-RestMethod en utilisant l'objet de requête et la valeur de l'en-tête de réponse Retry-After. Si l’indicateur returnHeader est true, il retourne l’en-tête de réponse. Si Invoke-RestMethod échoue avec une autre erreur, elle renvoie l’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 $_
   }
}

Vous pouvez utiliser une fonction similaire dans vos fonctions réutilisables. Lorsque les fonctions doivent renvoyer des valeurs de l’en-tête de la réponse, elles doivent définir la valeur returnHeader sur $true. Par exemple, la fonction suivante New-Record modifie l’exemple de fonction dans Créer des fonctions d’opérations de table à utiliser Invoke-ResilientRestMethod plutôt que Invoke-RestMethod directement.

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

   }
   # 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())
}

Sinon, Invoke-ResilientRestMethod peut remplacer la Invoke-RestMethod valeur indiquée dans cet Get-Record exemple :

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
   }
   # Before:
   # Invoke-RestMethod @RetrieveRequest

   # After: 
   Invoke-ResilientRestMethod $RetrieveRequest
}

La seule différence est que vous transmettez la table de hachage ($RetrieveRequest) à la méthode au lieu d’utiliser le splatting (@RetrieveRequest). Sinon, vous obtenez une erreur de script : A parameter cannot be found that matches parameter name 'Headers'.

Déboguer à l’aide de Fiddler

Fiddler est un proxy de débogage web utilisé pour afficher le trafic HTTP sur votre ordinateur. L’affichage de ces données est utile lors du débogage de scripts. Par défaut, les requêtes et réponses HTTP envoyées à l’aide d'Invoke-RestMethod applet de commande ne sont pas visibles lorsque vous utilisez Fiddler.

Pour afficher le trafic HTTP dans Fiddler, définissez le Invoke-RestMethodparamètre proxy sur l’URL configurée comme proxy Fiddler sur votre ordinateur local. Par défaut, l’URL est http://127.0.0.1:8888. Votre URL peut être différente.

Par exemple, si vous appelez la fonction WhoAmI alors que Fiddler capture le trafic et que le paramètre -Proxy est défini :

Invoke-RestMethod `
   -Uri ($environmentUrl + 'api/data/v9.2/WhoAmI') `
   -Method Get `
   -Headers $baseHeaders `
   -Proxy 'http://127.0.0.1:8888'

Dans Fiddler, vous pouvez voir tous les détails :

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=00aa00aa-bb11-cc22-dd33-44ee44ee44ee; 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":"11bb11bb-cc22-dd33-ee44-55ff55ff55ff","UserId":"22cc22cc-dd33-ee44-ff55-66aa66aa66aa","OrganizationId":"00aa00aa-bb11-cc22-dd33-44ee44ee44ee"}

Si Fiddler n’est pas en cours d’exécution, vous obtenez une erreur :

Invoke-RestMethod: C:\scripts\test.ps1:8:1
Line |
   8 |  Invoke-RestMethod `
     |  ~~~~~~~~~~~~~~~~~~~
     | No connection could be made because the target machine actively refused it.

Si vous choisissez d’acheminer tous vos Invoke-RestMethod appels par le biais d’une seule fonction, par exemple la Invoke-ResilientRestMethod description décrite dans Gérer les limites de protection du service Dataverse, vous pouvez définir certaines variables dans le Core.ps1 fichier pour configurer cette option dans un emplacement unique.

# 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'

Dans votre fonction centralisée, vous pouvez définir le paramètre -Proxy avec l'éclatement et utiliser la table de hachage $request uniquement lors du débogage avec Fiddler.

function Invoke-ResilientRestMethod {
   param (
      [Parameter(Mandatory)] 
      $request,
      [bool]
      $returnHeader
   )

   if ($debug) {
      $request.Add('Proxy', $proxyUrl)
   }

   ...

En savoir plus sur la capture du trafic web avec Fiddler

Télécharger le document $metadata CSDL de l’API web Dataverse

Le langage CSDL (Common Schema Definition Language) $metadata est la source de vérité sur les fonctionnalités de l’API Web Dataverse. Vous pouvez l’afficher dans un navigateur, mais il peut être plus facile de télécharger le fichier et de l’afficher dans Visual Studio Code. Le script suivant est une version modifiée du script introduit dans l’API web démarrage rapide avec PowerShell. La différence est qu’elle utilise l’applet de commandeInvoke-WebRequest, qui est plus appropriée pour télécharger un document XML.

$environmentUrl = 'https://yourorg.crm.dynamics.com/' # change to your organization
$writeFileTo =  'C:\temp\yourorg.xml' # change to your organization

## Login if not already logged in
if ($null -eq (Get-AzTenant -ErrorAction SilentlyContinue)) {
   Connect-AzAccount | Out-Null
}
# Get an access token
$secureToken = (Get-AzAccessToken `
   -ResourceUrl $environmentUrl `
   -AsSecureString).Token

# Convert the secure token to a string
$token = ConvertFrom-SecureString `
   -SecureString $secureToken `
   -AsPlainText


# 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
  1. Copiez le script.
  2. Modifiez les variables $environmentUrl et $writeFileTo pour qu’elles correspondent à vos besoins.
  3. Exécutez le script dans Visual Studio Code.

Le document $metadata CSDL de l’API web Dataverse s’ouvre dans Visual Studio Code.

Vous pouvez recevoir une notification indiquant : Pour des raisons de performances, les symboles de document sont limités à 5 000 éléments. Si une nouvelle limite est définie, fermez et rouvrez ce fichier pour recompiler les symboles de document.

La notification offre la possibilité de modifier la limite d’extension xml.symbols.maxItemsComputed XML de Visual Studio Code. Pour la plupart des documents CSDL de l’API web Dataverse $metadata, la définition de la limite 500000 doit être suffisante.

Résolution des problèmes

Cette section contient des conseils pour les problèmes que vous pouvez rencontrer.

Boîte de dialogue d’erreur : connectez ENOENT\\.\pipe\<RANDOM_text> avec le bouton Ouvrir 'launch.json'

Cette erreur peut se produire lors du débogage avec Visual Studio Code. Pour résoudre l’erreur :

  1. Sélectionnez Afficher> lapalette de commandes... dans le menu Visual Studio Code, ou appuyez sur Ctrl+Maj+P.
  2. Tapez restart et sélectionnez Powershell: Restart session. Pour plus d’informations, consultez PowerShell/vscode-powershell GitHub Issue 4332 .

Étapes suivantes

En savoir plus sur les fonctionnalités de l’API Web Dataverse en comprenant les documents de service.

Passez en revue et exécutez des exemples de code.