如何使用自定义分配策略How to use custom allocation policies

自定义分配策略让你能够对设备分配到 IoT 中心的方式进行更多地控制。A custom allocation policy gives you more control over how devices are assigned to an IoT hub. 它是通过使用 Azure 函数中的自定义代码将设备分配到 IoT 中心来实现的。This is accomplished by using custom code in an Azure Function to assign devices to an IoT hub. 设备预配服务将调用 Azure 函数代码,提供有关设备和注册的所有相关信息。The device provisioning service calls your Azure Function code providing all relevant information about the device and the enrollment. 将执行函数代码并返回用于预配设备的 IoT 中心信息。Your function code is executed and returns the IoT hub information used to provisioning the device.

当设备预配服务提供的策略不能满足你的方案的要求时,通过使用自定义分配策略,可以定义你自己的分配策略。By using custom allocation policies, you define your own allocation policies when the policies provided by the Device Provisioning Service don't meet the requirements of your scenario.

例如,你可能想要检查设备在预配过程中所使用的证书,并根据证书属性将该设备分配到 IoT 中心。For example, maybe you want to examine the certificate a device is using during provisioning and assign the device to an IoT hub based on a certificate property. 或者,你可能在数据库中存储了设备的信息,并需要查询数据库以确定应将该设备分配到哪个 IoT 中心。Or, maybe you have information stored in a database for your devices and need to query the database to determine which IoT hub a device should be assigned to.

本文演示使用 C# 编写的 Azure 函数的自定义分配策略。This article demonstrates a custom allocation policy using an Azure Function written in C#. 创建了两个新的 IoT 中心,分别表示 Contoso 烤箱分区 和 Contoso 热泵分区 。Two new IoT hubs are created representing a Contoso Toasters Division and a Contoso Heat Pumps Division. 请求预配的设备必须具有含以下后缀之一的注册 ID 才能被接受进行预配:Devices requesting provisioning must have a registration ID with one of the following suffixes to be accepted for provisioning:

  • -contoso-tstrsd-007:Contoso 烤箱分区-contoso-tstrsd-007: Contoso Toasters Division
  • -contoso-hpsd-088:Contoso 热泵分区-contoso-hpsd-088: Contoso Heat Pumps Division

将基于注册 ID 上这些所需的后缀之一对设备进行预配。The devices will be provisioned based on one of these required suffixes on the registration ID. 将使用 Azure IoT C SDK 中包含的预配示例对这些设备进行模拟。These devices will be simulated using a provisioning sample included in the Azure IoT C SDK.

你将在本文中执行以下步骤:You perform the following steps in this article:

  • 使用 Azure CLI 创建两个 Contoso 分区 IoT 中心(Contoso 烤箱分区 和 Contoso 热泵分区 )Use the Azure CLI to create two Contoso division IoT hubs (Contoso Toasters Division and Contoso Heat Pumps Division)
  • 使用 Azure 函数为自定义分配策略创建新的组注册Create a new group enrollment using an Azure Function for the custom allocation policy
  • 为两个设备模拟创建设备密钥。Create device keys for two device simulations.
  • 为 Azure IoT C SDK 设置开发环境Set up the development environment for the Azure IoT C SDK
  • 模拟设备,以验证其是否根据自定义分配策略的示例代码进行预配。Simulate the devices and verify that they are provisioned according to the example code in the custom allocation policy

如果没有 Azure 订阅,可在开始前创建一个试用帐户If you don't have an Azure subscription, create a trial account before you begin.

先决条件Prerequisites

以下先决条件适用于 Windows 开发环境。The following prerequisites are for a Windows development environment. 对于 Linux 或 macOS,请参阅 SDK 文档的准备开发环境中的相应部分。For Linux or macOS, see the appropriate section in Prepare your development environment in the SDK documentation.

创建预配服务和两个部门 IoT 中心Create the provisioning service and two divisional IoT hubs

在本部分,你将创建一个预配服务,以及分别表示“Contoso 烤箱部门”和“Contoso 热泵部门”的两个 IoT 中心。 In this section, you create a provisioning service and two IoT hubs representing the Contoso Toasters Division and the Contoso Heat Pumps division.

提示

