I tend to use Azure Functions to create external webhooks for Dynamics 365 server side events. Although this post is not about setting up your webhooks, at the end of this post is a series of links that you can use as reference.

This post follows up on a series of articles written by my colleague Dylan Haskins and myself where we take you through our ALM approach, with complete source code for our Framework and Provisioning tool available here on Github.

When setting up the webhook you need to provide a Name, endpoint URL and option for authentication. The webhook key is a perfect option to authenticate with an Azure Function because of the authentication query string is expected to have a key name of code.



https://webhook-dev.azurewebsites.net/api/WebhookFunctionName?code=gqqIX1a2b05vHVjPeGQoiOjkXIwGpr2qENVqFVSQFzR/cdl7QGNqKw==

You can query your webhook registrations as they are stored in the ServiceEndpoint entity:

GET [organization URI]/api/data/v9.0/serviceendpoints?$filter=contract eq 8&$select= serviceendpointid,name,authtype,url

Note: The actual AuthValue property cannot be retrieved.

 

Once that is all set up how do you then automate setting the unique Endpoint URL and AuthValue per instance that you deploy, for example I usually have a Dev, Test and Prod instance of Dynamics 365 as a bare minimum on a project and ideally you want a unique instance of the webhook deployed for each environment. Yes, you can manually register a unique ServiceEndpoint per instance, or you can automate it as set it up as part of your deployment.


release-stages.png

So instead of manually repeating and updating the ServiceEndpoint per environment you can do it via the release proces.

 

Here is a quick guide that I usually employ:

Step1: Ensure your ServiceEndpoint is added to the Dynamics 365 Solution that you will be deploying.

service-endpoint-solution.png

This will ensure your webhook maintains the same record identifier across all your environments. If you unpack your solution into source control it will be available under the PluginAssemblies directory and will contain all that you need.

<?xml version="1.0" encoding="utf-8"?>
<ServiceEndpoints xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <ServiceEndpoint ServiceEndpointId="{00000000-YOUR-GUID-HERE-000000000000}" Name="AccountWebhook">
    <ConnectionMode>1</ConnectionMode>
    <Contract>8</Contract>
    <Path></Path>
    <SolutionNamespace></SolutionNamespace>
    <UserClaim>1</UserClaim>
    <IsCustomizable>1</IsCustomizable>
    <AuthType>4</AuthType>
    <MessageFormat>2</MessageFormat>
    <NamespaceFormat>1</NamespaceFormat>
    <Url>https://mywebhook-dev.azurewebsites.net/api/AccountWebhook</Url>
  </ServiceEndpoint>
</ServiceEndpoints>


Step 2: Update the Webhook parameters during deployment

For this we need to do the following:

  1. Return ResourceId and DefaultHostName from the ARM template deployment, as below


    "outputs": {
    "FunctionAppResourceId": {
          "type": "string",
          "value": "[resourceId('Microsoft.Web/sites', parameters('functionAppName'))]"
        },
        "FunctionAppHostname": {
          "type": "string",
          "value": "[reference(resourceId('Microsoft.Web/sites', parameters('functionAppName')), '2018-02-01').defaultHostName]"
        }
    }โ€‹

    note: you will have to caputre the ARM output to be able to use it in other tasks further down the track
    "##vso[task.setvariable variable=foo;isOutput=true]bar"โ€‹



  2. Deploy the Azure Function


    deploy-function-step.png

    Stock standard App Service deploy task.


  3. Retrieve the Default host key 


    get-function-key-step.png

    Retrieve the Default Function key from the recently deployed Azure Function using the Azure CLI task with following script:
    POST https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/
    Microsoft.Web/sites/{name}/functions/{functionName}/listkeys?api-version=2019-08-01

    FOR /F "tokens=* USEBACKQ" %%F IN (`CALL az rest --method post --uri "https://management.azure.com$(functionAppResourceId)/functions/$(functionName)/listkeys ?api-version=2018-02-01" --output tsv`) DO (
    SET apikey=%%F
    )
    echo ##vso[task.setvariable variable=functionKey;]%apikey%โ€‹

    note: $(functionAppResourceId) is the output value from the ARM template deployment step.

    This will add the value of the apikey to the functionKey pipeline variable, to be used by the following task

  4. Update Webhook details in Dynamic 365



    update-webhook-step.png

    Passing in the new webhook Endpoint and new Host key as parameters the task updates the ServiceEndpoint record in the target Dynamics 365 environment.

    note: $(functionAppHostname) is the output value from the ARM template deployment step: https://mywebhook-dev.azurewebsites.net
    and $(endpoint) is the actual endpoint of your Azure Function: /api/WebhookFunctionName

    -webhook $(functionAppHostname)$(endpoint) 

    -authvalue $(functionKey)

    This step can also be used for various actions that you want to execute as part of your deployment such as disable or remove unused items.



Reference:

https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/use-webhooks

https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-rm-web-app-deployment?view=azure-devops

https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch

https://docs.microsoft.com/en-us/rest/api/appservice/webapps/listfunctionkeys