Exercise - Create a workflow using Durable Functions

Completed

In this exercise, you use the example scenario from the previous unit to learn how to create an approval workflow in the Azure portal using Durable Functions.

Create a Function App

  1. Sign in to the Azure portal using the same account that you used to activate the sandbox.

  2. On the Azure portal menu or from the Home page, under Azure services, select Create a resource. The Create a resource pane appears.

  3. Search for and select Function App. The Create Function App pane appears.

  4. On the Basics tab, enter the following values for each setting.

    Setting Value Description
    Project Details
    Subscription Concierge Subscription Specifies the subscription under which this new function app is created.
    Resource Group From the dropdown list, select [sandbox resource group name] Specifies the name of the resource group in which to create your function app. We create the function app in the sandbox resource group that was assigned when we activated the sandbox, namely, [sandbox resource group name].
    Instance Details
    Function App name [Globally unique name] Specifies the name that identifies your new function app. Valid characters are a-z, 0-9, and -.
    Publish Code Specifies that the function uses code instead of a container.
    Runtime stack Node.js Specifies that the sample code in this module is written in JavaScript.
    Version 20 LTS Specifies the version of the runtime stack.
    Region [Select from the list following this section] Choose the region closest to you that is also one of the allowed Sandbox regions that follow.
    Operating system
    Operating System Windows Specifies the operating system that hosts the function app.
    Plan
    Plan type Consumption (Serverless) Specifies the hosting plan that defines how resources are allocated to your function app. In the default Consumption plan, resources are added dynamically as required by your functions. In this serverless hosting model, you only pay for the time your functions run.

    The free sandbox allows you to create resources in a subset of the Azure global regions. Select a region from the following list when you create resources:

    • West US 2
    • South Central US
    • Central US
    • East US
    • West Europe
    • Southeast Asia
    • Japan East
    • Brazil South
    • Australia Southeast
    • Central India
  5. Select Next : Storage.

  6. On the Storage tab, enter the following values for each setting.

    Setting Value Description
    Storage
    Storage account [Globally unique name] Specifies the name of the new storage account used by your function app (which doesn't need to match the globally unique name that you specified for your function). Storage account names must be between 3 and 24 characters in length and might contain numbers and lowercase letters only. This dialog automatically populates the field with a unique name that is dynamically generated. However, feel free to use a different name or even an existing account.
  7. Select Next : Networking. Accept the defaults.

  8. Select Next : Monitoring.

  9. On the Monitoring tab, enter the following value for the setting.

    Setting Value Description
    Application Insights
    Enable Application Insights No Specifies that Application Insights is disabled for this module.
  10. Select Review + create and review the options that you configured. If you're satisfied with your options, select Create to provision and deploy the function app.

Wait for the deployment to complete before continuing. Deployment might take a few minutes.

Install the durable-functions npm package

Because we're creating JavaScript Durable Functions, we need to install the durable-functions npm package. To do so, perform the following steps.

  1. Select Go to resource to select your function app. Your Function App pane appears.

  2. In the left menu pane, under Development Tools, select App Service Editor (preview), then select Open editor. The App Service Editor Quick Start pane appears in a new browser window.

  3. In the left menu pane, highlight the WWWROOT folder.

  4. In the left toolbar menu, select the Open Console icon.

    This action starts the console. You can use this console to access the web server that hosts your functions and write the code for your functions.

  5. Create a new package.json file.

    • Run the following commands in the console to create the new JSON file and open it in the editor.

      touch package.json
      open package.json
      
    • Add the following code.

      {
        "name": "example",
        "version": "1.0.0"
      }
      

      Replace example with the name of your package. For example, you could use the globally unique name that you specified for your function earlier.

  6. Select Ctrl+S to save the file, then Ctrl+Q to close the document.

  7. Switch back to the Azure portal.

  8. In the left menu bar, under Development Tools, select Console. The Console pane appears for your function app.

  9. Run the following command:

    npm install durable-functions
    

    This command instructs the node package manager to install the durable-functions package and any required dependencies. Installation can take a few minutes to complete, and the node package manager might display some warnings, which you can ignore. If you're prompted to install a newer version of npm, use the command given in the error to install the newer version, then install the durable-functions package once the new version is installed.

    Wait until all packages are finished installing.

  10. In the left menu pane, scroll up and select Overview, and in the top menu bar, select Restart, and then select Yes when prompted to restart.

    Wait for the restart to complete before continuing.

Create the client function for submitting a design proposal

  1. On the Azure portal menu or from the Home page, under Recent resources, select See all, and then select your function app. Your Function App pane appears.

  2. On the Overview page, select the Functions tab in the center of the screen.

  3. Select the Create in Azure portal button. The Create function pane appears.

  4. Under Select a template, in the Filter box, enter Durable Functions HTTP starter and select that template from the list. This template creates a durable function that runs in response to an HTTP request.

  5. Under Template details, for the New Function field, enter HttpStart for the name of the function, and in the Authorization level field, select Function, then select Create. The HttpStart pane appears for your function.

  6. In the left menu pane, under Developer, select Code + Test. The Code + Test pane appears for your function.

    The code for the index.js file appears in the editor. Your file should resemble the following example:

    const df = require("durable-functions");
    
    module.exports = async function (context, req) {
        const client = df.getClient(context);
        const instanceId = await client.startNew(req.params.functionName, undefined, req.body);
    
        context.log(`Started orchestration with ID = '${instanceId}'.`);
    
        return client.createCheckStatusResponse(context.bindingData.req, instanceId);
    };
    
  7. In the dropdown list for the files in your function, select function.json to view the bindings associated with your new function. This information specifies any authentication requirements, together with the HTTP methods that can trigger the function. This file also specifies that the function is a client that starts the orchestration process. Your file should resemble the following example:

    {
      "bindings": [
        {
          "authLevel": "function",
          "name": "req",
          "type": "httpTrigger",
          "direction": "in",
          "route": "orchestrators/{functionName}",
          "methods": [
            "post",
            "get"
          ]
        },
        {
          "name": "$return",
          "type": "http",
          "direction": "out"
        },
        {
          "name": "starter",
          "type": "orchestrationClient",
          "direction": "in"
        }
      ]
    }
    

    Note

    A binding associates resources and other items with a trigger. It's a declarative mechanism that removes the need to hard-code references to other services and functions in your code.

Create the orchestrator function

  1. On the Azure portal menu or from the Home page, under Recent resources, select See all, and then select your function app. Your Function App pane appears.

  2. On the Overview page, select the Functions tab in the center of the screen.

  3. In the Functions menu bar, select Create. The Create function pane appears.

  4. Under Select a template, in the Filter box, enter Durable Functions orchestrator and select that template from the list. This template creates a durable function that orchestrates the execution of functions.

  5. Under Template details, for the New Function field, enter OrchFunction for the name of the function, then select Create. The OrchFunction function pane appears.

  6. In the left menu pane, under Developer, select Code + Test. The Code + Test pane appears for your function.

    The code for the index.js file appears in the editor.

  7. Replace the existing code with the following code.

    const df = require("durable-functions");
    
    module.exports = df.orchestrator(function* (context) {
        const outputs = [];
    
        /*
        * We will call the approval activity with a reject and an approved to simulate both
        */
    
        outputs.push(yield context.df.callActivity("Approval", "Approved"));
        outputs.push(yield context.df.callActivity("Approval", "Rejected"));
    
        return outputs;
    });
    

    This code calls an Activity function named Approval, which you create shortly. The code in the orchestrator function invokes the Approval function twice. The first time simulates accepting the proposal, and the second time tests the proposal-rejection logic.

    The value each call returns is combined and passed back to the client function. In a production environment, your orchestration function would call a series of activity functions that make the accept/reject decision, and return the result of these activities.

  8. In the top menu bar, select Save to save your new function.

Create the activity function

  1. On the Azure portal menu or from the Home page, under Recent resources, select See all, and then select your function app. Your Function App pane appears.

  2. On the Overview page, select the Functions tab in the center of the screen.

  3. In the Functions menu bar, select Create. The Create function pane appears.

  4. Under Select a template, in the Filter box, enter Durable Functions activity and select that template from the list. This template creates a durable function that is run when an activity is called by an orchestrator function.

  5. Under Template details, for the New Function field, enter Approval for the name of the function, then select Create. The Approval pane appears for your function app.

  6. In the left menu pane, under Developer, select Code + Test. The Code + Test pane appears for your function.

    The code for the index.js file appears in the editor.

  7. Replace the existing code with the following code.

    module.exports = async function (context) {
        return `Your project design proposal has been -  ${context.bindings.name}!`;
    };
    

    This function returns a message indicating the status of the proposal. The expression context.bindings.name is either Accepted or Rejected, depending on the parameter passed to the function from the orchestrator. In a real-world scenario, you'd add the logic that handles the accept or reject operations in this function.

  8. In the top menu bar, select Save to save your new function.

Verify that the durable functions workflow starts

  1. On the Azure portal menu or from the Home page, under Recent resources, select See all, and then select your function app. Your Function App pane appears.

  2. Select the Functions tab in the center of the page.

  3. Select the HttpStart function. The HttpStart pane appears for your function.

  4. In the top menu bar, select Get Function Url and copy the URL. Your URL should resemble the following example:

    https://example.azurewebsites.net/api/orchestrators/{functionName}?code=AbCdEfGhIjKlMnOpQrStUvWxYz==
    

    You use this URL to run your functions.

  5. Open a new browser window, and go to the URL that you copied. In the URL, replace the {functionName} placeholder with OrchFunction, which should resemble the following example:

    https://example.azurewebsites.net/api/orchestrators/OrchFunction?code=AbCdEfGhIjKlMnOpQrStUvWxYz==
    

    The response message contains a set of URI endpoints that you can use to monitor and manage the execution, which should resemble the following example:

    {
      "id": "f0e1d2c3b4a5968778695a4b3c2d1e0f",
      "statusQueryGetUri": "https://example.azurewebsites.net/...",
      "sendEventPostUri": "https://example.azurewebsites.net/...",
      "terminatePostUri": "https://example.azurewebsites.net/...",
      "rewindPostUri": "https://example.azurewebsites.net/...",
      "purgeHistoryDeleteUri": "https://example.azurewebsites.net/..."
    }
    
  6. Copy the statusQueryGetUri value, and use your web browser to go to this URL. You should see a response message that resembles the following example:

    {
      "name": "OrchFunction",
      "instanceId": "f0e1d2c3b4a5968778695a4b3c2d1e0f",
      "runtimeStatus": "Completed",
      "input": null,
      "customStatus": null,
      "output": [
        "Your project design proposal has been -  Approved!",
        "Your project design proposal has been -  Rejected!"
      ],
      "createdTime": "2019-04-16T15:23:03Z",
      "lastUpdatedTime": "2019-04-16T15:23:35Z"
    }
    

    Recall that the orchestration function runs the activity function twice. The first time, the activity function indicates that the project proposal is accepted. The second time, the proposal is rejected. The orchestration function combines the messages from both function calls and returns them to the client function.