本文中使用的命令将在“中国东部”位置创建预配服务和其他资源。The commands used in this article create the provisioning service and other resources in the China East location. 我们建议在与你最靠近的区域中创建支持设备预配服务的资源。We recommend that you create your resources in the region nearest you that supports Device Provisioning Service. 若要查看可用位置的列表,可以运行 az provider show --namespace Microsoft.Devices --query "resourceTypes[?resourceType=='ProvisioningServices'].locations | [0]" --out table 命令,也可以转到Azure 状态页,在其中搜索“设备预配服务”。You can view a list of available locations by running the command az provider show --namespace Microsoft.Devices --query "resourceTypes[?resourceType=='ProvisioningServices'].locations | [0]" --out table or by going to the Azure Status page and searching for "Device Provisioning Service". 在命令中,可以使用一个单词或多个单词的格式来指定位置,例如:chinaeast、China East、CHINA EAST,等等。该值不区分大小写。In commands, locations can be specified either in one word or multi-word format; for example: chinaeast, China East, CHINA EAST, etc. The value is not case sensitive. 如果使用多个单词的格式来指定位置,请将值置于引号中,例如 -- location "China East"If you use multi-word format to specify location, enclose the value in quotes; for example, -- location "China East".

  1. 在 Azure CLI 中,使用 az group create 命令创建资源组。Use the Azure CLI to create a resource group with the az group create command. Azure 资源组是在其中部署和管理 Azure 资源的逻辑容器。An Azure resource group is a logical container into which Azure resources are deployed and managed.

    以下示例在 chinaeast 区域创建名为 contoso-us-resource-group 的资源组。The following example creates a resource group named contoso-us-resource-group in the chinaeast region. 建议对本文中创建的所有资源使用该组。It is recommended that you use this group for all resources created in this article. 此方法使你能够在完成后更为轻松地进行清理。This approach will make clean up easier after you're finished.

    az group create --name contoso-us-resource-group --location chinaeast
    
  2. 使用 az iot dps create 命令创建设备预配服务。Create a device provisioning service with the az iot dps create command. 该预配服务将添加到 contoso-us-resource-groupThe provisioning service will be added to contoso-us-resource-group.

    以下示例在 chinaeast 位置创建名为 contoso-provisioning-service-1098 的预配服务。The following example creates a provisioning service named contoso-provisioning-service-1098 in the chinaeast location. 必须使用唯一的服务名称。You must use a unique service name. 在服务名称中的 1098 位置构成你自己的后缀。Make up your own suffix in the service name in place of 1098.

    az iot dps create --name contoso-provisioning-service-1098 --resource-group contoso-us-resource-group --location chinaeast
    

    此命令可能需要花费几分钟时间完成。This command may take a few minutes to complete.

  3. 在 Azure CLI 中,使用 az iot hub create 命令创建 Contoso 烤箱分区 IoT 中心。Use the Azure CLI to create the Contoso Toasters Division IoT hub with the az iot hub create command. IoT 中心将被添加到 contoso-us-resource-group 。The IoT hub will be added to contoso-us-resource-group.

    以下示例在 chinaeast 位置创建名为 contoso-toasters-hub-1098 的 IoT 中心。The following example creates an IoT hub named contoso-toasters-hub-1098 in the chinaeast location. 必须使用唯一的中心名称。You must use a unique hub name. 在中心名称中的 1098 位置构成你自己的后缀。Make up your own suffix in the hub name in place of 1098. 自定义分配策略的示例代码要求使用中心名称中的 -toasters-The example code for the custom allocation policy requires -toasters- in the hub name.

    az iot hub create --name contoso-toasters-hub-1098 --resource-group contoso-us-resource-group --location chinaeast --sku S1
    

    此命令可能需要花费几分钟时间完成。This command may take a few minutes to complete.

  4. 在 Azure CLI 中,使用 az iot hub create 命令创建 Contoso 热泵分区 IoT 中心。Use the Azure CLI to create the Contoso Heat Pumps Division IoT hub with the az iot hub create command. 此 IoT 中心也将被添加到 contoso-us-resource-group 。This IoT hub will also be added to contoso-us-resource-group.

    以下示例在 chinaeast 位置创建名为 contoso-heatpumps-hub-1098 的 IoT 中心。The following example creates an IoT hub named contoso-heatpumps-hub-1098 in the chinaeast location. 必须使用唯一的中心名称。You must use a unique hub name. 在中心名称中的 1098 位置构成你自己的后缀。Make up your own suffix in the hub name in place of 1098. 自定义分配策略的示例代码要求使用中心名称中的 -heatpumps-The example code for the custom allocation policy requires -heatpumps- in the hub name.

    az iot hub create --name contoso-heatpumps-hub-1098 --resource-group contoso-us-resource-group --location chinaeast --sku S1
    

    此命令可能需要花费几分钟时间完成。This command may take a few minutes to complete.

创建自定义分配函数Create the custom allocation function

