Creating DPS, IoT Hub and ADR Instances
Welcome back to this series of posts exploring the latest features of Azure IoT Hub, Device Provisioning Service (DPS), and Azure Device Registration (ADR).
In the previous post we dove deeper into DPS and what’s new.
Contents:
- Introduction
- A DPS Primer
- Creating DPS, IoT Hub and ADR Instances (This Post)
- Understanding X.509 and CSR Workflows
- The Device Application
Time to Build!
Now that you know what DPS does, let’s stand up the Azure resources. We’ll create a resource group, IoT Hub, and DPS, then link them. We’ll also prep ADR so certificate policies are ready for issuance.
Two Ways to Use This Series
Option 1: Quick Start (Clone and Run)
git clone https://github.com/pjgpetecodes/skittlesorter.git
cd skittlesorter/scripts
# Full ADR + X.509 setup (recommended)
.\setup-x509-dps-adr.ps1 `
-ResourceGroup "my-iot-rg" `
-Location "eastus" `
-IoTHubName "my-hub-001" `
-DPSName "my-dps-001" `
-AdrNamespace "my-adr-001" `
-UserIdentity "my-uami"Rename the fields above to suit your solution, then when you run it, the script will output the following:
- DPS ID Scope (needed for device config)
- Certificate paths (for device authentication)
- Azure resource details
- Enrollment group information
Or, if you have the Azure resources provisioned already, you can also just run the X.509 Setup script;
.\setup-x509-attestation.ps1 `
-RegistrationId "my-device" `
-DpsName "my-dps-001" `
-ResourceGroup "my-iot-rg"Update Device Configuration
Once the script completes, update your device settings:
{
"IoTHub": {
"DpsProvisioning": {
"IdScope": "0ne001XXXXX", // From script output
"RegistrationId": "my-device",
"AttestationMethod": "X509",
"AttestationCertPath": "C:\\path\\to\\scripts\\certs\\device\\device.pem",
"AttestationKeyPath": "C:\\path\\to\\scripts\\certs\\device\\device.key",
"AttestationCertChainPath": "C:\\path\\to\\scripts\\certs\\ca\\chain.pem"
}
},
"Adr": {
"Enabled": true,
"SubscriptionId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ResourceGroupName": "my-iot-rg",
"NamespaceName": "my-adrnamespace-001"
}
}Run the Device Application
Once you’ve run one of the above two commands, you’ll need to update your appsettings.json file with the results printed out at the end of the script execution.
You can then run the skittle sorter with;
cd .. # You should be in the skittlesorter directory
dotnet run --project src/skittlesorter.csprojOption 2: Step-by-Step Tutorial
Follow along post-by-post to understand why and how everything works. You’ll learn:
- DPS concepts and architecture
- Azure resource setup
- X.509 certificate workflows
- .NET implementation details
Prerequisites
- Azure subscription
- Azure CLI + IoT extension (
az extension add --name azure-iot --allow-preview) - PowerShell 7+
- Rights to create resource groups and role assignments
What We’re Building
- Resource Group (keeps everything together)
- IoT Hub (where devices send telemetry)
- DPS (handles zero-touch provisioning)
- ADR namespace + credential policy (issues certs)
- Links + roles so the pieces talk to each other
Step 1: Set Variables and create the Resource Group
We declare names once so you can rerun easily.
$subscriptionId = az account show --query id -o tsv
$location = "eastus"
$unique = "dev001" # make it unique
$resourceGroup = "$unique-skittlesorter-rg"
$iotHubName = "$unique-skittlesorter-hub" # must be globally unique
$dpsName = "$unique-skittlesorter-dps" # must be globally unique
$adrNamespace = "$unique-skittlesorter-adr" # lowercase only
$credentialPolicyName = "cert-policy"
$userIdentity = "$unique-skittlesorter-uami"
$enrollmentGroupName = "$unique-skittlesorter-group"
$registrationId = "$unique-skittlesorter"Now we create the container for everything.
az group create --name $resourceGroup --location $locationStep 2: Configure App Privileges
Now we configure App Privileges (IoT Hub app principal)
az role assignment create `
--assignee "89d10474-74af-4874-99a7-c23c2f643083" `
--role "Contributor" `
--scope "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup " `
--output none 2>$nullStep 3: Create Managed Identity and Assign Roles
We need permissions for DPS and IoT Hub to read ADR. Now we create UAMI and grant access.
First we create a reusable Azure managed identity so IoT Hub and DPS can securely access ADR without storing secrets.
az identity create `
--name $userIdentity `
--resource-group $resourceGroup `
--location $location `
--output noneNow we retrieve the managed identity’s resource ID and save it to $uamiResourceId so we can attach that identity to IoT Hub and DPS in the next steps.
$uamiResourceId = az identity show `
--name $userIdentity `
--resource-group $resourceGroup `
--query id -o tsvNow we retrieve the managed identity’s principal ID and store it in $uamiPrincipalId so we can grant it RBAC permissions on ADR.
$uamiPrincipalId = az identity show `
--name $userIdentity `
--resource-group $resourceGroup `
--query principalId -o tsvStep 4: Create ADR Namespace + Credential Policy
ADR backs certificate issuance. We create the namespace and a policy that uses Microsoft-managed CA.
Create ADR namespace:
az iot adr ns create `
--name $adrNamespace `
--resource-group $resourceGroup `
--location $location `
--certificate-authority-name microsoft-managed `
--identity-type SystemAssignedNow we retrieve the Device Registry Namespace Resource ID.
$namespaceResourceId = az iot adr ns show `
--name $adrNamespace `
--resource-group $resourceGroup `
--query id -o tsvNow we grant the managed identity the ADR contributor role at the namespace scope so it has permission to access and operate on that ADR namespace.
# Assign UAMI role to access the ADR namespace
az role assignment create `
--assignee $uamiPrincipalId `
--role "a5c3590a-3a1a-4cd4-9648-ea0a32b15137" `
--scope $namespaceResourceId `
--output noneStep 5: Create IoT Hub
Now it’s time to create the IoT Hub where our devices will land after DPS assigns them. We need to use S1 as the preview features need Standard.
az iot hub create `
--name $ioTHubName `
--resource-group $resourceGroup `
--location $location `
--sku s1 `
--mi-user-assigned $uamiResourceId `
--ns-resource-id $namespaceResourceId `
--ns-identity-id $uamiResourceId `
--output noneStep 6: Create Managed Identity and Assign Roles
We need permissions for DPS and IoT Hub to read ADR. Now we create UAMI and grant access.
Now we fetch the ADR namespace’s managed identity principal ID and store it in $adrPrincipalId so we can grant that identity access to IoT Hub.
$adrPrincipalId = az iot adr ns show `
--name $adrNamespace `
--resource-group $resourceGroup `
--query identity.principalId -o tsvNow we grant the ADR namespace identity Contributor access on the IoT Hub so ADR can manage required hub-side resources during provisioning.
az role assignment create `
--assignee $adrPrincipalId `
--role "Contributor" `
--scope $hubResourceId `
--output noneNow we grant the ADR namespace identity the IoT Hub Registry Contributor role so it can create and manage device identities during provisioning.
az role assignment create `
--assignee $adrPrincipalId `
--role "IoT Hub Registry Contributor" `
--scope $hubResourceId `
--output noneStep 7: Create DPS
DPS is the front door. It will handle attestation and hand out hubs.
az iot dps create `
--name $dPSName `
--resource-group $resourceGroup `
--location $location `
--mi-user-assigned $uamiResourceId `
--ns-resource-id $namespaceResourceId `
--ns-identity-id $uamiResourceId `
--output noneGet DPS ID Scope:
$idScope = az iot dps show `
--name $dpsName `
--resource-group $resourceGroup `
--query properties.idScope -o tsv
Write-Host "DPS ID Scope: $idScope" -ForegroundColor GreenStep 8: Link DPS to IoT Hub and ADR
Onwards we wire everything together.
az iot dps linked-hub create `
--dps-name $dpsName `
--resource-group $resourceGroup `
--hub-name $ioTHubName `
--output noneStep 9: Sync Credential Policy
Next up we sync ADR credentials into IoT Hub so certs verify correctly.
Sync credential policy to IoT Hub:
az iot adr ns credential sync `
--namespace $adrNamespace `
--resource-group $resourceGroup `
--output json 2>&1List certificates in IoT Hub:
az iot hub certificate list `
--hub-name $iotHubName `
--resource-group $resourceGroupYou should see the Microsoft-managed CA in the list.
Verify in Portal
Now we check:
- Resource Group shows IoT Hub, DPS, ADR
- DPS → Linked IoT hubs lists your hub
- DPS → Overview shows the ID Scope
- IoT Hub → Certificates includes the synced CA
Troubleshooting
- Name already exists: Pick a new
$unique. - Free tier blocked: Use
--sku S1(F1 does not support DPS). - Role assignment fails: Wait 1–2 minutes, then retry.
- No CA in IoT Hub: Re-run the credential-policy sync.
What We Finished
✅ Resource group created
✅ IoT Hub created (Standard)
✅ DPS created and ID Scope captured
✅ ADR namespace + credential policy ready
✅ Links and roles wired up
✅ Enrollment group with symmetric key + CSR path
Next up we generate the X.509 material and talk CSR workflows.
