Exercise - Use Dataverse Healthcare APIs with Azure Functions
In this exercise, you create a function from Microsoft Azure Functions by using .NET 6 as runtime to call Dataverse Healthcare APIs to create a new patient record in Dataverse.
Prerequisites
Make sure that you meet the following prerequisites before starting this exercise:
Good understanding of .NET development.
Visual Studio Code is downloaded and installed.
Azure Functions Core Tools is downloaded and installed.
Note
For this exercise, all Azure resources are provisioned in the East US region.
Tip
To complete this exercise, make sure that you create all Azure resources in the same subscription and resource group unless otherwise stated.
Create a function from Azure Functions
In this task, you create a function from Azure Functions to call Dataverse Healthcare API to post the FHIR bundle to create a patient resource (Contact) in Dataverse.
While you're signed in to your Microsoft 365 tenant, open a new tab, and then go to the Azure portal.
Search for the string Function App in the search box.
Select Function App under the Services section.
On the Function App page, select Create.
On the Create Function App page, enter the following settings:
Subscription - Select the subscription that you chose in the previous unit.
Resource Group - Select the resource group that you chose in the previous unit.
Function App name - AzFunc-DataverseHealthcareAPIs (If the name isn't available, append it with a number)
Do you want to deploy code or container image? - Code
Runtime stack - .NET
Version - 6
Region - East US
Operating System - Windows
Plan type - Consumption (Serverless)
Leave the rest of the settings to their default values. Select Review + create.
In the Summary section of the Create Function App page, select Create.
On the deployment status page, select Go to resource.
Add the user assigned managed identity
In this task, you add the FHIRRelayIdentity user-assigned managed identity that you previously created to the AzFunc-DataverseHealthareAPIs Azure Functions app.
On the Function App page, select Settings > Identity on the left pane.
On the right pane, select User assigned and then select + Add.
On the Add user assigned managed identity panel, select the FHIRRelayIdentity identity that you created in the previous unit.
Select Add.
Verify if the FHIRRelayIdentity user assigned managed identity is added successfully to the Azure Functions app.
Write Azure Functions code in Visual Studio Code
In this task, you use Visual Studio Code to write the logic to authenticate the function from Azure Functions, and then you call the Dataverse Healthcare APIs to post a Patient FHIR resource bundle to Dataverse.
Launch Visual Studio Code.
Install the Azure Functions and C# extensions for Visual Studio Code.
Select the Extensions icon on the left pane.
Use the search box to search for Azure Functions.
Select Azure Functions from the search result.
On the right pane, select Install, as shown in the following screenshot.
v. Follow the previous steps to install the C# extension, as shown in the following screenshot.
Select the Azure icon on the left navigation pane.
Select Sign in to Azure.
On the sign-in prompt, enter your Azure credentials.
After successful sign-in, your Azure subscription and underlying resources will display.
Expand Function App to view the AzFunc-DataverseHealthcareAPIs function app that you created in the previous task.
Select the Create Function icon under the Workspace Local section.
Select a folder location on your machine to scaffold the function from Azure Functions. Name the folder AzFunc-DataverseHealthcareAPIs.
To create a new project, when you're prompted, select a language. Select C#.
Select .Net 6.0 LTS as .NET runtime.
Select HTTP trigger as a template for your project's first function.
Provide AzFunc-DataverseHealthcareAPIs as the function name and then press the Enter key.
Provide MCH.DataverseHealthcareAPIs as the namespace and then press the Enter key.
Select Function as a value for AccessRights.
On the Select how you would like to open your project prompt, select Open in current window.
You're directed to project scaffolding. Select Terminal > New Terminal in the menu bar.
Run the following code on the terminal to add Azure.Identity library in the project.
dotnet add package Azure.Identity
Replace the code block for public static async Task<IActionResult> Run with the following code. In the following code, set the value of the following variables as they're set up in your environment:
userAssignedClientId - Set this value as the client ID of the FHIRRelayIdentity user-assigned managed identity.
dataverseURL - Set this value as the URL of your Dataverse environment.
log.LogInformation("HTTP trigger function initiated"); // When deployed to an azure host, the default azure credential will authenticate the specified user assigned managed identity. string userAssignedClientId = "55556666-ffff-7777-aaaa-8888bbbb9999 "; string dataverseURL = "https://lamnahealth.crm.dynamics.com"; var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId }); log.LogInformation("Invoked DefaultAzureCredential"); var token = credential.GetToken(new TokenRequestContext(new []{$"{dataverseURL}/.default"})).Token; log.LogInformation("Invoked GetTokenAsync"); string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); log.LogInformation($"Request Body as read by Stream Reader: {requestBody}"); // Creating HTTP Client object to call the Dataverse Healthcare API using HttpClient client = new HttpClient(); HttpRequestHeaders headers = client.DefaultRequestHeaders; headers.Authorization = new AuthenticationHeaderValue("Bearer", token); headers.Add("OData-Version", "4.0"); headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri($"{dataverseURL}/api/data/v9.1/msind_UpsertBundle"), Content = new StringContent(requestBody, Encoding.UTF8, "application/json") }; var response = await client.SendAsync(request); log.LogInformation("HTTP Post Method executed successfully"); var content = await response.Content.ReadAsStringAsync(); return new OkObjectResult($"Response from Dataverse: {content}");
Note
You can use the /api/data/v9.1/msind_UpsertBundle or the /api/data/v9.2/msind_UpsertBundle endpoint in your API call.
Replace the using statements with the following text:
using System; using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Azure.Core; using Azure. Identity; using System.Net.Http; using System.Net.Http.Headers; using System.Text;
The complete code for the AzFunc-DataverseHealthcareAPIs function resembles the following example:
using System; using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Azure.Core; using Azure.Identity; using System.Net.Http; using System.Net.Http.Headers; using System.Text; namespace MCH.DataverseHealthcareAPIs { public static class AzFunc_DataverseHealthcareAPIs { [FunctionName("AzFunc_DataverseHealthcareAPIs")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("HTTP trigger function initiated"); // When deployed to an azure host, the default azure credential will authenticate the specified user assigned managed identity. string userAssignedClientId = "55556666-ffff-7777-aaaa-8888bbbb9999 "; string dataverseURL = "https://lamnahealth.crm.dynamics.com"; var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = userAssignedClientId }); log.LogInformation("Invoked DefaultAzureCredential"); var token = credential.GetToken(new TokenRequestContext(new []{$"{dataverseURL}/.default"})).Token; log.LogInformation("Invoked GetTokenAsync"); string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); log.LogInformation($"Request Body as read by Stream Reader: {requestBody}"); // Creating HTTP Client object to call the Dataverse Healthcare API using HttpClient client = new HttpClient(); HttpRequestHeaders headers = client.DefaultRequestHeaders; headers.Authorization = new AuthenticationHeaderValue("Bearer", token); headers.Add("OData-Version", "4.0"); headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var request = new HttpRequestMessage{ Method = HttpMethod.Post, RequestUri = new Uri($"{dataverseURL}/api/data/v9.1/msind_UpsertBundle"), Content = new StringContent(requestBody, Encoding.UTF8, "application/json") }; var response = await client.SendAsync(request); log.LogInformation("HTTP Post Method executed successfully"); var content = await response.Content.ReadAsStringAsync(); return new OkObjectResult($"Response from Dataverse: {content}"); } } }
On the terminal, run the following code to build the function code.
dotnet build
To deploy the function code to the Azure Functions app, select the Azure icon on the left side bar.
Under Resources, expand Function App.
Right-click the AzFunc-DataverseHealthcareAPIs function under the Function App category and then select Deploy to Function App.
On the Visual Studio Code prompt, select Deploy.
Verify if the function code is successfully deployed. You see the confirmation pop-up on the lower right corner of the Visual Studio page.
Select Function App > AzFunc-DataverseHealthcareAPIs > Functions Read-only. The following screenshot confirms that the AzFunc_DataverseHealthcareAPIs HTTP is deployed.
Test the Azure Functions app code
In this task, you used the Azure portal to test the function code that you published to the AzFunc-DataverseHealthcareAPIs Azure Functions app.
While you're signed in to your Microsoft 365 tenant, open a new tab, and then go to the Azure portal.
Go to your resource group and select the AzFunc-DataverseHealthcareAPIs function app.
Select the Functions tab and then select AzFunc_DataverseHealthcareAPIs.
Select Code + Test on the left pane and then select Test/Run on the right pane.
Select Filesystem Logs as the log-streaming service.
Select Ok.
Select POST as the HTTP method.
Select master (Host key) as the key to invoke the Azure Functions app.
Pass the following JSON as the Request body.
{ "msind_BundleTag": "PatientDatatest", "msind_JSON": "{\"resourceType\":\"Bundle\",\"type\":\"batch\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"b7777777-b777-b777-b777-b77777777777\",\"text\":{\"status\":\"generated\"},\"identifier\":[{\"system\":\"https://github.com/synthetichealth/synthea\",\"value\":\"51978986-f824-43da-b69a-d2d17f49df70\"}],\"name\":[{\"use\":\"official\",\"family\":\"Contoso\",\"given\":[\"Allen\"],\"prefix\":[\"Ms.\"]}],\"telecom\":[{\"system\":\"phone\",\"value\":\"555-664-6121\",\"use\":\"home\"}],\"gender\":\"female\",\"birthDate\":\"1965-11-04\",\"address\":[{\"extension\":[{\"url\":\"http://hl7.org/fhir/StructureDefinition/geolocation\",\"extension\":[{\"url\":\"latitude\",\"valueDecimal\":42.605887766784662},{\"url\":\"longitude\",\"valueDecimal\":-71.0695322588603}]}],\"line\":[\"165 Shanahan View\"],\"city\":\"North Reading\",\"state\":\"Massachusetts\",\"country\":\"US\"}],\"multipleBirthBoolean\":false},\"request\":{\"method\":\"POST\",\"url\":\"Patient\"}}]}" }
Select Run.
After successful implementation of the function, Dataverse will respond with HTTP response code 200 OK, as shown in the following screenshot.
Go to the Contacts table in Dataverse to verify the newly added patient record.
You successfully completed this exercise by creating the Azure Functions app, writing .NET code for the HTTP function trigger, deploying the function to the Azure Functions app, and then testing the published function on Azure portal to create a new patient record in Dataverse by using the msind_UpsertBundle Dataverse Healthcare API.