在本部分,你将创建一个实现自定义分配策略的 Azure 函数。In this section, you create an Azure function that implements your custom allocation policy. 此函数根据设备的注册 ID 是包含字符串 -contoso-tstrsd-007 还是 -contoso-hpsd-088,来确定要将该设备注册到哪个部门 IoT 中心。This function decides which divisional IoT hub a device should be registered to based on whether its registration ID contains the string -contoso-tstrsd-007 or -contoso-hpsd-088. 它还根据设备是烤箱还是热泵,来设置设备孪生的初始状态。It also sets the initial state of the device twin based on whether the device is a toaster or a heat pump.

  1. 登录到 Azure 门户Sign in to the Azure portal. 在主页中选择“+ 创建资源”。 From your home page, select + Create a resource.

  2. 在“搜索市场”搜索框中键入“函数应用”。 In the Search the Marketplace search box, type "Function App". 从下拉列表中选择“函数应用”,然后选择“创建”。 From the drop-down list select Function App, and then select Create.

  3. 在“函数应用”创建页上的“基本信息”选项卡下,输入新函数应用的以下设置,然后选择“查看 + 创建”: On Function App create page, under the Basics tab, enter the following settings for your new function app and select Review + create:

    资源组:选择“contoso-us-resource-group”以将本文中创建的所有资源保留在一起。 Resource Group: Select the contoso-us-resource-group to keep all resources created in this article together.

    函数应用名称:输入唯一函数应用名称。Function App name: Enter a unique function app name. 本示例使用 contoso-function-app-1098This example uses contoso-function-app-1098.

    发布:确认已选择“代码”。 Publish: Verify that Code is selected.

    运行时堆栈:从下拉列表中选择“.NET Core”。 Runtime Stack: Select .NET Core from the drop-down.

    区域:选择你的资源组所在的同一区域。Region: Select the same region as your resource group. 此示例使用“中国东部” 。This example uses China East.

    备注

    默认已启用 Application Insights。By default, Application Insights is enabled. 本文不需要 Application Insights,但它可以帮助你了解和调查处理自定义分配时遇到的任何问题。Application Insights is not necessary for this article, but it might help you understand and investigate any issues you encounter with the custom allocation. 如果需要,可以禁用 Application Insights,方法是选择“监视”选项卡,然后对“启用 Application Insights”选择“否”。 If you prefer, you can disable Application Insights by selecting the Monitoring tab and then selecting No for Enable Application Insights.

    创建用于托管自定义分配函数的 Azure 函数应用

  4. 在“摘要”页上,选择“创建”以创建函数应用。 On the Summary page, select Create to create the function app. 部署可能需要花费几分钟时间。Deployment may take several minutes. 完成后,选择“转到资源”。 When it completes, select Go to resource.

  5. 在函数应用“概述”页的左窗格中,选择“函数”旁边的 + 以添加新函数。 On the left pane of the function app Overview page, select + next to Functions to add a new function.

    将函数添加到函数应用

  6. 在“适用于 .NET 的 Azure Functions - 入门”页上,对于“选择部署环境”步骤,请选择“门户中”磁贴,然后选择“继续”。 On the Azure Functions for .NET - getting started page, for the CHOOSE A DEPLOYMENT ENVIRONMENT step, select the In-portal tile, then select Continue.

    选择门户开发环境

  7. 在下一页上,对于“创建函数”步骤,请选择“Webhook + API”磁贴,然后选择“创建”。 On the next page, for the CREATE A FUNCTION step, select the Webhook + API tile, then select Create. 随即会创建名为 HttpTrigger1 的函数,门户将显示 run.csx 代码文件的内容。A function named HttpTrigger1 is created, and the portal displays the contents of the run.csx code file.

  8. 引用所需的 NuGet 包。Reference required Nuget packages. 为了创建初始设备孪生,自定义分配函数将使用必须载入托管环境的两个 Nuget 包中定义的类。To create the initial device twin, the custom allocation function uses classes that are defined in two Nuget packages that must be loaded into the hosting environment. 在 Azure Functions 中,Nuget 包是使用 function.host 文件引用的。With Azure Functions, Nuget packages are referenced using a function.host file. 此步骤将保存并上传 function.host 文件。In this step, you save and upload a function.host file.

    1. 将以下行复制到你偏好的编辑器中,并将文件作为 function.host 保存在计算机上。Copy the following lines into your favorite editor and save the file on your computer as function.host.

      <Project Sdk="Microsoft.NET.Sdk">  
          <PropertyGroup>  
              <TargetFramework>netstandard2.0</TargetFramework>  
          </PropertyGroup>  
          <ItemGroup>  
              <PackageReference Include="Microsoft.Azure.Devices.Provisioning.Service" Version="1.5.0" />  
              <PackageReference Include="Microsoft.Azure.Devices.Shared" Version="1.16.0" />  
          </ItemGroup>  
      </Project>
      
    2. 对于 HttpTrigger1 函数,请展开窗口右侧的“查看文件”选项卡。 On the HttpTrigger1 function, expand the View Files tab on the right side of the window.

      打开“查看文件”

    3. 选择“上传”,浏览到 function.proj 文件,然后选择“打开”以上传该文件。 Select Upload, browse to the function.proj file, and select Open to upload the file.

      选择“上传文件”

  9. HttpTrigger1 函数的代码替换为以下代码,然后选择“保存”: Replace the code for the HttpTrigger1 function with the following code and select Save:

    #r "Newtonsoft.Json"
    
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    using Newtonsoft.Json;
    
    using Microsoft.Azure.Devices.Shared;               // For TwinCollection
    using Microsoft.Azure.Devices.Provisioning.Service; // For TwinState
    
    public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");
    
        // Get request body
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        dynamic data = JsonConvert.DeserializeObject(requestBody);
    
        log.LogInformation("Request.Body:...");
        log.LogInformation(requestBody);
    
        // Get registration ID of the device
        string regId = data?.deviceRuntimeContext?.registrationId;
    
        string message = "Uncaught error";
        bool fail = false;
        ResponseObj obj = new ResponseObj();
    
        if (regId == null)
        {
            message = "Registration ID not provided for the device.";
            log.LogInformation("Registration ID : NULL");
            fail = true;
        }
        else
        {
            string[] hubs = data?.linkedHubs.ToObject<string[]>();
    
            // Must have hubs selected on the enrollment
            if (hubs == null)
            {
                message = "No hub group defined for the enrollment.";
                log.LogInformation("linkedHubs : NULL");
                fail = true;
            }
            else
            {
                // This is a Contoso Toaster Model 007
                if (regId.Contains("-contoso-tstrsd-007"))
                {
                    //Find the "-toasters-" IoT hub configured on the enrollment
                    foreach(string hubString in hubs)
                    {
                        if (hubString.Contains("-toasters-"))
                            obj.iotHubHostName = hubString;
                    }
    
                    if (obj.iotHubHostName == null)
                    {
                        message = "No toasters hub found for the enrollment.";
                        log.LogInformation(message);
                        fail = true;
                    }
                    else
                    {
                        // Specify the initial tags for the device.
                        TwinCollection tags = new TwinCollection();
                        tags["deviceType"] = "toaster";
    
                        // Specify the initial desired properties for the device.
                        TwinCollection properties = new TwinCollection();
                        properties["state"] = "ready";
                        properties["darknessSetting"] = "medium";
    
                        // Add the initial twin state to the response.
                        TwinState twinState = new TwinState(tags, properties);
                        obj.initialTwin = twinState;
                    }
                }
                // This is a Contoso Heat pump Model 008
                else if (regId.Contains("-contoso-hpsd-088"))
                {
                    //Find the "-heatpumps-" IoT hub configured on the enrollment
                    foreach(string hubString in hubs)
                    {
                        if (hubString.Contains("-heatpumps-"))
                            obj.iotHubHostName = hubString;
                    }
    
                    if (obj.iotHubHostName == null)
                    {
                        message = "No heat pumps hub found for the enrollment.";
                        log.LogInformation(message);
                        fail = true;
                    }
                    else
                    {
                        // Specify the initial tags for the device.
                        TwinCollection tags = new TwinCollection();
                        tags["deviceType"] = "heatpump";
    
                        // Specify the initial desired properties for the device.
                        TwinCollection properties = new TwinCollection();
                        properties["state"] = "on";
                        properties["temperatureSetting"] = "65";
    
                        // Add the initial twin state to the response.
                        TwinState twinState = new TwinState(tags, properties);
                        obj.initialTwin = twinState;
                    }
                }
                // Unrecognized device.
                else
                {
                    fail = true;
                    message = "Unrecognized device registration.";
                    log.LogInformation("Unknown device registration");
                }
            }
        }
    
        log.LogInformation("\nResponse");
        log.LogInformation((obj.iotHubHostName != null) ? JsonConvert.SerializeObject(obj) : message);
    
        return (fail)
            ? new BadRequestObjectResult(message) 
            : (ActionResult)new OkObjectResult(obj);
    }
    
    public class ResponseObj
    {
        public string iotHubHostName {get; set;}
        public TwinState initialTwin {get; set;}
    }
    

