教程:通过设备预配服务 (DPS) 使用自定义分配策略Tutorial: Use custom allocation policies with Device Provisioning Service (DPS)

自定义分配策略让你能够对设备分配到 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 that runs during provisioning 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 an enrollment group with a custom allocation policy that uses an Azure Function written in C# to provision toaster devices using symmetric keys. 不会将任何未被识别为烤箱设备的设备预配到 IoT 中心。Any device not recognized as a toaster device will not be provisioned to an IoT hub.

设备会请求使用 Azure IoT C SDK 中包含的预配示例代码进行预配。Devices will request provisioning using provisioning sample code included in the Azure IoT C SDK.

在本教程中,你将执行以下操作:In this tutorial you will do the following:

  • 创建新的 Azure 函数应用来支持自定义分配函数Create a new Azure Function App to support a custom allocation function
  • 使用 Azure 函数为自定义分配策略创建新的组注册Create a new group enrollment using an Azure Function for the custom allocation policy
  • 为两个设备创建设备密钥Create device keys for two devices
  • Azure IoT C SDK 中的示例设备代码设置开发环境Set up the development environment for example device code from the Azure IoT C SDK
  • 运行设备,并验证是否已根据自定义分配策略预配它们Run the devices and verify that they are provisioned according to the custom allocation policy

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

先决条件Prerequisites

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

在本部分,你将创建一个实现自定义分配策略的 Azure 函数。In this section, you create an Azure function that implements your custom allocation policy. 此函数决定了是否应根据设备的注册 ID 是否包含字符串前缀 contoso-toaster 将设备注册到 IoT 中心。This function decides whether a device should be registered to your IoT Hub based on whether its registration ID contains the string prefix contoso-toaster.

  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:

    订阅:如果你有多个订阅,但未选择所需的订阅,请选择要使用的订阅。Subscription: If you have multiple subscriptions and the desired subscription is not selected, select the subscription you want to use.

    资源组:此字段允许你创建新的资源组或选择现有资源组来包含函数应用。Resource Group: This field allows you to create a new resource group, or choose an existing one to contain the function app. 选择你之前创建的用于测试的 IoT 中心所在的资源组,例如 TestResourcesChoose the same resource group that contains the Iot hub you created for testing previously, for example, TestResources. 通过将所有相关资源都放在一个组中,可以一起管理它们。By putting all related resources in a group together, you can manage them together.

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

    发布:验证是否选择了“代码”。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. 此示例使用“中国东部 2”。This example uses China East 2.

    备注

    默认已启用 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 under Functions click Functions and then + Add to add a new function.

  6. 在模板页上,选择“HTTP 触发器”磁贴,然后选择“创建函数”。On the templates page, select the HTTP Trigger tile, then select Create Function. 将创建一个名为 HttpTrigger1 的函数,而门户则会显示该函数的概览页。A function named HttpTrigger1 is created, and the portal displays the overview page for your function.

  7. 针对你的新函数单击“代码 + 测试”。Click Code + Test for your new function. 门户会显示 run.csx 代码文件的内容。The portal displays the contents of the run.csx code file.

  8. HttpTrigger1 函数的代码替换为以下代码,然后选择“保存”。Replace the code for the HttpTrigger1 function with the following code and select Save. 你的自定义分配代码已准备就绪,可供使用。Your custom allocation code is ready to be used.

    #r "Newtonsoft.Json"
    
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    using Newtonsoft.Json;
    
    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 
                if (regId.Contains("contoso-toaster"))
                {
                    //Log IoT hubs configured for the enrollment
                    foreach(string hubString in hubs)
                    {
                        log.LogInformation("linkedHub : " + hubString);
                    }
    
                    obj.iotHubHostName = hubs[0];
                    log.LogInformation("Selected hub : " + obj.iotHubHostName);
                }
                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;}
    }
    
  9. run.csx 代码文件底部的下方,单击“日志”以监视来自自定义分配函数的日志记录。Just below the bottom of the run.csx code file, click Logs to monitor the logging from the custom allocation function.

