Share via


使用裝置佈建服務 (DPS) 在 Azure Digital Twins 中自動管理裝置

在本文中,您將了解如何整合 Azure Digital Twins 與裝置佈建服務 (DPS)

本文所述的解決方案可讓您使用裝置佈建服務,在 Azure Digital Twins 中將佈建和淘汰 IoT 中樞裝置的流程自動化。

如需佈建和淘汰階段的詳細資訊,並進一步了解所有企業 IoT 專案通用的一般裝置管理階段集合,請參閱 IoT 中樞裝置管理文件的<裝置生命週期>一節

必要條件

您必須先設定下列資源,才能設定佈建:

  • Azure Digital Twins 執行個體。 按照設定執行個體和驗證中的指示,建立 Azure Digital Twins 執行個體。 在 Azure 入口網站中收集該執行個體的主機名稱 (指示)。
  • IoT 中樞。 如需指示,請參閱 IoT 中樞快速入門的<建立 IoT 中樞>一節。
  • 根據 IoT 中樞資料更新數位對應項資訊的 Azure 函式。 按照內嵌 IoT 中樞資料中的指示,建立此 Azure 函式。 收集函式 name,以在本文中使用。

此範例也會使用「裝置模擬器」,其中包含使用裝置佈建服務佈建。 裝置模擬器位於此處:Azure Digital Twins 和 IoT 中樞整合範例。 藉由瀏覽至範例的 GitHub 存放庫,您可以選取 [程式碼] 按鈕及 [下載 ZIP] 按鈕,以 .zip 檔案形式下載範例,來取得電腦上的範例專案。

Screenshot of the digital-twins-iothub-integration repo on GitHub, highlighting the steps to download it as a zip.

將下載的資料夾解壓縮。

您必須在機器上安裝 Node.js。 裝置模擬器是以 Node.js 10.0.x 版或更新版本為基礎。

解決方案架構

此解決方案包含使用裝置佈建服務,在 Azure Digital Twins 中佈建和淘汰裝置的步驟。

若要在解決方案中配置裝置,資料會在控溫器裝置與 DPS 之間流動。 資料接著會從 DPS 流向 IoT 中樞,並透過 Azure 函式流向 Azure Digital Twins。

若要淘汰裝置,手動刪除裝置動作的資料會透過 IoT 中樞、事件中樞和 Azure 函式,流入 Azure Digital Twins。

下圖說明此架構。

Diagram of device and several Azure services in an end-to-end scenario showing the data flow.

本文分成兩個章節,分別著重於此完整架構的一部分:

使用裝置佈建服務自動佈建裝置

在本節中,您會透過以下路徑,將裝置佈建服務連結至 Azure Digital Twins,以自動佈建裝置。 此圖表是先前所示完整架構的摘要。

Diagram of Provision flow—an excerpt of the solution architecture diagram following data from a thermostat into Azure Digital Twins.

以下是程序流程的說明:

  1. 裝置會連絡 DPS 端點,傳遞識別資訊來證明其身分識別。
  2. DPS 會根據註冊清單驗證註冊識別碼和金鑰,藉此驗證裝置身分識別,並呼叫 Azure 函式來配置。
  3. Azure 函式會在裝置的 Azure Digital Twins 中建立新的對應項。 對應項會與裝置的註冊識別碼有相同名稱。
  4. DPS 會使用 IoT 中樞來註冊裝置,並填入裝置的所選對應項狀態。
  5. IoT 中樞會將裝置識別碼資訊和 IoT 中樞連線資訊傳回至裝置。 裝置現在可以連線到 IoT 中樞。

下列各節會逐步說明設定此自動佈建裝置流程的步驟。

建立裝置佈建服務

使用裝置佈建服務佈建新裝置時,可以在與註冊識別碼具有相同名稱的 Azure Digital Twins 中,建立該裝置的新對應項。