创建注册Create the enrollment

在本部分,你将创建一个使用自定义分配策略的新注册组。In this section, you'll create a new enrollment group that uses the custom allocation policy. 为简单起见,本文将在注册中使用对称密钥证明For simplicity, this article uses Symmetric key attestation with the enrollment. 对于更安全的解决方案,请考虑使用具有信任链的 X.509 证书证明For a more secure solution, consider using X.509 certificate attestation with a chain of trust.

  1. 仍在 Azure 门户中操作,打开预配服务。Still on the Azure portal, open your provisioning service.

  2. 在左窗格中选择“管理注册”,然后在页面顶部选择“添加注册组”按钮。 Select Manage enrollments on the left pane, and then select the Add enrollment group button at the top of the page.

  3. 在“添加注册组”中输入以下信息,然后选择“保存”按钮。 On Add Enrollment Group, enter the following information, and select the Save button.

    组名称:输入 contoso-custom-allocated-devicesGroup name: Enter contoso-custom-allocated-devices.

    证明类型:选择“对称密钥” 。Attestation Type: Select Symmetric Key.

    自动生成密钥:此复选框应已处于选中状态。Auto Generate Keys: This checkbox should already be checked.

    选择要如何将设备分配到中心:选择“自定义(使用 Azure Function)”。 Select how you want to assign devices to hubs: Select Custom (Use Azure Function).

    为对称密钥证明添加自定义分配注册组

  4. 在“添加注册组”中,选择“链接新的 IoT 中心”以链接这两个新的部门 IoT 中心。 On Add Enrollment Group, select Link a new IoT hub to link both of your new divisional IoT hubs.

    请对两个部门 IoT 中心执行上述步骤。Execute this step for both of your divisional IoT hubs.

    订阅:如果你有多个订阅,请选择创建分区 IoT 中心的订阅。Subscription: If you have multiple subscriptions, choose the subscription where you created the divisional IoT hubs.

    IoT 中心:选择你创建的分区中心之一。IoT hub: Select one of the divisional hubs you created.

    访问策略:选择“iothubowner”。 Access Policy: Choose iothubowner.

    使用预配服务链接分区 IoT 中心

  5. 在“添加注册组” 上,一旦链接这两个分区 IoT 中心后,必须将其选择为注册组的 IoT 中心组,如下所示:On Add Enrollment Group, once both divisional IoT hubs have been linked, you must select them as the IoT Hub group for the enrollment group as shown below:

    为注册创建分区中心组

  6. 在“添加注册组”中,向下滚动到“选择 Azure 函数”部分,选择在上一部分创建的函数应用。 On Add Enrollment Group, scroll down to the Select Azure Function section, select the Function app you created in the previous section. 选择创建的函数,然后选择“保存”以保存该注册组。Then select the function you created and select Save to save the enrollment group.

    选择函数并保存注册组

  7. 保存注册后,重新打开它,并记录“主键” 。After saving the enrollment, reopen it and make a note of the Primary Key. 必须先保存注册,才能生成密钥。You must save the enrollment first to have the keys generated. 此密钥稍后将用于为模拟设备生成唯一设备密钥。This key will be used to generate unique device keys for simulated devices later.

