The Azure Cosmos DB Blog

All about Cosmos DB, IoT and Azure

Tag: IoT (page 1 of 2)

Saving Medical Device to Cloud messages as HL7 FHIR Observation resources

In this tutorial we are going to learn how to get our Medical Device to Cloud data.

This tutorial is from a chapter in the see url HL7 FHIR on Azure – Device Framework eBook.

The following are the Requirements.


  • Ability to capture our Medical Device output data.
  • Ability to filter the data.
  • Ability to update the Observation Resource document with the output data.
  • Ability to modify the EndPoint Response after updating the Observation Resource document.
  • Ability to parse the Observation Resource document and post it to any EndPoint.


We are going to create a Function App, with a watch EventHubTrigger to do the following:

  1. Loop through the buy generic viagra online paypal EventHub messages.
  2. Create a EventGrid message.
  3. Post the EventGrid message.

Next we will create a Function App, DeviceGrid that handles the updating of the Observation Resource document.

  1. Subscribes to our EventGrid messages
  2. Upserts the Observation Resource document with the data from the EventGrid messages

Leverage Azure API Management Policies.

  1. Publish DeviceGrid Function App to Azure API Management.
  2. Create a policy that subscribes to the EventGrid topic.
  3. Extract specific device data and post it to any EndPoint.

Function Apps

EventHubTrigger – Function App

We are using an EventHubTrigger to loop through the EventHub Messages.
Then we create a EventGrid message and publish it.

Source Code

using System;
using System.Configuration;
using System.Globalization;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Azure.WebJobs.ServiceBus;
using Newtonsoft.Json;


namespace DeviceMetrics
    public static class DeviceToCloud
        private static readonly string TopicEndpoint = ConfigurationManager.AppSettings["topicEndpoint"];
        private static readonly string TopicKey = ConfigurationManager.AppSettings["topicKey"];

        public static void Run([EventHubTrigger("DeviceToCloud", Connection = " EventHubConnectionString")]
            string[] myEventHubMessages, TraceWriter log)
            log.Info($"C# Event Hub trigger function processed a message: {myEventHubMessages.Length}");

            foreach (var msg in myEventHubMessages)
                dynamic data = JsonConvert.DeserializeObject(msg);

                var eventGridMessage = new GridEvent
                    Id = Guid.NewGuid().ToString(),
                    EventTime = DateTime.UtcNow,
                    EventType = data.Properties.EventType, 
                    Data = data.Properties.Data,
                    Subject = data.Properties.Name,
                    Topic = data.Properties.Topic,
                    DeviceId = data.Id

                    SendEvent(TopicEndpoint, TopicKey, eventGridMessage).ConfigureAwait(false);
                catch (HttpRequestException e)
                    log.Error(e.Message, e);

        public static async Task SendEvent(string topicEndpoint, string topicKey, object data)
            // Create a SAS token for the call to the event grid. We can do this with 
            // the SAS key as well but wanted to show an alternative.
            var sas = CreateEventGridSasToken(topicEndpoint, DateTime.Now.AddDays(1), topicKey);

            // Instantiate an instance of the HTTP client with the 
            // event grid topic endpoint.
            var client = new HttpClient {BaseAddress = new Uri(topicEndpoint)};

            // Configure the request headers with the content type
            // and SAS token needed to make the request.
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("aeg-sas-token", sas);

            // Serialize the data
            var json = JsonConvert.SerializeObject(data);
            var stringContent = new StringContent(json, Encoding.UTF8, "application/json");

            // Publish grid event
            await client.PostAsync(string.Empty, stringContent);

        private static string CreateEventGridSasToken(string resourcePath, DateTime expirationUtc, string topicKey)
            const char resource = 'r';
            const char expiration = 'e';
            const char signature = 's';

            // Encode the topic resource path and expiration parameters
            var encodedResource = HttpUtility.UrlEncode(resourcePath);
            var encodedExpirationUtc = HttpUtility.UrlEncode(expirationUtc.ToString(CultureInfo.InvariantCulture));

            // Format the unsigned SAS token
            var unsignedSas = $"{resource}={encodedResource}&{expiration}={encodedExpirationUtc}";

            // Create an HMCASHA256 policy with the topic key
            using (var hmac = new HMACSHA256(Convert.FromBase64String(topicKey)))
                // Encode the signature and create the fully signed URL with the
                // appropriate parameters.
                var bytes = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(unsignedSas)));
                var encodedSignature = HttpUtility.UrlEncode(bytes);
                var signedSas = $"{unsignedSas}&{signature}={encodedSignature}";

                return signedSas;

