The implemented provisioning relies on Azure IoT Hub Device Provisioning Service.
The simulator has the capability to contact a DPS with the provided configuration and get the connection settings for the assigned Azure IoT Hub. The Azure IoT Hub is assigned according to the rules set in the DPS.
The simulator has been designed to work with different provisioning use cases:
-
If the simulator has no connection string, a provisioning process is initiated. This process requires a DPS configuration to be set.
The DPS configuration can be provided by:
- environment variables (might be useful in containerized platforms. Pay attention though to the processes allowed to read the environment variables.).
- command line parameters, that will overwrite any existing environment variables setting (recommended for not containerized targets.).
- if none of the previous settings are found, a
dpssettings.json
file will be loaded (recommended only for development platforms, unless the JSON is encrypted or stored in safe location.).
-
If the simulator finds a connection string, the provisioning process is skipped and the found connection string is used. This avoids not necessary provisionning requests. In case a device needs to be reprovisionned, you only need to delete all the IoT Hub connection settings and the simulator will understand it needs to run the provisionning process again. Just keep in mind that to make this possible, the DPS settings need to be set.
NOTE
With Symmetric Key authentication, besides the DPS settings, the environment variables allow to configure the
device Id
. The environment value will overwrite the value in the configuration filedevicesettings.json
.
If the provisioning process succeeds, it will create a device identity in the corresponding Azure IoT Hub. The DPS will send the keys and the connection string back to the device.
These connection settings may be stored at the device level to avoid new reprovisioning processes. In production environments, they should be stored in a secured way.
The simulator saves the connection string in the devicesettings.json
file.
It is persisted in clear for development purposes but keep in mind this data should be protected more securely in production environments.
{
"deviceId": "",
"connectionString": "",
"simulationSettings": {
"enableLatencyTests": false,
"latencyTestsFrecuency": 30,
"enableDevice": true,
"enableModules": false,
"enableTelemetryMessages": false,
"telemetryFrecuency": 10,
"enableErrorMessages": false,
"errorFrecuency": 20,
"enableCommissioningMessages": false,
"commissioningFrecuency": 30,
"enableTwinReportedMessages": false,
"twinReportedMessagesFrecuency": 60,
"enableReadingTwinProperties": false,
"enableC2DDirectMethods": true,
"enableC2DMessages": true,
"enableTwinPropertiesDesiredChangesNotifications": true
}
}
If module identities are created, they will have their own connection string. The connection string (and keys) will be generated dynamically by the process (device simulator and IoT Hub REST API).
If you need to create module identities, do not forget to set the moduleId
field in the modulesettings.json
file.
The deviceId
has to be set too (either by the environment variable or in the devicesettings.json
file).
The simulator covers two authentication types:
- symmetric keys
- CA X509 certificates
Each authentication type requires different DPS settings.
The list of the environment variables to set are:
- TRANSPORT_TYPE, Mqtt
- DPS_SECURITY_TYPE, SymmetricKey
- DPS_IDSCOPE, the Id Scope of the DPS
- PROVISIONING_REGISTRATION_ID, the device Id (optional. If not set here, it should be set in the devicesettings.json file.)
- PRIMARY_SYMMETRIC_KEY, the primary pey of the DPS
Linux (bash)
export TRANSPORT_TYPE='Mqtt'
export DPS_SECURITY_TYPE='SymmetricKey'
export DPS_IDSCOPE=[YOUR DPS ID SCOPE]
export PRIMARY_SYMMETRIC_KEY=[YOUR PRIMMARY KEY]
dotnet IoT.Simulator.dll
Windows (cmd)
set TRANSPORT_TYPE='Mqtt'
set DPS_SECURITY_TYPE='SymmetricKey'
set DPS_IDSCOPE=[YOUR DPS ID SCOPE]
set PRIMARY_SYMMETRIC_KEY=[YOUR PRIMMARY KEY]
dotnet IoT.Simulator.dll
NOTE
If the primary key is stored at some point, it should be saved in a secured location.
To visualize the required settings, you can type:
dotnet IoT.Simulator.dll symmetrickey --help
The required parameters are the same:
- -s, the Id Scope of the DPS
- -p, the primary key of the DPS
Other required parameters but with default values:
- -e, enrollment type (default and only possible now, Group)
- -g, global DPS endpoint (default value, global.azure-devices-provisioning.net)
- -t, transport type (default valyue, MQTT. The only one globally tested for now)
Linux (bash)
dotnet IoT.Simulator.dll symmetrickey -s [YOUR ID SCOPE] -p [YOUR PRIMMARY KEY] -t Mqtt -e Group
Windows (cmd)
dotnet IoT.Simulator.dll symmetrickey -s [YOUR ID SCOPE] -p [YOUR PRIMMARY KEY] -t Mqtt -e Group
DPS settings can also be provided through a configuration file. It looks like this:
{
"dpsSettings": {
"enrollmentType": "Group", //Group
"groupEnrollmentSettings": {
"securityType": "SymmetricKey", //SymmetricKey, X509CA
"symmetricKeySettings": {
"idScope": "0ne000XXXXX",
"primaryKey": "ii3VEjzWZpxhPlWT85O8sg/hZvqk2sNPHPDsP+M9v73BKs9NQHky+Tvg/IFNu1QEWqt5OPZuz1Ia/9IM6R+rbb==",
"enrollmentType": "Group",
"globalDeviceEndpoint": "global.azure-devices-provisioning.net",
"transportType": "Mqtt"
},
"CAX509Settings": {
"idScope": "",
"deviceX509Path": "",
"password": "",
"enrollmentType": "Group",
"globalDeviceEndpoint": "global.azure-devices-provisioning.net",
"transportType": "Mqtt"
}
}
}
}
Parameters like enrollmentType
, globalDeviceEndpoint
and transportType
cannot be changed for now.
Upcoming versions should allow more possibilities.
The list of the environment variables to set are:
- TRANSPORT_TYPE, Mqtt
- DPS_SECURITY_TYPE, SymmetricKey
- DPS_IDSCOPE, the Id Scope of the DPS
- DEVICE_CERTIFICATE_PATH, leaf certificate path
- DEVICE_CERTIFICATE_PASSWORD, leaf certificate password
Linux (bash)
export TRANSPORT_TYPE='Mqtt'
export DPS_SECURITY_TYPE='X509CA'
export DPS_IDSCOPE=[YOUR DPS ID SCOPE]
export DEVICE_CERTIFICATE_PATH=[Leaf certificate path]
export DEVICE_CERTIFICATE_PASSWORD=[Certificate password]
dotnet IoT.Simulator.dll
Windows (cmd)
set TRANSPORT_TYPE='Mqtt'
set DPS_SECURITY_TYPE='X509CA'
set DPS_IDSCOPE=[YOUR DPS ID SCOPE]
set DEVICE_CERTIFICATE_PATH=[Leaf certificate path]
set DEVICE_CERTIFICATE_PASSWORD=[Certificate password]
dotnet IoT.Simulator.dll
NOTE
If the primary key is stored at some point, it should be saved in a secured location.
To visualize the required settings, you can type:
dotnet IoT.Simulator.dll x509ca --help
The required parameters are the same:
- -s, the Id Scope of the DPS
- -c, the leaf certificate path
- -p, certificate password
Other required parameters by with default values:
- -e, enrollment type (default and only possible now, Group)
- -g, global DPS endpoint (default value, global.azure-devices-provisioning.net)
- -t, transport type (default valyue, MQTT. The only one globally tested for now)
Linux (bash)
dotnet IoT.Simulator.dll x509ca -s [YOUR ID SCOPE] -c [your leaf certificate path] -p [your certificate password] -t Mqtt -e Group
Windows (cmd)
dotnet IoT.Simulator.dll x509ca -s [YOUR ID SCOPE] -c [your leaf certificate path] -p [your certificate password] -t Mqtt -e Group
DPS settings can also be provided through a configuration file. It looks like this:
{
"dpsSettings": {
"enrollmentType": "Group", //Group
"groupEnrollmentSettings": {
"securityType": "X509CA", //SymmetricKey, X509CA
"symmetricKeySettings": {
"idScope": "",
"primaryKey": "",
"enrollmentType": "Group",
"globalDeviceEndpoint": "global.azure-devices-provisioning.net",
"transportType": "Mqtt"
},
"CAX509Settings": {
"idScope": "0ne000E3E14",
"deviceX509Path": "X509\\new-device.devx5092.cert.pfx",
"password": "1234",
"enrollmentType": "Group",
"globalDeviceEndpoint": "global.azure-devices-provisioning.net",
"transportType": "Mqtt"
}
}
}
}
Parameters like enrollmentType
, globalDeviceEndpoint
and transportType
cannot be changed for now.
Upcoming versions should allow more possibilities.
NOTE
It seems that, when using CA X509 authentication with DPS, the device is created with symmetric keys. Do not be surprised by this. For now, is the normal behavior. This said, it has been reported to Microsoft in order to know whether there will be a way in the future to configure this.
At the time this post has been written, it did not seem to be a way to create device modules (a.k.a. module identities) during the provisioning process.
This can be implemented by code but it requires one of the steps below:
- reference the Azure IoT Hub Service SDK (which requires in turn a SAS connection string to the IoT Hub with specific rights....not really matching the security standards we are looking for in the implemented scenario with DPS).
- reference a REST API taking in charge the creation of the device modules. The provisioned device could call that REST API once the DPS would have finished its job and that the device was provisioned.
A version of a REST endpoint implementing this has been included in the repository here.
...
public async Task<Module> AddModuleToDevice([FromBody] ProvisionModuleRequest value)
...
The used REST API structure is based on a REST API Template for .NET:
The authentication mode of module identities is totally decorrelated from devices'. For now, this implementation covers symmetric keys only for module identities.
One of the straightforward use cases of this simulator would be to create a set of devices dynamically. The easiest way to achieve this would be:
- create a Docker image with the IoT Hub REST API
- create a Docker image with the simulator
- create a container based on the image of the IoT Hub REST API
- create a script that iterates to create containers based on the image of the simulator (the device Id may be provided by an environment variable)
NOTE
Check that the simulator containers can reach the IoT Hub REST API if you need to create module identities dynamically.
Simple script samples to create all this are provided here.
Other more enterprise oriented architectures could be considered with ACI or AKS.