派生唯一设备密钥Derive unique device keys

在本部分,你将创建两个唯一的设备密钥。In this section, you create two unique device keys. 一个密钥将用于模拟的烤箱设备。One key will be used for a simulated toaster device. 另一个密钥将用于模拟的热泵设备。The other key will be used for a simulated heat pump device.

若要生成设备密钥,请使用前面记下的“主密钥”来计算每个设备的设备注册 ID 的 HMAC-SHA256,并将结果转换为 Base64 格式。****To generate the device key, you use the Primary Key you noted earlier to compute the HMAC-SHA256 of the device registration ID for each device and convert the result into Base64 format. 有关使用注册组创建派生设备密钥的详细信息,请参阅对称密钥证明的组注册部分。For more information on creating derived device keys with enrollment groups, see the group enrollments section of Symmetric key attestation.

对于本文中的示例,使用以下两个设备注册 ID 并计算这两个设备的设备密钥。For the example in this article, use the following two device registration IDs and compute a device key for both devices. 这两个注册 ID 都具有有效的后缀,以与自定义分配策略的示例代码结合使用:Both registration IDs have a valid suffix to work with the example code for the custom allocation policy:

  • breakroom499-contoso-tstrsd-007 breakroom499-contoso-tstrsd-007
  • mainbuilding167-contoso-hpsd-088 mainbuilding167-contoso-hpsd-088

Linux 工作站Linux workstations

如果使用的是 Linux 工作站,可以使用 openssl 生成派生的设备密钥,如以下示例中所示。If you're using a Linux workstation, you can use openssl to generate your derived device keys as shown in the following example.

  1. 将“键” 值替换为前面记录的“主键” 。Replace the value of KEY with the Primary Key you noted earlier.

    KEY=oiK77Oy7rBw8YB6IS6ukRChAw+Yq6GC61RMrPLSTiOOtdI+XDu0LmLuNm11p+qv2I+adqGUdZHm46zXAQdZoOA==
    
    REG_ID1=breakroom499-contoso-tstrsd-007
    REG_ID2=mainbuilding167-contoso-hpsd-088
    
    keybytes=$(echo $KEY | base64 --decode | xxd -p -u -c 1000)
    devkey1=$(echo -n $REG_ID1 | openssl sha256 -mac HMAC -macopt hexkey:$keybytes -binary | base64)
    devkey2=$(echo -n $REG_ID2 | openssl sha256 -mac HMAC -macopt hexkey:$keybytes -binary | base64)
    
    echo -e $"\n\n$REG_ID1 : $devkey1\n$REG_ID2 : $devkey2\n\n"
    
    breakroom499-contoso-tstrsd-007 : JC8F96eayuQwwz+PkE7IzjH2lIAjCUnAa61tDigBnSs=
    mainbuilding167-contoso-hpsd-088 : 6uejA9PfkQgmYylj8Zerp3kcbeVrGZ172YLa7VSnJzg=
    

基于 Windows 的工作站Windows-based workstations

如果使用的是基于 Windows 的工作站,可以使用 PowerShell 生成派生的设备密钥,如以下示例中所示。If you're using a Windows-based workstation, you can use PowerShell to generate your derived device key as shown in the following example.

  1. 将“键” 值替换为前面记录的“主键” 。Replace the value of KEY with the Primary Key you noted earlier.

    $KEY='oiK77Oy7rBw8YB6IS6ukRChAw+Yq6GC61RMrPLSTiOOtdI+XDu0LmLuNm11p+qv2I+adqGUdZHm46zXAQdZoOA=='
    
    $REG_ID1='breakroom499-contoso-tstrsd-007'
    $REG_ID2='mainbuilding167-contoso-hpsd-088'
    
    $hmacsha256 = New-Object System.Security.Cryptography.HMACSHA256
    $hmacsha256.key = [Convert]::FromBase64String($key)
    $sig1 = $hmacsha256.ComputeHash([Text.Encoding]::ASCII.GetBytes($REG_ID1))
    $sig2 = $hmacsha256.ComputeHash([Text.Encoding]::ASCII.GetBytes($REG_ID2))
    $derivedkey1 = [Convert]::ToBase64String($sig1)
    $derivedkey2 = [Convert]::ToBase64String($sig2)
    
    echo "`n`n$REG_ID1 : $derivedkey1`n$REG_ID2 : $derivedkey2`n`n"
    
    breakroom499-contoso-tstrsd-007 : JC8F96eayuQwwz+PkE7IzjH2lIAjCUnAa61tDigBnSs=
    mainbuilding167-contoso-hpsd-088 : 6uejA9PfkQgmYylj8Zerp3kcbeVrGZ172YLa7VSnJzg=
    

模拟设备将使用含有每个注册 ID 的派生的设备密钥,以执行对称密钥证明。The simulated devices will use the derived device keys with each registration ID to perform symmetric key attestation.

