azure-iot-hubasp.net-core-6.0azure-iot-dps

Azure IOT Device Provisioning - How to get the IOT Device Primary Connection String from the Device/Provisoning Client SDK?


I'm testing IOT Device Provisioning using a C# sample code quickstart "Provision a simulated TPM device" MS Docs reference Quickstart: Provision a simulated TPM device

I can successfully provision the device, but I want the device provisioning service to some how return the IOT Device Primary (or Secondary) Connection string on completion of the registration in order that the next time I connect to Azure, I can simply connect directly to the IOT Hub and not have to keep re-provisoning the device as a means of simply getting connected to Azure.

I would store the connection string in an encrypted local database so the connection string information remains secure. Only if the connection string is no longer valid after a period of time when re-connecting to the IOT Hub, would I then call a method to re-provision the device and repeat the above process.

The MS Docs says "We recommend not provisioning on every reboot of the device, as this could hit the service throttling limits especially when reprovisioning several thousands or millions of devices at once. Instead you should attempt to use the Device Registration Status Lookup API and try to connect with that information to IoT Hub" see Runtime Registration - Device Registration Status Lookup

I really don't understand the documentation for the "Device Registration Status Lookup" API and hoping there is another way to obtain the connecton string without calling this service?

If there is no other option, then from reading the docs on this API, I cant find anything that explains how to construct the IOT Device connection string from the information returned from this API service? ( i dont see any json properties returned mentioned in the docs that resmebles a connection string)

Secondly I dont see any code samples for calling this service using C#, the MS docs only gives the HTTP Uri reference which is unhelpful considering I'd be calling the code in C# using HTTP Client class, not using postman.

Sample code below shows two lines of commented out text where I'm looking for some kind of solution to retrieve the IOT Device Primary Connection string, straight after the device had been provisioned:

public async Task RunSampleAsync()
{
    Console.WriteLine("Initializing security using the local TPM...");
    using SecurityProviderTpm security = new SecurityProviderTpmHsm("MyRegistraton123456"); // Registration Id

    Console.WriteLine($"Initializing the device provisioning client...");

    using var transport = GetTransportHandler();
    ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create(
        "global.azure-devices-provisioning.net", // GlobalDeviceEndpoint
        "MyIdScope", // IdScope
        security,
        transport);

    Console.WriteLine($"Initialized for registration Id {security.GetRegistrationID()}.");

    Console.WriteLine("Registering with the device provisioning service... ");
    DeviceRegistrationResult result = await provClient.RegisterAsync();

    // ------------------------------------------------------------------------------
    // Is it possible to get the connection string from the DeviceRegistrationResult?
    // ------------------------------------------------------------------------------

    Console.WriteLine($"Registration status: {result.Status}.");
    if (result.Status != ProvisioningRegistrationStatusType.Assigned)
    {
        Console.WriteLine($"Registration status did not assign a hub, so exiting this sample.");
        return;
    }

    Console.WriteLine($"Device {result.DeviceId} registered to {result.AssignedHub}.");

    Console.WriteLine("Creating TPM authentication for IoT Hub...");
    IAuthenticationMethod auth = new DeviceAuthenticationWithTpm(result.DeviceId, security);

    Console.WriteLine($"Testing the provisioned device with IoT Hub...");
    Console.WriteLine("Assigned Hub" + result.AssignedHub);
    using DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, TransportType.Amqp_WebSocket_Only);

    // ------------------------------------------------------------------------------------
    // Is it possible to get the connection string from the active DeviceClient connection?
    // ------------------------------------------------------------------------------------

    Console.WriteLine("Sending a telemetry message...");
    using var message = new Message(Encoding.UTF8.GetBytes("TestMessage"));
    await iotClient.SendEventAsync(message);

    await iotClient.CloseAsync();
    Console.WriteLine("Finished.");
}

Just to confirm, the primary/secondary connection string I'm tryin to obtain is the one that is unique to that particular IOT Device, which allows it to connect with the IOT Hub using the Microsoft.Azure.Devices.Client SDK i.e. like the screenshot below:

enter image description here


Solution

  • Well, the answer was staring at me, within the code sample shown in my question!

    So it seems my approach was wrong in that we dont use the IOT Device connection string for subsequent calls after registration. In my environment and the code sample shown in my question, we're using the TPM as our means of authentication, so we keep using this as it will always produce the same output as long as we're feeding in the same "RegistrationId".

    Using my original code sample, for testing purposes we allow the IOT device to complete the registration, it then sends a test message "TestMessage" and we then close the client connection.

    I then since added a new method "SendMessageAfterRegistration" call and placed this at the end of this sequence, as shown below:

    enter image description here

    Bearing in mind that once the we have first registered, we'd store the details of the DeviceId and the Assigned Hub somehwere locally, database or something.

    Then I added the following test method underneath the code sample from my question:

    private async Task SendMessageAfterRegistration(string deviceId, string assignedHub)
    {
        Console.WriteLine("Initializing security using the local TPM...");
        using SecurityProviderTpm security = new SecurityProviderTpmHsm("MyRegistraton150820221815"); // Registration Id
    
        Console.WriteLine("Creating TPM authentication for IoT Hub...");
        IAuthenticationMethod auth = new DeviceAuthenticationWithTpm(deviceId, security);
    
        // Create an instance of the Device Client
        using DeviceClient iotClient = DeviceClient.Create(assignedHub, auth, TransportType.Amqp_WebSocket_Only);
    
        Console.WriteLine("Sending a telemetry message...");
        using var message = new Message(Encoding.UTF8.GetBytes("TestMessage-WithNewConnection"));
        await iotClient.SendEventAsync(message);
    }
    

    In the new method "SendMessageAfterRegistration" you'll see that we call up the SecurityProviderTpm class and feed in the same orignal Registration Id as a string. Using the security value returned we then call up the IAuthenticationMethod class (these ar both baked into the device provisioning SDK)

    So the "auth" result returned from IAuthenticationMethod is then used to create the DeviceClient, from there we can send the second test message "TestMessage-WithNewConnection"

    I have a seperate project that recieves the Device To Cloud messages so I used this to confirm we have the result we're looking for.