Azure IoT Device Provisioning Service
What Azure DPS Is
The Azure IoT Device Provisioning Service (DPS) is a helper service for Azure IoT Hub that enables zero-touch, just-in-time provisioning of devices at scale. It handles the job of registering a device with the correct IoT Hub, returning connection information to the device, and doing all of this without any human intervention after the initial configuration.
DPS sits between device manufacturing and the cloud. Devices contact DPS on first boot, prove their identity through an attestation mechanism, and receive back the hostname of the IoT Hub they should connect to along with whatever initial configuration the provisioning process specifies. From that point forward, the device communicates directly with its assigned IoT Hub. DPS is not in the data path after provisioning completes.
The service operates as a globally distributed endpoint. Devices contact a single well-known URL regardless of where they are geographically, and DPS handles assignment logic behind that endpoint. This global endpoint is global.azure-devices-provisioning.net, and devices use a scope ID to identify which DPS instance they belong to.
The Problem DPS Solves
Before DPS, provisioning IoT devices at scale required manual steps that did not compose well with manufacturing pipelines. To register a device in IoT Hub, an operator had to create the device identity, retrieve the connection string, and either embed that connection string in firmware before the device shipped or manually configure it during installation. At the scale of hundreds of devices this is tedious; at the scale of hundreds of thousands it is impossible to do reliably.
The manufacturing problem is particularly sharp. A factory producing smart meters, industrial sensors, or connected appliances would ideally flash identical firmware onto every unit on the production line. Identical firmware is cheaper to produce, easier to test, and simpler to manage. But if each device needs a unique connection string embedded at the factory, every unit requires individual attention during manufacturing. This adds cost and introduces error.
DPS resolves this by decoupling device manufacturing from cloud configuration. Every device gets identical firmware that contains the DPS global endpoint, the scope ID, and its attestation credentials. The specific IoT Hub assignment happens at first boot, not at the factory. This means manufacturing runs at full speed without per-device customization, and cloud operators control device assignment through DPS policy rather than through factory floor processes.
Multi-hub scenarios benefit similarly. A global deployment might run IoT Hubs in multiple regions for latency, compliance, or capacity reasons. Without DPS, routing a device to the right hub requires knowing in advance which hub that device belongs to and configuring it accordingly. With DPS, an allocation policy handles this decision dynamically, routing each device to the most appropriate hub based on geography, workload, or custom logic.
Attestation Mechanisms
Attestation is how a device proves its identity to DPS. DPS supports three mechanisms, each with different security properties, operational complexity, and hardware requirements. Choosing the right mechanism depends on the security requirements of the deployment, the capabilities of the device hardware, and the operational constraints of the manufacturing and provisioning process.
X.509 Certificates
X.509 certificate attestation uses the standard PKI certificate chain model. Each device presents a certificate during provisioning, and DPS validates that the certificate chains up to a trusted root or intermediate CA that has been uploaded to the DPS enrollment.
There are two ways to use X.509 with DPS. In an individual enrollment, a specific leaf certificate is registered for a specific device. In a group enrollment, an intermediate or root CA certificate is registered, and any device that presents a leaf certificate signed by that CA is eligible for provisioning. Group enrollments are more practical at manufacturing scale because the CA private key is held by the certificate authority, not embedded in devices, and each device only needs its own unique leaf certificate.
Certificate rotation is supported. Devices can re-provision with new certificates before their current certificates expire, and DPS can be configured to accept certificates from updated CAs. This makes X.509 the best fit for long-lived deployments where certificate lifecycle management needs to be planned from the start.
The operational requirement for X.509 is that each device needs a unique leaf certificate installed during manufacturing. This requires a certificate issuance step in the manufacturing pipeline, which adds process complexity but produces the strongest security properties of the three mechanisms.
Symmetric Keys
Symmetric key attestation is the simplest mechanism to implement and requires no special hardware. A group enrollment is created in DPS with a primary and secondary master key. Each device gets a per-device key derived from the master key using HMAC-SHA256 with the device’s registration ID as the message. The device presents this derived key during provisioning.
The derivation formula means that each device has a unique key even though all keys share a common root. The master key itself never leaves DPS or the provisioning pipeline; devices hold only their derived key. This separation is important: if a single device’s key is compromised, it does not expose the keys of other devices. However, it does not provide the same level of assurance as hardware-backed attestation, since the derived key is a software credential that lives in device firmware.
Symmetric keys work well for development, testing, prototyping, and deployments where the threat model accepts software credentials. They are also appropriate for devices that lack the hardware or firmware capability to support X.509 certificates or TPM. The implementation simplicity is a genuine advantage when getting started.
The most important security rule with symmetric keys: never embed the group master key in device firmware. The derived per-device key is what goes on the device. The master key stays in secure storage and is used only during the key derivation step of manufacturing or provisioning.
TPM Attestation
TPM attestation is built on the Trusted Platform Module, a hardware security chip present on many enterprise-grade devices. The TPM holds an endorsement key pair where the private key is generated inside the chip and cannot be extracted. During provisioning, DPS issues a nonce that can only be decrypted by the device’s TPM, proving that the device physically possesses the chip associated with its registered endorsement key.
TPM attestation is tamper-resistant in a way that software credentials are not. Even if an attacker gains full access to the device’s operating system and filesystem, they cannot extract the private endorsement key. This makes TPM the strongest attestation mechanism for high-security scenarios, particularly for enterprise endpoints and industrial devices where physical tampering or firmware attacks are realistic threats.
The constraint is hardware dependency. TPM requires a physical TPM chip or firmware TPM, which not all IoT hardware includes. Simulated TPM environments exist for development purposes, but production deployments need real hardware support.
Mechanism Comparison
| Mechanism | Hardware Required | Key Extraction Risk | Best Fit |
|---|---|---|---|
| X.509 Certificates | No (cert in firmware/storage) | Low (leaf cert per device, CA protected) | Large fleets, long-lived devices, regulatory requirements |
| Symmetric Keys | No | Moderate (derived key in firmware) | Development, simple deployments, constrained hardware |
| TPM | Yes (TPM chip) | Very low (private key stays in chip) | Enterprise devices, high-security industrial scenarios |
Enrollment Types
An enrollment is a record in DPS that authorizes a device or group of devices to provision. Every device that successfully provisions must match an enrollment record. DPS supports two enrollment types that differ in scope and configuration.
Individual Enrollment
An individual enrollment registers a single device. The record contains one device’s attestation credentials, the IoT Hub assignment for that device, and any initial twin properties or tags that should be set when the device first connects. Individual enrollments are specific: they match exactly one device by registration ID.
Individual enrollments are appropriate for high-value or high-trust devices where granular control over each unit matters. Security cameras in sensitive facilities, industrial controllers with significant operational authority, and medical devices are examples where the ability to configure, audit, and disable each device individually justifies the overhead of per-device enrollment management. Individual enrollments can also be used during development when working with a small number of test devices.
Group Enrollment
A group enrollment authorizes multiple devices that share a common attestation root. For X.509, this means devices whose certificates chain to the same registered CA. For symmetric keys, this means devices whose derived keys share the same master key. Group enrollments can cover thousands or millions of devices under a single enrollment record.
Group enrollments are the practical choice for manufacturing at scale. A device manufacturer can create one group enrollment per product line or per customer, upload the CA certificate or master key, and then provision any number of devices without creating individual records. Initial twin configuration and IoT Hub assignment are set once at the group level and applied consistently across all devices in the group.
Individual enrollments take precedence over group enrollments. If a device matches both an individual enrollment and a group enrollment, DPS uses the individual enrollment. This allows exceptions: a specific device can have its IoT Hub assignment or initial configuration overridden without affecting the rest of the group.
Allocation Policies
After DPS validates a device’s attestation, it must decide which IoT Hub to assign the device to. This decision is controlled by the allocation policy configured on the DPS instance. A DPS instance can be linked to multiple IoT Hubs, and the allocation policy determines how devices are distributed across them.
Evenly Weighted Distribution
The default policy distributes devices across all linked IoT Hubs with approximately equal probability. Each hub has a weight value, and DPS assigns devices proportionally. With three hubs at equal weight, roughly one third of devices go to each hub over time.
This policy works well when all linked hubs are equivalent from the application’s perspective and the goal is to spread load. It does not consider geography, network conditions, or current hub utilization; it is purely a statistical distribution. It is simple to understand and predictable in aggregate, though individual device assignments are not deterministic.
Lowest Latency
The lowest latency policy assigns each device to the IoT Hub that will have the lowest network latency for that device, based on the geographic location of the device’s DPS connection and the locations of the linked hubs. A device provisioning from Asia will be directed toward an Asia-region hub, while a device provisioning from Europe will be directed toward a European hub.
This policy reduces device-to-hub round-trip times during operation, which matters for time-sensitive telemetry and commands. It also provides a natural form of geographic data residency, since devices connect to regionally proximate hubs, though this is an emergent property rather than a compliance-grade data residency control.
Static Configuration
Static configuration pins all devices to a specific IoT Hub regardless of geography or load. This is appropriate when there is only one IoT Hub, when regulatory requirements mandate that all data goes to a specific hub, or when the application requires all devices to be on the same hub for operational simplicity.
Custom Allocation with Azure Functions
Custom allocation allows an Azure Function to determine the IoT Hub assignment. When a device registers, DPS calls the function with information about the device including its registration ID, attestation type, and any custom data in its registration payload. The function returns the hub assignment and can also return initial twin properties.
Custom allocation supports scenarios that the built-in policies cannot handle. A multi-tenant SaaS product might use custom allocation to route devices to tenant-specific IoT Hubs based on a tenant identifier in the device’s registration payload. A phased rollout might use custom allocation to route a percentage of new devices to a new hub while keeping existing devices on the old hub. A custom function can query external data sources, apply business logic, and return context-sensitive assignments.
Policy Comparison
| Policy | Key Characteristic | Best Fit |
|---|---|---|
| Evenly Weighted | Statistical distribution across linked hubs | Load spreading when hubs are equivalent |
| Lowest Latency | Routes to geographically nearest hub | Global deployments with latency sensitivity |
| Static Configuration | All devices to one hub | Single-hub deployments, strict data routing |
| Custom Allocation | Azure Function determines assignment | Multi-tenant, business rule-driven routing |
The Provisioning Flow
Understanding the provisioning flow end to end clarifies what DPS does and does not do, and where failures can occur.
First Boot Registration
When a device boots for the first time, it has no IoT Hub connection information. The provisioning flow begins with the device contacting the DPS global endpoint at global.azure-devices-provisioning.net using the MQTT, AMQP, or HTTPS protocol. The device presents its scope ID, registration ID, and attestation credentials.
DPS receives the registration request and validates the attestation. For X.509, it validates the certificate chain against enrolled CAs. For symmetric keys, it derives the expected key from the master key and compares. For TPM, it performs the nonce challenge-response. If attestation fails, the registration request is rejected and the device receives an error.
After successful attestation, DPS checks whether the device matches an enrollment record. If it matches an individual enrollment, that enrollment’s configuration applies. If it matches a group enrollment, the group’s configuration applies. If no enrollment matches, the registration is rejected.
DPS then applies the allocation policy to select the target IoT Hub, creates the device identity in that hub if it does not already exist, applies any initial device twin properties specified in the enrollment, and returns the IoT Hub hostname and device ID to the device. The device stores this connection information and uses it to open a direct connection to the assigned IoT Hub. From this point forward, all device communication goes to the IoT Hub directly; DPS is no longer involved unless the device re-provisions.
Provisioning Flow Summary
| Step | Who Acts | What Happens |
|---|---|---|
| 1. Device boots | Device | Checks local storage for cached IoT Hub connection info |
| 2. Contact DPS | Device | Connects to global.azure-devices-provisioning.net with scope ID |
| 3. Present attestation | Device | Sends registration ID and attestation credentials |
| 4. Validate attestation | DPS | Verifies certificate chain, derives expected symmetric key, or issues TPM nonce |
| 5. Match enrollment | DPS | Finds individual or group enrollment record; rejects if none found |
| 6. Apply allocation policy | DPS | Selects target IoT Hub according to configured policy |
| 7. Create device identity | DPS | Creates device in IoT Hub if not already present; applies initial twin state |
| 8. Return connection info | DPS | Returns IoT Hub hostname and device ID to device |
| 9. Cache and connect | Device | Stores hub info locally; opens direct connection to IoT Hub |
| 10. Normal operation | Device | All subsequent communication goes directly to IoT Hub |
This table clarifies an important detail in step 9: the device is responsible for persisting the IoT Hub hostname it receives from DPS. If the device has no persistent storage, it must re-provision on every boot. If it does have storage, it should cache the result and only re-provision when the cached connection fails, rather than contacting DPS on every startup. Unnecessary re-provisioning adds latency at boot and consumes DPS quota.
Re-Provisioning
Re-provisioning occurs when a device contacts DPS again after its initial provisioning. This might happen because the device rebooted and lost its stored connection information, because it received an explicit re-provisioning command, or because it needs to migrate to a different IoT Hub.
DPS handles re-provisioning according to the re-provisioning policy on the enrollment. Three options are available. The first keeps the device on its current hub and preserves all device twin data; this is appropriate when re-provisioning is expected to be a recovery operation and continuity matters. The second migrates the device to whatever hub the current allocation policy selects, preserving twin data from the previous hub; this is appropriate for planned migrations across hubs. The third resets the device to its initial state and assigns it to a hub with fresh twin data; this is appropriate when re-provisioning should treat the device as if it is new, such as when a device is resold or reassigned.
Choosing the right re-provisioning policy requires thinking about what re-provisioning means in the specific deployment context. A device that re-provisions because it rebooted probably wants the first option. A device being physically moved from one region to another probably wants the second. A device being factory-reset for a new customer probably wants the third.
Re-Provisioning Policy Comparison
| Policy | Twin Data | Hub Assignment | Best Fit |
|---|---|---|---|
| Keep data (never reprovision) | Preserved | Stays on current hub | Recovery from lost connection state |
| Migrate and preserve | Copied from old hub | Selected by allocation policy | Planned hub migrations |
| Reprovision and reset | Cleared | Selected by allocation policy | Device reassignment, factory reset |
C# SDK Patterns
The Azure IoT SDK for .NET provides both device-side and service-side clients for working with DPS.
Device-Side: Symmetric Key Provisioning
The device-side client is ProvisioningDeviceClient. To use symmetric key attestation, create a SecurityProviderSymmetricKey with the registration ID and the derived device key, then create the provisioning client pointing at the global endpoint with the scope ID.
using Microsoft.Azure.Devices.Provisioning.Client;
using Microsoft.Azure.Devices.Provisioning.Client.Transport;
using Microsoft.Azure.Devices.Shared;
string globalEndpoint = "global.azure-devices-provisioning.net";
string scopeId = "<your-scope-id>";
string registrationId = "<device-registration-id>";
string derivedDeviceKey = "<per-device-derived-key>";
using var security = new SecurityProviderSymmetricKey(
registrationId,
derivedDeviceKey,
null); // secondary key is optional
using var transport = new ProvisioningTransportHandlerMqtt();
var provisioningClient = ProvisioningDeviceClient.Create(
globalEndpoint,
scopeId,
security,
transport);
DeviceRegistrationResult result = await provisioningClient.RegisterAsync();
if (result.Status != ProvisioningRegistrationStatusType.Assigned)
{
throw new Exception($"Provisioning failed: {result.Status} - {result.ErrorMessage}");
}
Console.WriteLine($"Assigned to hub: {result.AssignedHub}");
Console.WriteLine($"Device ID: {result.DeviceId}");
After provisioning, use the returned AssignedHub and DeviceId to build the IoT Hub connection and connect the device.
using Microsoft.Azure.Devices.Client;
// Build authentication from the same symmetric key security provider
IAuthenticationMethod auth = new DeviceAuthenticationWithRegistrySymmetricKey(
result.DeviceId,
derivedDeviceKey);
using var deviceClient = DeviceClient.Create(
result.AssignedHub,
auth,
TransportType.Mqtt);
await deviceClient.OpenAsync();
// Device is now connected to the assigned IoT Hub
Device-Side: X.509 Certificate Provisioning
For X.509 attestation, replace the security provider with SecurityProviderX509Certificate, loading the device certificate from a file or certificate store.
using System.Security.Cryptography.X509Certificates;
// Load device certificate (with private key)
var certificate = new X509Certificate2("device-cert.pfx", "cert-password");
using var security = new SecurityProviderX509Certificate(certificate);
using var transport = new ProvisioningTransportHandlerMqtt();
var provisioningClient = ProvisioningDeviceClient.Create(
globalEndpoint,
scopeId,
security,
transport);
DeviceRegistrationResult result = await provisioningClient.RegisterAsync();
if (result.Status != ProvisioningRegistrationStatusType.Assigned)
{
throw new Exception($"Provisioning failed: {result.Status}");
}
// Connect to IoT Hub using the certificate for authentication
IAuthenticationMethod auth = new DeviceAuthenticationWithX509Certificate(
result.DeviceId,
certificate);
using var deviceClient = DeviceClient.Create(
result.AssignedHub,
auth,
TransportType.Mqtt);
await deviceClient.OpenAsync();
Device-Side: Custom Registration Payload
DPS allows devices to send a custom JSON payload with the registration request. This payload is forwarded to custom allocation functions and can influence hub assignment decisions.
var registrationPayload = new
{
tenantId = "tenant-abc",
region = "eu-west",
deviceModel = "SensorUnit-v2"
};
string payloadJson = System.Text.Json.JsonSerializer.Serialize(registrationPayload);
DeviceRegistrationResult result = await provisioningClient.RegisterAsync(payloadJson);
The custom allocation Azure Function receives this payload in the request body and can use it to make routing decisions.
Service-Side: Managing Enrollments
The ProvisioningServiceClient handles enrollment management from the service side. Use it to create, query, and delete enrollments.
using Microsoft.Azure.Devices.Provisioning.Service;
string dpsConnectionString = "<your-dps-connection-string>";
using var serviceClient = ProvisioningServiceClient.CreateFromConnectionString(dpsConnectionString);
// Create an individual enrollment with symmetric key attestation
var attestation = new SymmetricKeyAttestation(primaryKey: null, secondaryKey: null);
// Passing null generates keys automatically
var enrollment = new IndividualEnrollment(
registrationId: "my-device-001",
attestation: attestation)
{
DeviceId = "my-device-001",
InitialTwinState = new TwinState(
tags: null,
desiredProperties: new TwinCollection(@"{ ""targetTemperature"": 22 }"))
};
IndividualEnrollment createdEnrollment =
await serviceClient.CreateOrUpdateIndividualEnrollmentAsync(enrollment);
Console.WriteLine($"Primary key: {createdEnrollment.Attestation.Cast<SymmetricKeyAttestation>().PrimaryKey}");
To create a group enrollment for X.509:
// Upload the CA certificate verification is done separately through the Azure portal or CLI
// Then create the group enrollment referencing the verified CA
var x509Attestation = X509Attestation.CreateFromRootCertificates(
primary: new X509Certificate2("root-ca.cer"));
var groupEnrollment = new EnrollmentGroup(
enrollmentGroupId: "factory-batch-2025",
attestation: x509Attestation)
{
AllocationPolicy = AllocationPolicy.GeoLatency
};
EnrollmentGroup createdGroup =
await serviceClient.CreateOrUpdateEnrollmentGroupAsync(groupEnrollment);
To query registration records and check device status:
// Query individual device registration status
DeviceRegistrationState state =
await serviceClient.GetDeviceRegistrationStateAsync("my-device-001");
Console.WriteLine($"Status: {state.Status}");
Console.WriteLine($"Assigned hub: {state.AssignedHub}");
Console.WriteLine($"Last updated: {state.LastUpdatedDateTimeUtc}");
Persisting Provisioning Results Across Reboots
Devices that provision on every boot put unnecessary pressure on DPS quota and add latency to startup. The better pattern is to cache the provisioning result locally and only re-provision when the cached result is stale or when an IoT Hub connection attempt fails consistently.
using System.Text.Json;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Provisioning.Client;
using Microsoft.Azure.Devices.Provisioning.Client.Transport;
using Microsoft.Azure.Devices.Shared;
record ProvisioningCache(string AssignedHub, string DeviceId);
static async Task<DeviceClient> GetDeviceClientAsync(
string scopeId,
string registrationId,
string derivedDeviceKey,
string cacheFilePath)
{
ProvisioningCache cache = null;
// Try loading cached result first
if (File.Exists(cacheFilePath))
{
string json = await File.ReadAllTextAsync(cacheFilePath);
cache = JsonSerializer.Deserialize<ProvisioningCache>(json);
}
if (cache != null)
{
// Attempt to connect with cached info
var auth = new DeviceAuthenticationWithRegistrySymmetricKey(
cache.DeviceId, derivedDeviceKey);
var client = DeviceClient.Create(cache.AssignedHub, auth, TransportType.Mqtt);
try
{
await client.OpenAsync();
return client; // Cache is valid
}
catch
{
// Cached info is stale; fall through to re-provision
await client.DisposeAsync();
}
}
// Provision through DPS
using var security = new SecurityProviderSymmetricKey(
registrationId, derivedDeviceKey, null);
using var transport = new ProvisioningTransportHandlerMqtt();
var provisioningClient = ProvisioningDeviceClient.Create(
"global.azure-devices-provisioning.net", scopeId, security, transport);
DeviceRegistrationResult result = await provisioningClient.RegisterAsync();
if (result.Status != ProvisioningRegistrationStatusType.Assigned)
throw new Exception($"Provisioning failed: {result.Status}");
// Persist result for next boot
cache = new ProvisioningCache(result.AssignedHub, result.DeviceId);
await File.WriteAllTextAsync(cacheFilePath,
JsonSerializer.Serialize(cache));
var freshAuth = new DeviceAuthenticationWithRegistrySymmetricKey(
result.DeviceId, derivedDeviceKey);
var freshClient = DeviceClient.Create(
result.AssignedHub, freshAuth, TransportType.Mqtt);
await freshClient.OpenAsync();
return freshClient;
}
This pattern provisions once and reconnects on subsequent boots. If the cached connection fails (because the device was migrated to a different hub or the IoT Hub was deleted), it falls back to DPS automatically.
Service-Side: Bulk Enrollment Queries
When managing large fleets, querying individual enrollments one at a time is impractical. The service client supports SQL-style queries to enumerate enrollments and registration states.
// Query all individual enrollments
var query = serviceClient.CreateIndividualEnrollmentQuery(
new QuerySpecification("SELECT * FROM enrollments"),
pageSize: 100);
while (query.HasMoreResults)
{
QueryResult page = await query.NextAsync();
foreach (IndividualEnrollment enrollment in page.Items)
{
Console.WriteLine($"{enrollment.RegistrationId}: {enrollment.ProvisioningStatus}");
}
}
// Query registration states for a group enrollment
var stateQuery = serviceClient.CreateEnrollmentGroupRegistrationStateQuery(
new QuerySpecification("SELECT * FROM enrollments"),
enrollmentGroupId: "factory-batch-2025",
pageSize: 100);
while (stateQuery.HasMoreResults)
{
QueryResult statePage = await stateQuery.NextAsync();
foreach (DeviceRegistrationState state in statePage.Items)
{
Console.WriteLine($"{state.RegistrationId}: {state.Status} -> {state.AssignedHub}");
}
}
These queries are useful for fleet health dashboards, decommissioning scripts that iterate over all devices in a group, and audit reports that verify every enrolled device has successfully provisioned.
Handling Provisioning Failures
Production provisioning code needs to handle transient failures and distinguishable error conditions. The SDK throws ProvisioningTransportException for transport-level errors and returns non-assigned status codes for logical failures.
const int maxRetries = 5;
const int baseDelayMs = 1000;
DeviceRegistrationResult result = null;
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
result = await provisioningClient.RegisterAsync();
if (result.Status == ProvisioningRegistrationStatusType.Assigned)
{
break; // Success
}
if (result.Status == ProvisioningRegistrationStatusType.Disabled)
{
// Enrollment is disabled; retrying will not help
throw new InvalidOperationException(
$"Device enrollment is disabled: {result.ErrorMessage}");
}
// Other non-assigned statuses may be transient
int delay = baseDelayMs * (int)Math.Pow(2, attempt);
await Task.Delay(delay);
}
catch (ProvisioningTransportException ex) when (attempt < maxRetries - 1)
{
// Transient transport error; retry with backoff
int delay = baseDelayMs * (int)Math.Pow(2, attempt);
Console.WriteLine($"Provisioning transport error (attempt {attempt + 1}): {ex.Message}");
await Task.Delay(delay);
}
}
if (result?.Status != ProvisioningRegistrationStatusType.Assigned)
{
throw new Exception("Provisioning failed after maximum retries.");
}
Operational Concerns
Monitoring Registration Status
DPS exposes metrics through Azure Monitor including registration attempt counts, success and failure rates, and attestation failure breakdowns. Setting up alerts on attestation failure rates helps catch issues like expired certificates or misconfigured derived keys before they affect large numbers of devices.
The service-side SDK query API allows operators to enumerate device registration records and check their status. This is useful during rollouts to verify that devices are provisioning successfully, or after incidents to identify which devices were affected.
Device Decommissioning
When a device reaches end of life, is lost, or is reassigned, its enrollment record should be updated or removed. For individual enrollments, the record can be disabled (blocking future provisioning for that device without losing registration history) or deleted. For group enrollments, individual device registrations within the group can be disabled even though the group enrollment remains active.
Disabling an enrollment prevents future provisioning but does not disconnect a currently connected device. Disconnecting an active device requires action in IoT Hub, such as disabling the device identity or revoking its SAS token. DPS and IoT Hub are separate services; decommissioning requires actions in both.
Reprovisioning After IoT Hub Migration
When migrating devices from one IoT Hub to another, the reprovisioning flow handles reassignment automatically if DPS is in use. Update the DPS allocation policy or linked hubs to point to the new hub, then trigger reprovisioning on the device side. Devices contact DPS, receive the new hub assignment, and reconnect. The reprovisioning policy controls whether twin state is migrated or reset.
Without DPS, migrating devices means updating connection strings on each device individually, which requires a remote management mechanism and is error-prone at scale. This is one of the strongest arguments for adopting DPS from the start of a project rather than retrofitting it later.
Quota and Scaling Limits
DPS has service limits that matter for large-scale deployments. The default limits cover the typical production scenario, but fleet deployments that provision large numbers of devices in a short window (such as a product launch or factory burn-in process) can hit rate limits if provisioning requests are not spread across time.
The DPS quota documentation covers the current limits on registrations per minute and the number of linked IoT Hubs per DPS instance. Key operational considerations include:
- Provisioning requests from devices should include exponential backoff when receiving throttling responses (HTTP 429). The retry pattern shown in the C# section handles this naturally when configured with appropriate backoff parameters.
- Devices that re-provision on every boot instead of caching results create unnecessary provisioning load. A fleet of 100,000 devices that each re-provision daily generates significantly more DPS traffic than the same fleet that provisions once and caches the result.
- Multiple DPS instances can be used for very large deployments or for geographic separation, though this requires managing which scope ID each device uses.
- The service-side management API also has rate limits; bulk enrollment operations like the fleet queries shown above should be paged and throttled to avoid hitting those limits.
Diagnostics and Logging
DPS operations appear in Azure Diagnostic Logs when diagnostic settings are configured to send logs to a Log Analytics workspace or storage account. The RegistrationsOperationalLogs category captures registration attempts with outcome, attestation type, and error details. Attestation failures at scale often indicate certificate rotation timing issues or derived key generation errors in the manufacturing pipeline.
Correlating DPS registration logs with IoT Hub connection logs is useful for diagnosing provisioning failures that succeed at the DPS level but fail when the device tries to connect to the assigned hub. A device that provisions successfully but cannot connect to its assigned IoT Hub might be hitting a networking issue, a firewall rule, or a mismatch between the device identity created by DPS and the credentials the device is presenting. Checking both log sources together narrows the problem faster than checking either in isolation.
Distributed tracing through Application Insights is not native to DPS, but custom allocation Azure Functions can emit traces that connect provisioning decisions to downstream behavior. If a custom allocation function is routing devices incorrectly, traces from the function reveal what input data triggered each routing decision.
Security Considerations
Group Master Key Protection
With symmetric key group enrollments, the master key is the root of trust for all devices in the group. If the master key is compromised, an attacker can derive valid device keys for any registration ID and impersonate any device in the group. The master key must never be embedded in device firmware, transmitted over insecure channels, or stored in source control.
The correct approach is to derive per-device keys during manufacturing using the master key, then store only the derived key on each device. The master key lives in a secrets management system like Azure Key Vault, accessible only to the manufacturing system at key derivation time. The HMAC-SHA256 derivation is straightforward, and many DPS documentation examples include a utility function for it.
using System.Security.Cryptography;
using System.Text;
static string ComputeDerivedKey(string masterKey, string registrationId)
{
byte[] keyBytes = Convert.FromBase64String(masterKey);
byte[] registrationIdBytes = Encoding.UTF8.GetBytes(registrationId);
using var hmac = new HMACSHA256(keyBytes);
byte[] derivedKeyBytes = hmac.ComputeHash(registrationIdBytes);
return Convert.ToBase64String(derivedKeyBytes);
}
This derived key is what gets stored on the device. The master key never touches the device.
Certificate Lifecycle Management
X.509 certificates have expiry dates, and a certificate rotation plan must be established before the first device ships. Certificates that expire without rotation cause provisioning failures for newly booting or reprovisioning devices, and can cause connectivity failures if devices use certificates for IoT Hub authentication as well.
The rotation process involves issuing new leaf certificates from the current CA (or a new CA if the CA itself is rotating), distributing new certificates to devices through OTA update mechanisms, and verifying that DPS and IoT Hub accept the new certificates before the old ones expire. CA rotation is more involved because the new CA certificate must be verified in DPS before any devices with certificates signed by it can provision.
Group enrollment records in DPS support both primary and secondary CA certificates, which allows rolling updates. During a CA rotation, the secondary certificate is set to the new CA, devices are updated to use certificates from the new CA, and then the primary is updated to the new CA once the transition is complete.
Scope IDs and Multi-Tenant Scenarios
Each DPS instance has a unique scope ID that devices include in registration requests. The scope ID prevents a device from accidentally or maliciously provisioning against a different organization’s DPS instance, even if both use the same global endpoint. In multi-tenant architectures where different customers have separate DPS instances, the scope ID is a necessary part of the device identity that must be managed carefully. Devices that share firmware but belong to different tenants need different scope IDs, which typically means different firmware builds or a runtime configuration mechanism for the scope ID.
Access Control for the Service API
The ProvisioningServiceClient uses a connection string with a shared access policy. The connection string carries significant authority: it can create, modify, and delete enrollments. Service-side access should be granted to specific application identities with only the permissions they need, rather than sharing a single high-privilege connection string across all backend services. Connection strings should be stored in Key Vault and rotated on a regular schedule.
Key Takeaways
Azure DPS removes the provisioning bottleneck that otherwise forces manual per-device configuration at manufacturing time or deployment time. The three attestation mechanisms cover the range from development simplicity (symmetric keys) to hardware-backed security (TPM), with X.509 certificates being the standard choice for production fleets where hardware TPM is not available. Enrollment types map cleanly to operational needs: individual enrollments for high-value devices with per-device configuration requirements, group enrollments for fleet-scale manufacturing.
The allocation policy decision deserves early attention because it affects IoT Hub architecture. Custom allocation unlocks multi-tenant routing and business rule-based assignment, but it requires an Azure Function dependency in the provisioning path. The re-provisioning policy is similarly easy to overlook during initial setup but becomes critical during IoT Hub migrations.
On the security side, the master key protection rule for symmetric key enrollments is the most common mistake in DPS deployments. Deriving per-device keys at manufacturing time and keeping the master key in secure storage is straightforward to implement and eliminates the most serious symmetric key risk. Certificate lifecycle management for X.509 deployments requires planning before devices ship, since retroactively establishing a rotation process for deployed devices is significantly harder than building it in from the start.
Found this guide helpful? Share it with your team:
Share on LinkedIn