准备 Azure IoT C SDK 开发环境Prepare an Azure IoT C SDK development environment

在本部分,你将准备一个用于生成 Azure IoT C SDK 的开发环境。In this section, you prepare the development environment used to build the Azure IoT C SDK. SDK 包含模拟设备的示例代码。The SDK includes the sample code for the simulated device. 该模拟设备将尝试在设备启动顺序期间进行预配。This simulated device will attempt provisioning during the device's boot sequence.

本部分面向基于 Windows 的工作站。This section is oriented toward a Windows-based workstation. 对于 Linux 示例,请参阅如何进行多租户预配中的 VM 的设置。For a Linux example, see the set-up of the VMs in How to provision for multitenancy.

  1. 下载 CMake 生成系统Download the CMake build system.

    在进行 CMake 安装之前,必须在计算机上安装 Visual Studio 必备组件(Visual Studio 和“使用 C++ 的桌面开发”工作负荷)。It is important that the Visual Studio prerequisites (Visual Studio and the 'Desktop development with C++' workload) are installed on your machine, before starting the CMake installation. 满足先决条件并验证下载内容后,安装 CMake 生成系统。Once the prerequisites are in place, and the download is verified, install the CMake build system.

  2. 查找最新版本 SDK 的标记名称。Find the tag name for the latest release of the SDK.

  3. 打开命令提示符或 Git Bash shell。Open a command prompt or Git Bash shell. 运行以下命令以克隆最新版本的 Azure IoT C SDK GitHub 存储库。Run the following commands to clone the latest release of the Azure IoT C SDK GitHub repository. 使用在上一步中找到的标记作为 -b 参数的值:Use the tag you found in the previous step as the value for the -b parameter:

    git clone -b <release-tag> https://github.com/Azure/azure-iot-sdk-c.git
    cd azure-iot-sdk-c
    git submodule update --init
    

    应该预料到此操作需要几分钟才能完成。You should expect this operation to take several minutes to complete.

  4. 在 git 存储库的根目录中创建 cmake 子目录,并导航到该文件夹。Create a cmake subdirectory in the root directory of the git repository, and navigate to that folder. azure-iot-sdk-c 目录运行以下命令:Run the following commands from the azure-iot-sdk-c directory:

    mkdir cmake
    cd cmake
    
  5. 运行以下命令,生成特定于你的开发客户端平台的 SDK 版本。Run the following command, which builds a version of the SDK specific to your development client platform. 将在 cmake 目录中生成模拟设备的 Visual Studio 解决方案。A Visual Studio solution for the simulated device will be generated in the cmake directory.

    cmake -Dhsm_type_symm_key:BOOL=ON -Duse_prov_client:BOOL=ON  ..
    

    如果 cmake 找不到 C++ 编译器,则在运行该命令时可能会出现生成错误。If cmake doesn't find your C++ compiler, you might get build errors while running the command. 如果出现这种情况,请尝试在 Visual Studio 命令提示符窗口中运行该命令。If that happens, try running the command in the Visual Studio command prompt.

    生成成功后,最后的几个输出行如下所示:Once the build succeeds, the last few output lines will look similar to the following output:

    $ cmake -Dhsm_type_symm_key:BOOL=ON -Duse_prov_client:BOOL=ON  ..
    -- Building for: Visual Studio 15 2017
    -- Selecting Windows SDK version 10.0.16299.0 to target Windows 10.0.17134.
    -- The C compiler identification is MSVC 19.12.25835.0
    -- The CXX compiler identification is MSVC 19.12.25835.0
    
    ...
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: E:/IoT Testing/azure-iot-sdk-c/cmake
    

模拟设备Simulate the devices

在本部分,你将更新前面设置的、位于 Azure IoT C SDK 中的名为 prov_dev_client_sample 的预配示例。In this section, you update a provisioning sample named prov_dev_client_sample located in the Azure IoT C SDK you set up previously.

此示例代码模拟将预配请求发送到你的设备预配服务实例的设备启动序列。This sample code simulates a device boot sequence that sends the provisioning request to your Device Provisioning Service instance. 启动序列将导致烤箱设备被识别,且使用自定义分配策略将其配置到 IoT 中心。The boot sequence will cause the toaster device to be recognized and assigned to the IoT hub using the custom allocation policy.

  1. 在 Azure 门户中,选择设备预配服务的“概述”选项卡,记下“ID 范围”的值。 In the Azure portal, select the Overview tab for your Device Provisioning service and note down the ID Scope value.

    从门户边栏选项卡中提取设备预配服务终结点信息

  2. 在 Visual Studio 中,打开较早前通过运行 CMake 生成的 azure_iot_sdks.sln 解决方案文件。In Visual Studio, open the azure_iot_sdks.sln solution file that was generated by running CMake earlier. 解决方案文件应位于以下位置:The solution file should be in the following location:

    azure-iot-sdk-c\cmake\azure_iot_sdks.sln
    
  3. 在 Visual Studio 的“解决方案资源管理器”窗口中,导航到 Provision_Samples 文件夹。 In Visual Studio's Solution Explorer window, navigate to the Provision_Samples folder. 展开名为 prov_dev_client_sample 的示例项目。Expand the sample project named prov_dev_client_sample. 展开“源文件”,打开 prov_dev_client_sample.cExpand Source Files, and open prov_dev_client_sample.c.

  4. 找到 id_scope 常量,将值替换为前面复制的“ID 范围”值。 Find the id_scope constant, and replace the value with your ID Scope value that you copied earlier.

    static const char* id_scope = "0ne00002193";
    
  5. 在同一文件中找到 main() 函数的定义。Find the definition for the main() function in the same file. 确保 hsm_type 变量设置为 SECURE_DEVICE_TYPE_SYMMETRIC_KEY,如下所示:Make sure the hsm_type variable is set to SECURE_DEVICE_TYPE_SYMMETRIC_KEY as shown below:

    SECURE_DEVICE_TYPE hsm_type;
    //hsm_type = SECURE_DEVICE_TYPE_TPM;
    //hsm_type = SECURE_DEVICE_TYPE_X509;
    hsm_type = SECURE_DEVICE_TYPE_SYMMETRIC_KEY;
    
  6. 右键单击“prov_dev_client_sample”项目, 然后选择“设为启动项目”。 Right-click the prov_dev_client_sample project and select Set as Startup Project.