创建注册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 information in the table below and click the Save button.

    字段Field 说明和/或建议的值Description and/or suggested value
    组名Group name 输入 contoso-custom-allocated-devicesEnter contoso-custom-allocated-devices
    证明类型Attestation Type 选择“对称密钥”Select Symmetric Key
    自动生成密钥Auto Generate Keys 此复选框应已处于选中状态。This checkbox should already be checked.
    选择要如何将设备分配到中心Select how you want to assign devices to hubs 选择“自定义(使用 Azure 函数)”Select Custom (Use Azure Function)
    选择此组可分配到的 IoT 中心Select the IoT hubs this group can be assigned to 选择你之前在完成快速入门时创建的 IoT 中心。Select the IoT hub you created previously when you completed the quick start.
    选择 Azure 函数Select Azure Function 选择包含你创建的函数应用的订阅。Select the subscription that contains the function app you created. 然后,为该函数选择 contoso-function-appHttpTrigger1Then select the contoso-function-app and HttpTrigger1 for the function.

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

  4. 保存注册后,重新打开它,并记录“主键”。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 primary symmetric key will be used to generate unique device keys for devices that attempt provisioning later.

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

设备不直接使用主对称密钥。Devices don't use the primary symmetric key directly. 你可以改为使用主密钥来派生每个设备的设备密钥。Instead you use the primary key to derive a device key for each device. 在本部分,你将创建两个唯一的设备密钥。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. 生成的密钥会允许这两个设备尝试注册。The keys generated will allow both devices to attempt a registration. 只有一个设备注册 ID 将具有自定义分配策略示例代码可接受的有效后缀。Only one device registration ID will have a valid suffix to be accepted by custom allocation policy example code. 因此,一个会被接受,另一个会被拒绝As a result, one will be accepted and the other rejected

若要派生设备密钥,请使用前面记下的对称密钥来计算每个设备的设备注册 ID 的 HMAC-SHA256,并将结果转换为 Base64 格式。To derive the device key, you use the symmetric 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 with the code below to compute a device key for both devices:

  • contoso-toaster-007contoso-toaster-007
  • contoso-heatpump-088contoso-heatpump-088

KEY 变量的值替换为你之前在创建注册组后记下的 主密钥Replace the value of KEY variable with the Primary Key you noted earlier after your enrollment group was created. 下面的代码显示的密钥值和输出只是一个示例。The key value and output shown with the code below is only an example.

如果使用的是基于 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.

$KEY='oiK77Oy7rBw8YB6IS6ukRChAw+Yq6GC61RMrPLSTiOOtdI+XDu0LmLuNm11p+qv2I+adqGUdZHm46zXAQdZoOA=='

$REG_ID1='contoso-toaster-007'
$REG_ID2='contoso-heatpump-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"
contoso-toaster-007 : JC8F96eayuQwwz+PkE7IzjH2lIAjCUnAa61tDigBnSs=
contoso-heatpump-088 : 6uejA9PfkQgmYylj8Zerp3kcbeVrGZ172YLa7VSnJzg=

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

设备会请求使用 Azure IoT C SDK 中包含的预配示例代码进行预配。Devices will request provisioning using provisioning sample code included in the Azure IoT C SDK.

