I am trying to create a DeviceClient using DPS and IoTHub but there does not seem to be any example of how one uses DPS to provision a device and then save the connection string such that subsequent connections are performed directly with the IoT Hub.
Have I misunderstood the purpose of the DPS ? Is it not the intention that the provisioning process is a once off step and that the device would then save its connection string for subsequent use ?
None of the examples appear to illustrate that the device saves its connection string and uses that during subsequent startup.
Is the intention that a device will always use DPS when it reboots to get a connection to it's IoTHub ?
Is there any documentation from Microsoft that explains these concepts and the intended use in different device lifecycle scenarios.
EDIT:
Below is what I have found to work when using DPS. You can use DPS for every connection but it it not necessary if you can cache the AssignedHub.
Does this look correct ? It seems to work but there may be better ways.
private async Task<DeviceClient> SetupDeviceClientAsync(Parameters parameters, EventLog logger, CancellationToken cancellationToken)
{
DeviceClient deviceClient;
switch (parameters.DeviceSecurityType.ToLowerInvariant())
{
// First time if we have no cached connection string
case "dps":
logger.WriteEntry($"Initializing via DPS");
DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, cancellationToken);
logger.WriteEntry($"dpsRegistrationResult: {dpsRegistrationResult.DeviceId}, {dpsRegistrationResult.AssignedHub}");
var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DeviceSymmetricKey);
deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod);
connectionString = "HostName=" + dpsRegistrationResult.AssignedHub + ".azure-devices.net;DeviceId=" + parameters.DeviceId + ";SharedAccessKey=" + parameters.DeviceSymmetricKey;
// Save this for next time
SaveConnectionString($"{parameters.DeviceId}\n{dpsRegistrationResult.AssignedHub}");
break;
// This does not work with DPS - don't use it in conjunction with DPS - use if for standalone IoTHub connections
case "connectionstring":
logger.WriteEntry($"Initializing via IoT hub connection string");
deviceClient = InitializeDeviceClient(parameters.PrimaryConnectionString);
break;
// Use this for subsequent connections with the saved DeviceId, SymmetricKey and AssignedHub from "dps" provisioning
case "hubinfo":
logger.WriteEntry($"Initializing via IoT hub info from DPS string");
deviceClient = DeviceClient.Create(parameters.PrimaryConnectionString, new
DeviceAuthenticationWithRegistrySymmetricKey(parameters.DeviceId, parameters.DeviceSymmetricKey), TransportType.Mqtt);
break;
default:
throw new ArgumentException($"Unrecognized value for device provisioning received: {parameters.DeviceSecurityType}." +
$" It should be either \"dps\", \"hubinf\" or \"connectionString\" (case-insensitive).");
}
return deviceClient;
}
Provisioning through DPS can be a one-time process, but there are cases in which you want to contact DPS again regularly:
In most cases, you can try the connection string that was obtained in an earlier DPS provisioning. If that connection fails, you can contact DPS again to see if you get a new connection string.
It's described in this paragraph of the docs. How you save the connection string will vary per use case. Because IoT devices are not as standardised as regular computing hardware, how you store the connection string will be different each time. There is a C# sample though that stores the provisioning result and contains logic to reprovision in case of an error.