模拟 Contoso 烤箱设备Simulate the Contoso toaster device

  1. 若要模拟烤箱设备,请在 prov_dev_client_sample.c 中找到已注释掉的对 prov_dev_set_symmetric_key_info() 的调用。To simulate the toaster device, find the call to prov_dev_set_symmetric_key_info() in prov_dev_client_sample.c which is commented out.

    // Set the symmetric key if using they auth type
    //prov_dev_set_symmetric_key_info("<symm_registration_id>", "<symmetric_Key>");
    

    取消注释该函数调用,并将占位符值(包括尖括号)替换为烤箱注册 ID 以及前面生成的派生设备密钥。Uncomment the function call and replace the placeholder values (including the angle brackets) with the toaster registration ID and derived device key you generated previously. 下面显示的密钥值 JC8F96eayuQwwz+PkE7IzjH2lIAjCUnAa61tDigBnSs= 仅作为示例提供。The key value JC8F96eayuQwwz+PkE7IzjH2lIAjCUnAa61tDigBnSs= shown below is only given as an example.

    // Set the symmetric key if using they auth type
    prov_dev_set_symmetric_key_info("breakroom499-contoso-tstrsd-007", "JC8F96eayuQwwz+PkE7IzjH2lIAjCUnAa61tDigBnSs=");
    

    保存文件。Save the file.

  2. 在 Visual Studio 菜单中,选择“调试” > “开始执行(不调试)”以运行该解决方案。 On the Visual Studio menu, select Debug > Start without debugging to run the solution. 出现重新生成项目的提示时,请选择“是”,以便在运行项目之前重新生成项目 。In the prompt to rebuild the project, select Yes, to rebuild the project before running.

    以下输出是模拟烤箱设备成功启动并连接到预配服务实例以通过自定义分配策略分配到烤箱 IoT 中心的一个示例:The following output is an example of the simulated toaster device successfully booting up and connecting to the provisioning service instance to be assigned to the toasters IoT hub by the custom allocation policy:

    Provisioning API Version: 1.3.6
    
    Registering Device
    
    Provisioning Status: PROV_DEVICE_REG_STATUS_CONNECTED
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    
    Registration Information received from service: contoso-toasters-hub-1098.azure-devices.cn, deviceId: breakroom499-contoso-tstrsd-007
    
    Press enter key to exit:
    

模拟 Contoso 热泵设备Simulate the Contoso heat pump device

  1. 若要模拟热泵设备,请使用热泵注册 ID 和之前生成的派生设备密钥更新 prov_dev_client_sample.c 中的 prov_dev_set_symmetric_key_info() 调用。To simulate the heat pump device, update the call to prov_dev_set_symmetric_key_info() in prov_dev_client_sample.c again with the heat pump registration ID and derived device key you generated earlier. 下面显示的密钥值 6uejA9PfkQgmYylj8Zerp3kcbeVrGZ172YLa7VSnJzg= 也仅作为示例提供。The key value 6uejA9PfkQgmYylj8Zerp3kcbeVrGZ172YLa7VSnJzg= shown below is also only given as an example.

    // Set the symmetric key if using they auth type
    prov_dev_set_symmetric_key_info("mainbuilding167-contoso-hpsd-088", "6uejA9PfkQgmYylj8Zerp3kcbeVrGZ172YLa7VSnJzg=");
    

    保存文件。Save the file.

  2. 在 Visual Studio 菜单中,选择“调试” > “开始执行(不调试)”以运行该解决方案。 On the Visual Studio menu, select Debug > Start without debugging to run the solution. 出现重新生成项目的提示时,请选择“是”,以便在运行项目之前重新生成项目 。In the prompt to rebuild the project, select Yes to rebuild the project before running.

    以下输出是模拟热泵设备成功启动并连接到预配服务实例以通过自定义分配策略分配到 Contoso 热泵 IoT 中心的一个示例:The following output is an example of the simulated heat pump device successfully booting up and connecting to the provisioning service instance to be assigned to the Contoso heat pumps IoT hub by the custom allocation policy:

    Provisioning API Version: 1.3.6
    
    Registering Device
    
    Provisioning Status: PROV_DEVICE_REG_STATUS_CONNECTED
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    
    Registration Information received from service: contoso-heatpumps-hub-1098.azure-devices.cn, deviceId: mainbuilding167-contoso-hpsd-088
    
    Press enter key to exit:
    