在本部分,你将准备一个用于生成 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. main() 函数中,找到对 Prov_Device_Register_Device() 的调用。In the main() function, find the call to Prov_Device_Register_Device(). 在调用之前,添加以下几行代码,这些代码在预配期间使用 Prov_Device_Set_Provisioning_Payload() 传递自定义 JSON 有效负载。Just before that call, add the following lines of code that use Prov_Device_Set_Provisioning_Payload() to pass a custom JSON payload during provisioning. 可通过这种方式为自定义分配函数提供详细信息。This can be used to provide more information to your custom allocation functions. 也可通过这种方式传递设备类型,而不检查注册 ID。This could also be used to pass the device type instead of examining the registration ID.

    // An example custom payload
    const char* custom_json_payload = "{\"MyDeviceFirmwareVersion\":\"12.0.2.5\",\"MyDeviceProvisioningVersion\":\"1.0.0.0\"}";
    
    prov_device_result = Prov_Device_Set_Provisioning_Payload(prov_device_handle, custom_json_payload);
    if (prov_device_result != PROV_DEVICE_RESULT_OK)
    {
        (void)printf("\r\nFailure setting provisioning payload: %s\r\n", MU_ENUM_TO_STRING(PROV_DEVICE_RESULT, prov_device_result));
    }
    
  7. 右键单击“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("contoso-toaster-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.

    以下测试是为烤箱设备运行的自定义分配函数代码的示例日志记录输出。The following text is example logging output from the custom allocation function code running for the toaster device. 请注意,已成功为烤箱设备选择了一个中心。Notice a hub is successfully selected for a toaster device. 还要注意 payload 成员,它包含你添加到代码中的自定义 JSON 内容。Also notice the payload member that contains the custom JSON content you added to the code. 这可供你的代码在 deviceRuntimeContext 中使用。This is available for your code to use within the deviceRuntimeContext.

    在门户中的函数代码下单击“日志”可以获得此日志记录:This logging is available by clicking Logs under the function code in the portal:

    2020-09-23T11:44:37.505 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=4596d45e-086f-4e86-929b-4a02814eee40)
    2020-09-23T11:44:41.380 [Information] C# HTTP trigger function processed a request.
    2020-09-23T11:44:41.381 [Information] Request.Body:...
    2020-09-23T11:44:41.381 [Information] {"enrollmentGroup":{"enrollmentGroupId":"contoso-custom-allocated-devices","attestation":{"type":"symmetricKey"},"capabilities":{"iotEdge":false},"etag":"\"e8002126-0000-0100-0000-5f6b2a570000\"","provisioningStatus":"enabled","reprovisionPolicy":{"updateHubAssignment":true,"migrateDeviceData":true},"createdDateTimeUtc":"2020-09-23T10:58:31.62286Z","lastUpdatedDateTimeUtc":"2020-09-23T10:58:31.62286Z","allocationPolicy":"custom","iotHubs":["contoso-toasters-hub-1098.azure-devices.net"],"customAllocationDefinition":{"webhookUrl":"https://contoso-function-app.azurewebsites.net/api/HttpTrigger1?****","apiVersion":"2019-03-31"}},"deviceRuntimeContext":{"registrationId":"contoso-toaster-007","symmetricKey":{},"payload":{"MyDeviceFirmwareVersion":"12.0.2.5","MyDeviceProvisioningVersion":"1.0.0.0"}},"linkedHubs":["contoso-toasters-hub-1098.azure-devices.net"]}
    2020-09-23T11:44:41.687 [Information] linkedHub : contoso-toasters-hub-1098.azure-devices.net
    2020-09-23T11:44:41.688 [Information] Selected hub : contoso-toasters-hub-1098.azure-devices.net
    2020-09-23T11:44:41.688 [Information] Response
    2020-09-23T11:44:41.688 [Information] {"iotHubHostName":"contoso-toasters-hub-1098.azure-devices.net"}
    2020-09-23T11:44:41.689 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=4596d45e-086f-4e86-929b-4a02814eee40, Duration=4347ms)    
    

    以下示例设备输出显示模拟的烤箱设备成功启动并连接到预配服务实例,因此可以通过自定义分配策略分配到烤箱 IoT 中心:The following example device output shows 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.net, deviceId: contoso-toaster-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("contoso-heatpump-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.

    以下测试是为热泵设备运行的自定义分配函数代码的示例日志记录输出。The following text is example logging output from the custom allocation function code running for the heat pump device. 自定义分配策略拒绝此注册并显示 HTTP 错误“400 错误的请求”。The custom allocation policy rejects this registration with a HTTP error 400 Bad Request. 注意 payload 成员,它包含你添加到代码中的自定义 JSON 内容。Notice the payload member that contains the custom JSON content you added to the code. 这可供你的代码在 deviceRuntimeContext 中使用。This is available for your code to use within the deviceRuntimeContext.

    在门户中的函数代码下单击“日志”可以获得此日志记录:This logging is available by clicking Logs under the function code in the portal:

    2020-09-23T11:50:23.652 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=2fa77f10-42f8-43fe-88d9-a8c01d4d3f68)
    2020-09-23T11:50:23.653 [Information] C# HTTP trigger function processed a request.
    2020-09-23T11:50:23.654 [Information] Request.Body:...
    2020-09-23T11:50:23.654 [Information] {"enrollmentGroup":{"enrollmentGroupId":"contoso-custom-allocated-devices","attestation":{"type":"symmetricKey"},"capabilities":{"iotEdge":false},"etag":"\"e8002126-0000-0100-0000-5f6b2a570000\"","provisioningStatus":"enabled","reprovisionPolicy":{"updateHubAssignment":true,"migrateDeviceData":true},"createdDateTimeUtc":"2020-09-23T10:58:31.62286Z","lastUpdatedDateTimeUtc":"2020-09-23T10:58:31.62286Z","allocationPolicy":"custom","iotHubs":["contoso-toasters-hub-1098.azure-devices.net"],"customAllocationDefinition":{"webhookUrl":"https://contoso-function-app.azurewebsites.net/api/HttpTrigger1?****","apiVersion":"2019-03-31"}},"deviceRuntimeContext":{"registrationId":"contoso-heatpump-088","symmetricKey":{},"payload":{"MyDeviceFirmwareVersion":"12.0.2.5","MyDeviceProvisioningVersion":"1.0.0.0"}},"linkedHubs":["contoso-toasters-hub-1098.azure-devices.net"]}
    2020-09-23T11:50:23.654 [Information] Unknown device registration
    2020-09-23T11:50:23.654 [Information] Response
    2020-09-23T11:50:23.654 [Information] Unrecognized device registration.
    2020-09-23T11:50:23.655 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=2fa77f10-42f8-43fe-88d9-a8c01d4d3f68, Duration=11ms)
    

    以下示例设备输出显示模拟的热泵设备启动并连接到预配服务实例,因此可以尝试使用自定义分配策略注册到 IoT 中心。The following example device output shows the simulated heat pump device booting up and connecting to the provisioning service instance to attempt registration to an IoT hub using the custom allocation policy. 此操作失败并出现错误“Custom allocation failed with status code: 400”,因为自定义分配策略设计为仅允许烤箱设备:This fails with error (Custom allocation failed with status code: 400) since the custom allocation policy was designed to only allows toaster devices:

    Provisioning API Version: 1.3.7
    
    Registering Device
    
    Provisioning Status: PROV_DEVICE_REG_STATUS_CONNECTED
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    Provisioning Status: PROV_DEVICE_REG_STATUS_ASSIGNING
    Error: Time:Wed Sep 23 13:06:01 2020 File:d:\testing\azure-iot-sdk-c\provisioning_client\src\prov_device_ll_client.c Func:_prov_transport_process_json_reply Line:658 Provisioning Failure: OperationId: 4.eb89f3e8407a3711.2525bd34-02e9-4e91-a9c0-4dbc4ad5de66 - Date: 2020-09-23T17:05:58.2363145Z - Msg: Custom allocation failed with status code: 400
    Error: Time:Wed Sep 23 13:06:01 2020 File:d:\testing\azure-iot-sdk-c\provisioning_client\src\prov_transport_mqtt_common.c Func:_prov_transport_common_mqtt_dowork Line:1014 Unable to process registration reply.
    Error: Time:Wed Sep 23 13:06:01 2020 File:d:\testing\azure-iot-sdk-c\provisioning_client\src\prov_device_ll_client.c Func:_on_transport_registration_data Line:770 Failure retrieving data from the provisioning service
    
    Failure registering device: PROV_DEVICE_RESULT_DEV_AUTH_ERROR
    Press enter key to exit:    
    

清理资源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