建立裝置佈建服務執行個體,這會用來佈建 IoT 裝置。 您可以使用下列 Azure CLI 指示,或按照使用 Azure 入口網站設定 IoT 中樞裝置佈建服務,使用 Azure 入口網站。

以下 Azure CLI 命令會建立裝置佈建服務。 您必須指定裝置佈建服務名稱、資源群組和區域。 若要查看哪些區域支援裝置佈建服務,請造訪依區域提供的 Azure 產品。 此命令可在 Cloud Shell 中執行,或者如果您在機器上安裝 Azure CLI,可在本機執行。

az iot dps create --name <Device-Provisioning-Service-name> --resource-group <resource-group-name> --location <region>

新增函式以搭配裝置佈建服務使用

在您在<必要條件>一節中建立的函式應用程式專案內,您會建立新的函式以搭配裝置佈建服務使用。 裝置佈建服務會在自訂配置原則中使用此函式,佈建新的裝置。

瀏覽至您機器上的函式應用程式專案,並按照下列步驟操作。

  1. 首先,在函式應用程式專案中建立 HTTP 觸發程序類型的新函式。

  2. 將新的 NuGet 套件新增至專案:Microsoft.Azure.Devices.Provisioning.Service。 如果程式碼中使用的套件尚未加入專案中,您可能也需要將更多套件新增至您的專案。

  3. 在新建立的函式程式碼檔案中,貼上下列程式碼,將函式命名為 DpsAdtAllocationFunc.cs,然後儲存檔案。

    // Copyright (c) Microsoft. All rights reserved.
    // Licensed under the MIT license. See LICENSE file in the project root for full license information.
    
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Azure;
    using Azure.Core.Pipeline;
    using Azure.DigitalTwins.Core;
    using Azure.Identity;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.Azure.Devices.Shared;
    using Microsoft.Azure.Devices.Provisioning.Service;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    
    namespace Samples.AdtIothub
    {
        public static class DpsAdtAllocationFunc
        {
            private static string adtInstanceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL");
            private static readonly HttpClient singletonHttpClientInstance = new HttpClient();
    
            [FunctionName("DpsAdtAllocationFunc")]
            public static async Task<IActionResult> Run(
                [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log)
            {
                // Get request body
                string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                log.LogDebug($"Request.Body: {requestBody}");
                dynamic data = JsonConvert.DeserializeObject(requestBody);
    
                // Get registration ID of the device
                string regId = data?.deviceRuntimeContext?.registrationId;
    
                bool fail = false;
                string message = "Uncaught error";
                var response = new ResponseObj();
    
                // Must have unique registration ID on DPS request
                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
                        || hubs.Length < 1)
                    {
                        message = "No hub group defined for the enrollment.";
                        log.LogInformation("linkedHubs: NULL");
                        fail = true;
                    }
                    else
                    {
                        // Find or create twin based on the provided registration ID and model ID
                        dynamic payloadContext = data?.deviceRuntimeContext?.payload;
                        string dtmi = payloadContext.modelId;
                        log.LogDebug($"payload.modelId: {dtmi}");
                        string dtId = await FindOrCreateTwinAsync(dtmi, regId, log);
    
                        // Get first linked hub (TODO: select one of the linked hubs based on policy)
                        response.iotHubHostName = hubs[0];
    
                        // Specify the initial tags for the device.
                        var tags = new TwinCollection();
                        tags["dtmi"] = dtmi;
                        tags["dtId"] = dtId;
    
                        // Specify the initial desired properties for the device.
                        var properties = new TwinCollection();
    
                        // Add the initial twin state to the response.
                        var twinState = new TwinState(tags, properties);
                        response.initialTwin = twinState;
                    }
                }
    
                log.LogDebug("Response: " + ((response.iotHubHostName != null)? JsonConvert.SerializeObject(response) : message));
    
                return fail
                    ? new BadRequestObjectResult(message)
                    : (ActionResult)new OkObjectResult(response);
            }
    
            public static async Task<string> FindOrCreateTwinAsync(string dtmi, string regId, ILogger log)
            {
                // Create Digital Twins client
                var cred = new DefaultAzureCredential();
                var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), cred);
    
                // Find existing DigitalTwin with registration ID
                try
                {
                    // Get DigitalTwin with Id 'regId'
                    BasicDigitalTwin existingDt = await client.GetDigitalTwinAsync<BasicDigitalTwin>(regId).ConfigureAwait(false);
    
                    // Check to make sure it is of the correct model type
                    if (StringComparer.OrdinalIgnoreCase.Equals(dtmi, existingDt.Metadata.ModelId))
                    {
                        log.LogInformation($"DigitalTwin {existingDt.Id} already exists");
                        return existingDt.Id;
                    }
    
                    // Found DigitalTwin but it is not of the correct model type
                    log.LogInformation($"Found DigitalTwin {existingDt.Id} but it is not of model {dtmi}");
                }
                catch(RequestFailedException ex) when (ex.Status == (int)HttpStatusCode.NotFound)
                {
                    log.LogDebug($"Did not find DigitalTwin {regId}");
                }
    
                // Either the DigitalTwin was not found, or we found it but it is of a different model type
                // Create or replace it with what it needs to be, meaning if it was not found a brand new DigitalTwin will be created
                // and if it was of a different model, it will replace that existing DigitalTwin
                // If it was intended to only create the DigitalTwin if there is no matching DigitalTwin with the same Id,
                // ETag.All could have been used as the ifNonMatch parameter to the CreateOrReplaceDigitalTwinAsync method call.
                // Read more in the CreateOrReplaceDigitalTwinAsync documentation here:
                // https://docs.microsoft.com/en-us/dotnet/api/azure.digitaltwins.core.digitaltwinsclient.createorreplacedigitaltwinasync?view=azure-dotnet
                BasicDigitalTwin dt = await client.CreateOrReplaceDigitalTwinAsync(
                    regId, 
                    new BasicDigitalTwin
                    {
                        Metadata = { ModelId = dtmi },
                        Contents = 
                        {
                            { "Temperature", 0.0 }
                        }
                    }
                ).ConfigureAwait(false);
    
                log.LogInformation($"Digital Twin {dt.Id} created.");
                return dt.Id;
            }
        }
    
        /// <summary>
        /// Expected function result format
        /// </summary>
        public class ResponseObj
        {
            public string iotHubHostName { get; set; }
            public TwinState initialTwin { get; set; }
        }
    }
    
  4. 使用 DpsAdtAllocationFunc.cs 函式,將專案發佈至 Azure 中的函式應用程式。

    如需如何使用 Visual Studio 來發佈函式的相關指示,請參閱使用 Visual Studio 開發 Azure Functions (機器翻譯)。 如需如何使用 Visual Studio Code 來發佈函式的相關指示,請參閱使用 Visual Studio Code 以在 Azure 中建立 C# 函式。 如需如何使用 Azure CLI 來發佈函式的相關指示,請參閱從命令列在 Azure 中建立 C# 函式 (機器翻譯)。

