Custom IoT Hub assignment in Device Provisioning Service

In my previous blog, I have shown how to provision a device using a real TPM using the Device Provisioning Service (DPS).

Once you are able to provision your IoT devices to the Azure IoT Platform using a DPS, a whole new world of possibilities opens up for you.

Before, you registered your device to one IoTHub. To change it, you had to go to the device and fix it. But now you are able to make a choice between multiple IoT Hubs within the cloud, dynamically!

But what strategy are you going to use?

Microsoft provides three standard strategies out of the box:

  1. Lowest latency (select the nearest IoT Hub)
  2. Evenly weighted distribution (select the IoT Hub with the least amount of devices)
  3. Static configuration (just select one yourself. This is the situation as before)

But there is a new strategy which is very flexible:

This fourth strategy makes use of a custom Azure Function which you can write yourself.

You could, for instance, access a database and read some data before you make the decision to which IoTHub you assign this device.

Let’s see how we can build a custom function ourselves and get the most out of it.

Azure Function for DPS

There is no custom trigger template supporting this kind of DPS requests yet.

Note: this feature seems to be in Preview. There is not that much documentation yet.

So I mainly used this video as a starting point for this blog. There, you can see how a NodeJS Function would look like.

But I wanted to build a Function written in C#. So the first question I had to get answered: Which function template do I have to choose? Which template is accepted by the DPS?

I have built all sorts of custom functions in the past (eg. for IoTHub monitoring). In general, an HTTP trigger works well enough.

So this is the HTTP triggered function I came up with:

#r "Newtonsoft.Json"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
  log.LogInformation("DPS processed a request.");

  string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

  //log.LogInformation(requestBody);

  dynamic data = JsonConvert.DeserializeObject(requestBody);

  log.LogInformation("Linked Hub:");
  foreach(var item in data.linkedHubs)
  {
    log.LogInformation((string) item);
  }

  var response = new Response("prov-west-ih.azure-devices.net");

  response.initialTwin.tags = new JObject();
  response.initialTwin.tags.tagOne = "one";

  response.initialTwin.properties.desired = new JObject();
  response.initialTwin.properties.desired.propOne = "ONE";
  response.initialTwin.properties.desired.propTwo = new JObject();
  response.initialTwin.properties.desired.propTwo.attribOne = "attrib one";

  return (ActionResult) new OkObjectResult(response);
}

public class Response
{
  public Response(string hostName)
  {
    iotHubHostName = hostName;
    initialTwin = new ResponseTwin();
  }

  public string iotHubHostName {get; set;}
  public ResponseTwin initialTwin {get; set;}
}

  public class ResponseTwin
  {
    public ResponseTwin()
    {
      properties = new ResponseProperties();
    }

    public dynamic tags {get; set;}
    public ResponseProperties properties {get; set;} // contains desired properties
}

public class ResponseProperties
{
  public dynamic desired {get; set;}
}

Before we proceed, let’s take a close look at this code:

  • The requestBody contains the information we have to work with. I have added an example below
  • The response is a JSON structure we have to return and which at least contains the iotHubHostName (something like ‘[your unique IoT Hub name].azure-devices.net’)
  • The response can also contain tags and desired properties which you want to pass to devices which are created in an IoT Hub the first time
  • I select just one IoT Hub, hardcoded. I could have checked the dynamic ‘data’ for the ‘data.linkedHubs’ and select one from them of course

Build the function yourself

Create an Azure Function App (Runtime Stack should be .Net) and there, create a new Azure Function based on the HTTP trigger template.

Add the code and see how it successfully compiles after saving the Azure Function.

Once added, you can select the Azure function within an enrollment.

Here you can see I was able to select two IoT Hubs and combine them with the function:

Executing the provisioning with the new custom assignment

Now try to provision your device again:

While the provisioning is in progress, you will notice some activity in the log of the Azure Function.

Here is the requestBody I got:

{
    "individualEnrollment": {
        "capabilities": {
            "iotEdge": true
        },
        "registrationId": "uno2372gsv",
        "deviceId": "uno2372gsv",
        "registrationState": {
            "registrationId": "uno2372gsv",
            "assignedHub": "prov-ih.azure-devices.net",
            "deviceId": "uno2372gsv",
            "status": "assigning",
            "lastUpdatedDateTimeUtc": "2018-12-29T12:38:05.6501906Z",
            "etag": "\"0a003c2f-0000-0000-0000-5c276aac0000\""
        },
        "attestation": {
            "type": "tpm",
            "tpm": {
                "endorsementKey": "[endorsementKey]"
            }
        },
        "etag": "\"0a002f2f-0000-0000-0000-5c276a6c0000\"",
        "provisioningStatus": "enabled",
        "reprovisionPolicy": {
            "updateHubAssignment": true,
            "migrateDeviceData": true
        },
        "createdDateTimeUtc": "2018-12-28T15:01:25.9545065Z",
        "lastUpdatedDateTimeUtc": "2018-12-29T12:37:01.3768803Z",
        "allocationPolicy": "custom",
        "iotHubs": [
            "prov-west-ih.azure-devices.net",
            "prov-ih.azure-devices.net"
        ],
        "customAllocationDefinition": {
            "webhookUrl": "https://prov-fa.azurewebsites.net/api/DpsHttpTrigger?****",
            "apiVersion": "2018-09-01-preview"
        }
    },
    "deviceRuntimeContext": {
        "registrationId": "uno2372gsv",
        "currentIotHubHostName": "prov-ih.azure-devices.net",
        "currentDeviceId": "uno2372gsv",
        "tpm": {
            "endorsementKey": "[endorsementKey]",
            "storageRootKey": "[storageRootKey]"
        }
    },
    "linkedHubs": [
        "prov-west-ih.azure-devices.net",
        "prov-ih.azure-devices.net"
    ]
}

I get a lot of information:

  • The name of the device
  • The current IoT Hub (if existing)
  • If the device is an IoT Edge device or not
  • The currently available (linked) IoT Hubs

This should be enough input to start building your own custom assignments in production!

See, after the assignment, we are notified which IoT Hub has been chosen:

We are not shown the connection secrets though 🙂

Checking out the assignment

If we go to the IoTHub, we can see the assignment:

Note: If you switched from one IoTHub to another, the device is still registered at the ioTHub.

Look at the Device twin of this device. We can see the tag and the desired properties (even the complex type):

Note: If the device is already registered one, the device twin seems not to be updated afterward.

IoT Edge: In this custom Function, I alter the Desired Properties in the Device Twin. This is just to show that it works for all types of IoT devices. The desired property of a Device Twin is not reachable by code in an IoT Edge device though. Just keep it to the Tags then.

Conclusion

As you can see, this new DPS feature makes provisioning very flexible. You are now in control which IoTHub is assigned.

The Azure Function is available as Github Gist:

https://gist.github.com/sandervandevelde/88ee8b2523a948c80c647170c26789d3.js

For more information about provisioning using multiple IoTHubs, follow this link

Advertenties

Geef een reactie

Vul je gegevens in of klik op een icoon om in te loggen.

WordPress.com logo

Je reageert onder je WordPress.com account. Log uit /  Bijwerken )

Google+ photo

Je reageert onder je Google+ account. Log uit /  Bijwerken )

Twitter-afbeelding

Je reageert onder je Twitter account. Log uit /  Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log uit /  Bijwerken )

Verbinden met %s