自定义分配策略疑难解答Troubleshooting custom allocation policies

下表显示了预期场景以及可能遇到的结果错误代码。The following table shows expected scenarios and the results error codes you might receive. 使用此表来帮助你解决使用 Azure 函数时自定义分配策略失败的问题。Use this table to help troubleshoot custom allocation policy failures with your Azure Functions.

方案Scenario 预配服务的注册结果Registration result from Provisioning Service 预配 SDK 结果Provisioning SDK Results
Webhook 返回“200 正常”,其中“iotHubHostName”设置为有效的 IoT 中心主机名The webhook returns 200 OK with 'iotHubHostName' set to a valid IoT hub host name 结果状态:已分配Result status: Assigned SDK 返回 PROV_DEVICE_RESULT_OK 和中心信息SDK returns PROV_DEVICE_RESULT_OK along with hub information
Webhook 返回“200 正常”,响应中存在“iotHubHostName”,但设置为空字符串或 nullThe webhook returns 200 OK with 'iotHubHostName' present in the response, but set to an empty string or null 结果状态:已失败Result status: Failed

错误代码:CustomAllocationIotHubNotSpecified (400208)Error code: CustomAllocationIotHubNotSpecified (400208)
SDK 返回 PROV_DEVICE_RESULT_HUB_NOT_SPECIFIEDSDK returns PROV_DEVICE_RESULT_HUB_NOT_SPECIFIED
Webhook 返回 401 未授权The webhook returns 401 Unauthorized 结果状态:已失败Result status: Failed

错误代码:CustomAllocationUnauthorizedAccess (400209)Error code: CustomAllocationUnauthorizedAccess (400209)
SDK 返回 PROV_DEVICE_RESULT_UNAUTHORIZEDSDK returns PROV_DEVICE_RESULT_UNAUTHORIZED
创建了个人注册,以禁用设备An Individual Enrollment was created to disable the device 结果状态:已禁用Result status: Disabled SDK 返回 PROV_DEVICE_RESULT_DISABLEDSDK returns PROV_DEVICE_RESULT_DISABLED
Webhook 返回错误代码 > = 429The webhook returns error code >= 429 DPS 业务流程将多次重试。DPS' orchestration will retry a number of times. 重试策略当前:The retry policy is currently:

  - 重试计数:10 个  - Retry count: 10
  - 初始时间间隔:1 秒  - Initial interval: 1s
  - 增量:9 秒  - Increment: 9s
SDK 将忽略错误并在指定时间提交另一个获取状态消息SDK will ignore error and submit another get status message in the specified time
Webhook 返回任何其他状态代码The webhook returns any other status code 结果状态:已失败Result status: Failed

错误代码:CustomAllocationFailed (400207)Error code: CustomAllocationFailed (400207)
SDK 返回 PROV_DEVICE_RESULT_DEV_AUTH_ERRORSDK returns PROV_DEVICE_RESULT_DEV_AUTH_ERROR

清理资源Clean up resources

如果你打算继续使用本文中创建的资源,可以保留它们。If you plan to continue working with the resources created in this article, you can leave them. 如果你不打算继续使用这些资源,请使用以下步骤删除本文创建的所有资源,以避免不必要的费用。If you don't plan to continue using the resources, use the following steps to delete all of the resources created in this article to avoid unnecessary charges.

此处的步骤假定你按照名为 contoso-us-resource-group 的同一资源组的指示创建了本文中的所有资源。The steps here assume you created all resources in this article as instructed in the same resource group named contoso-us-resource-group.

重要

删除资源组的操作不可逆。Deleting a resource group is irreversible. 资源组以及包含在其中的所有资源将被永久删除。The resource group and all the resources contained in it are permanently deleted. 请确保不要意外删除错误的资源组或资源。Make sure that you don't accidentally delete the wrong resource group or resources. 如果在现有的包含要保留资源的资源组中创建了 IoT 中心,则只删除 IoT 中心资源本身,而不要删除资源组。If you created the IoT Hub inside an existing resource group that contains resources you want to keep, only delete the IoT Hub resource itself instead of deleting the resource group.

若要按名称删除资源组:To delete the resource group by name:

  1. 登录到 Azure 门户,然后选择“资源组”。 Sign in to the Azure portal and select Resource groups.

  2. 在“按名称筛选...” 文本框中,键入包含资源的资源组名称“contoso-us-resource-group” 。In the Filter by name... textbox, type the name of the resource group containing your resources, contoso-us-resource-group.

  3. 在结果列表中的资源组右侧,选择“...”,然后选择“删除资源组” 。To the right of your resource group in the result list, select ... then Delete resource group.

  4. 系统会要求确认是否删除该资源组。You'll be asked to confirm the deletion of the resource group. 再次键入资源组的名称进行确认,然后选择“删除” 。Type the name of your resource group again to confirm, and then select Delete. 片刻之后,将会删除该资源组及其包含的所有资源。After a few moments, the resource group and all of its contained resources are deleted.

后续步骤Next steps