重要

第一次在<必要條件>一節中建立函式應用程式時,您可能已為函式指派存取角色,並為其設定應用程式設定,以存取您的 Azure Digital Twins 執行個體。 您必須針對整個函式應用程式進行一次上述步驟,因此請先確認已在您的應用程式中完成,再繼續進行。 您可以在「撰寫應用程式驗證碼」一文的<設定已發佈的應用程式>一節中找到指示。

建立裝置佈建註冊

接下來,您必須使用「自訂配置函式」,在裝置佈建服務中建立註冊。 若要建立註冊,請按照裝置佈建服務文件中自訂配置原則一文<建立註冊>章節中的指示操作。

進行該流程時,請確定您選取下列選項,將註冊連結至您建立的函式。

  • 選取要如何將裝置指派給中樞:[自訂 (使用 Azure 函式)]。
  • 選取可作為此群組指派對象的 IoT 中樞:選擇 IoT 中樞名稱,或選取 [連結新的 IoT 中樞] 按鈕,然後從選項中選擇您的 IoT 中樞。

接下來,選擇 [選取新的函式] 按鈕,將您的函式應用程式連結至註冊群組。 接著填入下列值:

  • 訂用帳戶:系統會自動填入您的 Azure 訂用帳戶。 請確定這是正確的訂用帳戶。
  • 函式應用程式:選擇您的函式應用程式名稱。
  • 函式:選擇 DpsAdtAllocationFunc