DeviceEventGrid – Function App

Source Code


using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Newtonsoft.Json;


namespace DeviceMetrics
    public static class DeviceEventGrid
        private static readonly string Endpoint = ConfigurationManager.AppSettings["endpoint"];
        private static readonly string AuthKey = ConfigurationManager.AppSettings["authKey"];
        private static readonly string Database = ConfigurationManager.AppSettings["database"];
        private static readonly string Collection = ConfigurationManager.AppSettings["collection"];

        private static readonly DocumentClient Client = new DocumentClient(new Uri(Endpoint), AuthKey,
            new ConnectionPolicy
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp

        public static async Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post")]
            HttpRequestMessage req,
            TraceWriter log)
            log.Info("C# HTTP trigger function processed a request.");

            var jsonContent = await req.Content.ReadAsStringAsync();
            var gridEvent = JsonConvert.DeserializeObject<List<GridEvent<Dictionary<string, string>>>>(jsonContent)

            if (gridEvent == null) return req.CreateErrorResponse(HttpStatusCode.BadRequest, $@"Missing event details");

            var gridEventType = req.Headers.GetValues("Aeg-Event-Type").FirstOrDefault();

            if (gridEventType == "SubscriptionValidation")
                // Retrieve the validation code and echo back.
                var validationCode = gridEvent.Data["validationCode"];
                var validationResponse = JsonConvert.SerializeObject(new
                    validationResponse = validationCode

                return new HttpResponseMessage
                    StatusCode = HttpStatusCode.OK,
                    Content = new StringContent(validationResponse)

            if (gridEventType == "Notification")
                // Get the Observation Resource Document
                dynamic doc = await Client.ReadDocumentAsync(UriFactory.CreateDocumentUri(Database, Collection,

                var valueQuanity = new ValueQuantity
                    Code = doc.Component.ValueQuantity.Code,
                    System = doc.Component.ValueQuantity.System,
                    Unit = gridEvent.Data["unit"],
                    Value = gridEvent.Data["value"]

                var component = new Component
                    Code = doc.Component.Code,
                    ValueQuantity = valueQuanity

                // partial values 
                var observation = new CompoundNumericObservation
                    Id = doc.Id,
                    Component = {[0] = component},
                    EffectiveDateTime = new DateTimeOffset(DateTime.Parse(gridEvent.Data["timestamp"]))

                    var result =
                        await Client.UpsertDocumentAsync(UriFactory.CreateDocumentUri(Database, Collection, gridEvent.Data["deviceId"]),

                    return req.CreateResponse(result.StatusCode);
                catch (DocumentClientException e)
                    var resp = new HttpResponseMessage
                        StatusCode = (HttpStatusCode) e.StatusCode,
                        Content = new StringContent(e.Message)
                    return resp;

            return req.CreateErrorResponse(HttpStatusCode.BadRequest,
                $@"Unknown request type");

        public class GridEvent<T> where T : class
            public string Id { get; set; }
            public string EventType { get; set; }
            public string Subject { get; set; }
            public DateTime EventTime { get; set; }
            public T Data { get; set; }
            public string Topic { get; set; }

Subscribing to EventGrid Events Using Azure API Management

After we pubish our Function App to Api Management, we will create a new Policy for our Function App. This allows us to extract specific device data post it to any EndPoint.

The following is an example policy.

    <base />
    <set-variable value="@(context.Request.Headers["Aeg-Event-Type"].Contains("SubscriptionValidation"))" name="isEventGridSubscriptionValidation" />
    <set-variable value="@(context.Request.Headers["Aeg-Event-Type"].Contains("Notification"))" name="isEventGridNotification" />
      <when condition="@(context.Variables.GetValueOrDefault<bool>("isEventGridSubscriptionValidation"))">
          <set-status code="200" reason="OK" />
            var events = context.Request.Body.As<string>();
            JArray a = JArray.Parse(events);
            var eventGridData = a.First["data"];
            var validationCode = eventGridData["validationCode"];
            var jOutput =
              new JObject(
                new JProperty("validationResponse", validationCode)
            return jOutput.ToString();
      <when condition="@(context.Variables.GetValueOrDefault<bool>("isEventGridNotification"))">
        <send-one-way-request mode="new">
            var events = context.Request.Body.As<string>();
            JArray a = JArray.Parse(events);
            var eventGridData = a.First["data"];
            var deviceId = eventGridData["deviceId"];
            var deviceData = eventGridData["deviceData"];
            var patientd = eventGridData["patientd"];
            return new JObject(
                new JProperty("deviceId", deviceId),
                new JProperty("data", deviceData),
                new JProperty("patientd", patientd)                ;
    <base />
    <base />
    <base />


  • We are able to leverage the use an Azure EventGrid to create an EventGrid message.
  • We are using a Function App to loop through the EventHub messages.
  • We are able to create and publish EventGrid messages within the Function App.
  • We created another Function App that subscribes to our EventGrid Topic.
  • This Function App upserts the H7 FHIR Observation Resource with the ValueQuantity data.
  • We are using a Azure API management Policy to parse the ValueQuantity data and post it to any EndPoint.

Next Steps

  • We are going to create a Durable Function to orchestrate the message flow

Azure Best Practices

Recently a client (IoT Device Manufacturer) asked me to provide them with a Best Practices guideline for migrating their IoT Services to Azure. I told them that there wasn’t a single guideline that encompasses everything, that there are many Best Practices Guidelines available.

I recommended that they start off with selecting the Architecture that is best suited to meet business requirements.

This is what I provided them.

Application Architecture Guide

1. We start off with selecting the Architectural Style.

There are several Architectural Styles that can be used. It has been my experience that the Microservices Architectural Style works best for IoT solutions. Another is the Event-driven Architectural Style.

2. Decide on the technology that will be used for Azure Applications.

  • Compute Options – refers to the hosting model for the computing resources that your application runs on. The main compute options available are Virtual Machines, App Service, Service Fabric, Azure Container Services, Azure Functions, Azure Batch, and Cloud Services.
  • Data Store – a single data store is usually not the best approach. Instead, it’s often better to store different types of data in different data stores, each focused towards a specific workload or usage pattern. These stores include Key/value stores, Document databases, Graph databases, Column-family databases, Data Analytics, Search Engine databases, Time Series databases, Object storage, and Shared files.

3. Following Design Principles is next

  • In a distributed system, failures happen. Design your application to be self healing when failures occur.
  • Build redundancy into your application, to avoid having single points of failure.
  • Minimize coordination between application services to achieve scalability.
  • Design your application so that it can scale horizontally, adding or removing new instances as demand requires.
  • Use partitioning to work around database, network, and compute limits.
  • Design your application so that the operations team has the tools they need.
  • When possible, use platform as a service (PaaS) rather than infrastructure as a service (IaaS).
  • Pick the storage technology that is the best fit for your data and how it will be used.
  • All successful applications change over time. An evolutionary design is key for continuous innovation.
  • Every design decision must be justified by a business requirement.

Software Quality is extremely important for a successful cloud application.

  • Scalability – The ability of a system to handle increased load.
  • Availability – The proportion of time that a system is functional and working.
  • Resiliency – The ability of a system to recover from failures and continue to function.
  • Management Operations processes that keep a system running in production.
  • Security – Protecting applications and data from threats.

Enforcing the use of Cloud Design Patterns will insure that your applications will be reliable, scalable, and secure.


  • Providing a high-level summary for choosing their Architectural Style and the Compute/ Data Store Technology choices, made it easier for them to understand.
  • Once they reviewed the information, I was able to answer their questons and provide them guidance on implementing Steps 1 and 2.
« Older posts
Skip to toolbar