儲存詳細資料。

Screenshot of the Customs enrollment group details window in the Azure portal.

建立註冊之後,請選取它以檢視其設定。 複製註冊的 [主索引鍵],在本文稍後會用來設定裝置模擬器。

設定裝置模擬器

此範例使用裝置模擬器,其中包含使用裝置佈建服務佈建。 裝置模擬器位於您在<必要條件>一節中下載的 Azure Digital Twins 和 IoT 中樞整合範例

上傳模型

裝置模擬器是控溫器類型裝置,搭配以下識別碼使用模型:dtmi:contosocom:DigitalTwins:Thermostat;1。 您必須先將此模型上傳至 Azure Digital Twins,才能為裝置建立此類型的對應項。

此模型看似如下:

{
    "@id": "dtmi:contosocom:DigitalTwins:Thermostat;1",
    "@type": "Interface",
    "@context": "dtmi:dtdl:context;3",
    "contents": [
      {
        "@type": "Property",
        "name": "Temperature",
        "schema": "double"
      }
    ]
  }

若要將此模型上傳至對應項執行個體,請執行下列 Azure CLI 命令,將上述模型上傳為內嵌 JSON。 您可以在瀏覽器的 Azure Cloud Shell (使用 Bash 環境),或在您的機器上 (如果您有在本地安裝 CLI 時) 執行命令。 執行個體主機名稱有一個預留位置 (您也可以使用執行個體的自訂名稱,但效能會稍微降低)。

az dt model create --dt-name <instance-hostname-or-name> --models '{  "@id": "dtmi:contosocom:DigitalTwins:Thermostat;1",  "@type": "Interface",  "@context": "dtmi:dtdl:context;2",  "contents": [    {      "@type": "Property",      "name": "Temperature",      "schema": "double"    }  ]}' 

注意

如果您在 Bash 環境中使用 Cloud Shell 以外的任何項目,您可能需要逸出內嵌 JSON 中的特定字元,使系統正確剖析。 如需詳細資訊,請參閱在不同殼層中使用特殊字元 (機器翻譯)。

如需模型的詳細資訊,請參閱管理模型 (機器翻譯)。

設定並執行模擬器

在本機電腦的命令視窗中,瀏覽至您先前解壓縮的下載範例 Azure Digital Twins and IoT Hub Integration,然後移至 device-simulator 目錄。 接下來,使用以下命令安裝專案的相依性:

npm install

接下來,在您的裝置模擬器目錄中,將 .env.template 檔案複製到名為 .env 的新檔案,並收集下列值以填入設定:

  • PROVISIONING_IDSCOPE:若要取得此值,請瀏覽至 Azure 入口網站中的裝置佈建服務,然後在功能表選項中選取 [概觀],然後找到 [識別碼範圍]

    Screenshot of the Azure portal view of the device provisioning overview page highlighting the ID Scope value.

  • PROVISIONING_REGISTRATION_ID:您可以為裝置選擇註冊識別碼。

  • ADT_MODEL_ID:dtmi:contosocom:DigitalTwins:Thermostat;1

  • PROVISIONING_SYMMETRIC_KEY:此環境變數是您先前設定之註冊的主要金鑰。 若要再次取得此值,請在 Azure 入口網站中瀏覽至您的裝置佈建服務,選取 [管理註冊],接著選取您先前建立的註冊群組,然後複製 [主要金鑰]

    Screenshot of the Azure portal view of the device provisioning service manage enrollments page highlighting the SAS primary key value.

現在,使用上述值來更新 .env 檔案設定。

PROVISIONING_HOST = "global.azure-devices-provisioning.net"
PROVISIONING_IDSCOPE = "<Device-Provisioning-Service-Scope-ID>"
PROVISIONING_REGISTRATION_ID = "<Device-Registration-ID>"
ADT_MODEL_ID = "dtmi:contosocom:DigitalTwins:Thermostat;1"
PROVISIONING_SYMMETRIC_KEY = "<Device-Provisioning-Service-enrollment-primary-SAS-key>"

儲存並關閉檔案。

開始執行裝置模擬器

繼續在命令視窗中的 device-simulator 目錄中,使用以下命令啟動裝置模擬器:

node .\adt_custom_register.js

您應該會看到正在註冊並連線到 IoT 中樞的裝置,然後開始傳送訊息。 Screenshot of the Command window showing device registration and sending messages.

Validate

您在本文中設定的流程,會讓系統在 Azure Digital Twins 中自動註冊裝置。 使用以下 Azure Digital Twins CLI 命令,在您建立的 Azure Digital Twins 執行個體中找到裝置的對應項。 執行個體主機名稱有預留位置 (您也可以使用執行個體的自訂名稱,但效能會稍微降低),裝置註冊識別碼也有預留位置。

az dt twin show --dt-name <instance-hostname-or-name> --twin-id "<device-registration-ID>"

您應該會在 Azure Digital Twins 執行個體中找到裝置的對應項。 Screenshot of the Command window showing newly created twin.

使用 IoT 中樞生命週期事件自動淘汰裝置

在本節中,您將透過以下路徑,將 IoT 中樞生命週期事件連結至 Azure Digital Twins,以自動淘汰裝置。 此圖表是先前所示完整架構的摘要。

Diagram of the Retire device flow—an excerpt of the solution architecture diagram, following data from a device deletion into Azure Digital Twins.

以下是程序流程的說明:

  1. 外部或手動程序會觸發刪除 IoT 中樞中的裝置。
  2. IoT 中樞會刪除裝置,並產生裝置生命週期事件,這會路由傳送至事件中樞
  3. Azure 函式會刪除 Azure Digital Twins 中裝置的對應項。

下列各節會逐步說明設定此自動淘汰裝置流程的步驟。

建立事件中樞

接下來,您會建立 Azure 事件中樞,接收 IoT 中樞生命週期事件。

按照建立事件中樞快速入門中所述的步驟操作。 將您的事件中樞命名為 lifecycleevents。 您在後續章節中設定 IoT 中樞路由和 Azure 函式時,會使用此事件中樞名稱。

下方螢幕擷取畫面說明如何建立事件中樞。 Screenshot of the Azure portal window showing how to create an event hub with the name lifecycleevents.

為您的事件中樞建立 SAS 原則

接下來,您必須建立共用存取簽章 (SAS) 原則,使用函式應用程式設定事件中樞。 若要建立 SAS 原則:

  1. 瀏覽至您在 Azure 入口網站中建立的事件中樞,然後在左側功能表選項中選取 [共用存取原則]
  2. 選取 [新增]。 在開啟的 [新增 SAS 原則] 視窗中,輸入您選擇的原則名稱,然後選取 [接聽] 核取方塊。
  3. 選取 建立

Screenshot of the Azure portal showing how to add an event hub SAS policy.

使用函式應用程式設定事件中樞

接下來,設定您在<必要條件>一節中設定的 Azure 函式應用程式,以使用新的事件中樞。 您會使用事件中樞的連接字串,設定函式應用程式內的環境變數,藉此設定函式。

  1. 開啟您建立的原則,並複製 [連接字串-主要金鑰] 值。

    Screenshot of the Azure portal showing how to copy the connection string-primary key.

  2. 使用以下 Azure CLI 命令,將連接字串新增為函式應用程式設定中的變數。 此命令可在 Cloud Shell 中執行,或者如果您在機器上安裝 Azure CLI,可在本機執行。

    az functionapp config appsettings set --settings "EVENTHUB_CONNECTIONSTRING=<Event-Hubs-SAS-connection-string-Listen>" --resource-group <resource-group> --name <your-function-app-name>
    

新增函式以使用 IoT 中樞生命週期事件淘汰

在您在<必要條件>一節中建立的函式應用程式專案內,您會建立新的函式,以使用 IoT 中樞生命週期事件淘汰現有裝置。

如需生命週期事件的詳細資訊,請參閱 IoT 中樞非遙測事件。 如需搭配 Azure 函式使用事件中樞的詳細資訊,請參閱適用於 Azure Functions 的 Azure 事件中樞觸發程序 (機器翻譯)。

瀏覽至您機器上的函式應用程式專案,並按照下列步驟操作。

  1. 首先,在函式應用程式專案中建立 [事件中樞觸發程序] 類型的新函式。

  2. 將新的 NuGet 套件新增至專案:Microsoft.Azure.Devices.Provisioning.Service。 如果程式碼中使用的套件尚未加入專案中,您可能也需要將更多套件新增至您的專案。

  3. 在新建立的函式程式碼檔案中,貼上下列程式碼,將函式命名為 DeleteDeviceInTwinFunc.cs,然後儲存檔案。

    // Copyright (c) Microsoft. All rights reserved.
    // Licensed under the MIT license. See LICENSE file in the project root for full license information.
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Azure;
    using Azure.Core.Pipeline;
    using Azure.DigitalTwins.Core;
    using Azure.Identity;
    using Microsoft.Azure.EventHubs;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Extensions.Logging;
    
    namespace Samples.AdtIothub
    {
        public static class DeleteDeviceInTwinFunc
        {
            private static string adtAppId = "https://digitaltwins.azure.net";
            private static readonly string adtInstanceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL", EnvironmentVariableTarget.Process);
            private static readonly HttpClient singletonHttpClientInstance = new HttpClient();
    
            [FunctionName("DeleteDeviceInTwinFunc")]
            public static async Task Run(
                [EventHubTrigger("lifecycleevents", Connection = "EVENTHUB_CONNECTIONSTRING")] EventData[] events, ILogger log)
            {
                var exceptions = new List<Exception>(events.Length);
    
                // Create Digital Twin client
                var cred = new ManagedIdentityCredential(adtAppId);
                var client = new DigitalTwinsClient(
                    new Uri(adtInstanceUrl),
                    cred,
                    new DigitalTwinsClientOptions
                    {
                        Transport = new HttpClientTransport(singletonHttpClientInstance)
                    });
    
                foreach (EventData eventData in events)
                {
                    try
                    {
                        //log.LogDebug($"EventData: {System.Text.Json.JsonSerializer.Serialize(eventData)}");
    
                        string opType = eventData.Properties["opType"] as string;
                        if (opType == "deleteDeviceIdentity")
                        {
                            string deviceId = eventData.Properties["deviceId"] as string;
    
                            try
                            {
                                // Find twin based on the original Registration ID
                                BasicDigitalTwin digitalTwin = await client.GetDigitalTwinAsync<BasicDigitalTwin>(deviceId);
    
                                // In order to delete the twin, all relationships must first be removed
                                await DeleteAllRelationshipsAsync(client, digitalTwin.Id, log);
    
                                // Delete the twin
                                await client.DeleteDigitalTwinAsync(digitalTwin.Id, digitalTwin.ETag);
                                log.LogInformation($"Twin {digitalTwin.Id} deleted in DT");
                            }
                            catch (RequestFailedException e) when (e.Status == (int)HttpStatusCode.NotFound)
                            {
                                log.LogWarning($"Twin {deviceId} not found in DT");
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        // We need to keep processing the rest of the batch - capture this exception and continue.
                        exceptions.Add(e);
                    }
                }
    
                if (exceptions.Count > 1)
                    throw new AggregateException(exceptions);
    
                if (exceptions.Count == 1)
                    throw exceptions.Single();
            }
    
            /// <summary>
            /// Deletes all outgoing and incoming relationships from a specified digital twin
            /// </summary>
            public static async Task DeleteAllRelationshipsAsync(DigitalTwinsClient client, string dtId, ILogger log)
            {
                AsyncPageable<BasicRelationship> relationships = client.GetRelationshipsAsync<BasicRelationship>(dtId);
                await foreach (BasicRelationship relationship in relationships)
                {
                    await client.DeleteRelationshipAsync(dtId, relationship.Id, relationship.ETag);
                    log.LogInformation($"Twin {dtId} relationship {relationship.Id} deleted in DT");
                }
    
                AsyncPageable<IncomingRelationship> incomingRelationships = client.GetIncomingRelationshipsAsync(dtId);
                await foreach (IncomingRelationship incomingRelationship in incomingRelationships)
                {
                    await client.DeleteRelationshipAsync(incomingRelationship.SourceId, incomingRelationship.RelationshipId);
                    log.LogInformation($"Twin {dtId} incoming relationship {incomingRelationship.RelationshipId} from {incomingRelationship.SourceId} deleted in DT");
                }
            }
        }
    }
    
  4. 使用 DeleteDeviceInTwinFunc.cs 函式,將專案發佈至 Azure 中的函式應用程式。

    如需如何使用 Visual Studio 來發佈函式的相關指示,請參閱使用 Visual Studio 開發 Azure Functions (機器翻譯)。 如需如何使用 Visual Studio Code 來發佈函式的相關指示,請參閱使用 Visual Studio Code 以在 Azure 中建立 C# 函式。 如需如何使用 Azure CLI 來發佈函式的相關指示,請參閱從命令列在 Azure 中建立 C# 函式 (機器翻譯)。

重要

第一次在<必要條件>一節中建立函式應用程式時,您可能已為函式指派存取角色,並為其設定應用程式設定,以存取您的 Azure Digital Twins 執行個體。 您必須針對整個函式應用程式進行一次上述步驟,因此請先確認已在您的應用程式中完成,再繼續進行。 您可以在「撰寫應用程式驗證碼」一文的<設定已發佈的應用程式>一節中找到指示。

為生命週期事件建立 IoT 中樞路由

您現在要設定 IoT 中樞路由,路由傳送裝置生命週期事件。 在此情況下,您將特別接聽以 if (opType == "deleteDeviceIdentity") 識別的裝置刪除事件。 此事件會觸發刪除數位對應項項目,完成裝置及其數位對應項的淘汰程序。

首先,您必須在 IoT 中樞內建立事件中樞端點。 您接著會在 IoT 中樞新增路由,將生命週期事件傳送至此事件中樞端點。 請按照下列步驟,建立事件中樞端點:

  1. Azure 入口網站中,瀏覽至您在<必要條件>一節中建立的 IoT 中樞,然後在左側功能表選項中選取 [訊息路由]

  2. 選取 [自訂端點] 索引標籤。

  3. 選取 [+ 新增],然後選擇 [事件中樞] 以新增事件中樞類型端點。

    Screenshot of the Azure portal showing how to add an Event Hubs custom endpoint.

  4. 在開啟的 [新增事件中樞端點] 視窗中,選擇下列值:

    • 端點名稱:選擇端點名稱。
    • 事件中樞命名空間:從下拉式清單中選取事件中樞命名空間。
    • 事件中樞執行個體:選擇您在上一步中建立的事件中樞名稱。
  5. 選取 建立。 讓此視窗保持開啟,以在下一步中新增路由。

    Screenshot of the Azure portal showing how to add an event hub endpoint.

接下來,您會新增路由,連線到您在上一步中建立的端點,以及傳送刪除事件的路由查詢。 按照下列步驟來建立路由:

  1. 瀏覽至 [路由] 索引標籤,然後選取 [新增] 以新增路由。

    Screenshot of the Azure portal showing how to add a route to send events.

  2. 在開啟的 [新增路由] 頁面中,選擇下列值:

    • 名稱:為路由選擇名稱。
    • 端點:從下拉式清單中選擇您先前建立的事件中樞端點。
    • 資料來源:選擇 [裝置生命週期事件]
    • 路由查詢:輸入 opType='deleteDeviceIdentity'。 此查詢會將裝置生命週期事件限制為只傳送刪除事件。
  3. 選取 [儲存]。

    Screenshot of the Azure portal showing how to add a route to send lifecycle events.

完成此流程之後,所有項目會設定為端對端淘汰裝置。

Validate

若要觸發淘汰程序,您必須手動從 IoT 中樞刪除裝置。

您可以使用 Azure CLI 命令或在 Azure 入口網站中,從 IoT 中樞手動刪除裝置。 按照下列步驟,在 Azure 入口網站中刪除裝置:

  1. 瀏覽至您的 IoT 中樞,然後在左側功能表選項中選擇 [IoT 裝置]
  2. 您會看到裝置的裝置註冊識別碼為您在本文前半部所選的識別碼。 您也可以選擇任何其他要刪除的裝置,只要它在 Azure Digital Twins 中有對應項,您就可以確認刪除裝置之後,會自動刪除對應項。
  3. 選取該裝置,然後選擇 [刪除]

Screenshot of the Azure portal showing how to delete device twin from the IoT devices.

可能需要幾分鐘的時間,才能看到 Azure Digital Twins 中反映變更。

使用以下 Azure Digital Twins CLI 命令,確認 Azure Digital Twins 執行個體中的裝置對應項已刪除。 執行個體主機名稱有預留位置 (您也可以使用執行個體的自訂名稱,但效能會稍微降低),裝置註冊識別碼也有預留位置。

az dt twin show --dt-name <instance-hostname-or-name> --twin-id "<device-registration-ID>"

您應該會看到無法在 Azure Digital Twins 執行個體中找到該裝置的對應項。

Screenshot of the Command window showing that the twin can't be found anymore.

清除資源

如果您不再需要本文中建立的資源,請按照下列步驟加以刪除。

您可以使用 Azure Cloud Shell 或本機 Azure CLI,以 az group delete 命令刪除資源群組中的所有 Azure 資源。 此命令會移除資源群組、Azure Digital Twins 執行個體、IoT 中樞和中樞裝置註冊、事件方格主題和相關聯的訂用帳戶、事件中樞命名空間,以及兩個 Azure Functions 應用程式,包括儲存體等相關聯的資源。

重要

刪除資源群組是無法回復的動作。 資源群組和其中包含的所有資源都將永久刪除。 請確定您不會不小心刪除錯誤的資源群組或資源。

az group delete --name <your-resource-group>

接著,刪除從本機電腦中下載的專案範例資料夾。

下一步

為裝置建立的數位對應項會儲存為 Azure Digital Twins 中的平面階層,但可以使用模型資訊和適用於組織的多層階層擴充。 若要深入了解此概念,請閱讀:

如需搭配 Azure 函式使用 HTTP 要求的詳細資訊,請參閱:

您可以使用已儲存在 Azure Digital Twins 中的模型和圖表資料,撰寫自訂邏輯來自動提供此資訊。 若要深入了解如何管理、升級和擷取來自對應項圖表的資訊,請參閱下列操作指南: