Compartilhar via


Dispositivos de gestão automática nos Gêmeos Digitais do Azure usando o DPS (Serviço de Provisionamento de Dispositivos no Hub IoT)

Neste artigo, você vai aprender a integrar os Gêmeos Digitais do Azure ao DPS (Serviço de Provisionamento de Dispositivos).

A solução descrita neste artigo permite que você automatize o processo para provisionar e desativar dispositivos do Hub IoT nos Gêmeos Digitais do Azure usando o Serviço de Provisionamento de Dispositivos.

Para obter mais informações sobre os estágios de provisionamento e desativação e para entender melhor o conjunto de estágios gerais de gerenciamento de dispositivos comuns a todos os projetos de IoT corporativos, consulte a seção Ciclo de vida do dispositivo da documentação de gerenciamento de dispositivos do Hub IoT.

Pré-requisitos

Antes de configurar o provisionamento, você precisará configurar os seguintes recursos:

Este exemplo também usa um simulador de dispositivo que inclui o provisionamento usando o Serviço de Provisionamento de Dispositivos. O simulador de dispositivo está localizado aqui em Exemplo de Integração de Hub IoT e Gêmeos Digitais do Azure. Obtenha o projeto de exemplo em seu computador navegando até o repositório GitHub para o exemplo, que você pode baixar como um arquivo .zip selecionando o botão Código e Baixar ZIP.

Uma captura de tela do repositório digital-twins-iothub-integration no GitHub, realçando as etapas usadas para baixar o exemplo como um arquivo zip.

Descompacte a pasta baixada.

Você vai precisar ter o Node.js instalado em seu computador. O simulador de dispositivo se baseia no Node.js, versão 10.0.x ou posterior.

Arquitetura da solução

Esta solução inclui etapas usadas para provisionar e desativar um dispositivo nos Gêmeos Digitais do Azure com o Serviço de Provisionamento de Dispositivos.

Os dados fluem entre um dispositivo de termostato e um DPS para alocar dispositivos na solução. Depois os dados fluem do DPS para o Hub IoT e para os Gêmeos Digitais do Azure por meio de uma função do Azure.

Para desativar um dispositivo, os dados de uma exclusão manual do dispositivo fluem para os Gêmeos Digitais do Azure por meio do Hub IoT, dos Hubs de Eventos e de uma função do Azure.

A imagem abaixo ilustrará essa arquitetura.

Um diagrama do dispositivo e de vários serviços do Azure em um cenário de ponta a ponta mostrando o fluxo de dados.

Este artigo será dividido em duas seções e cada uma delas vai focar em uma parte da arquitetura completa:

Provisionamento automático de dispositivo usando o Serviço de Provisionamento de Dispositivos

Nesta seção, você vai anexar o Serviço de Provisionamento de Dispositivos aos Gêmeos Digitais do Azure para provisionar dispositivos automaticamente usando o caminho abaixo. Este diagrama é um trecho da arquitetura completa exibida anteriormente.

Diagrama do fluxo de Provisionamento: um trecho do diagrama da arquitetura da solução seguindo os dados de um termostato para os Gêmeos Digitais do Azure.

Aqui está uma descrição do fluxo do processo:

  1. O dispositivo entra em contato com o ponto de extremidade do DPS, transmitindo informações de identificação para provar sua identidade.
  2. O DPS valida a identidade do dispositivo validando a ID e a chave de registro em relação à lista de registro e chama uma função do Azure para fazer a alocação.
  3. A função do Azure cria um gêmeo nos Gêmeos Digitais do Azure para o dispositivo. O gêmeo terá o mesmo nome que a ID de registro do dispositivo.
  4. O DPS registra o dispositivo com um Hub IoT e popula o status escolhido do gêmeo do dispositivo.
  5. O Hub IoT retorna informações da ID do dispositivo e as informações de conexão do Hub IoT para o dispositivo. O dispositivo pode se conectar ao Hub IoT.

As seções a seguir percorrem as etapas para configurar esse fluxo de provisionamento automático de dispositivo.

Criar um Serviço de Provisionamento de Dispositivos

Quando um novo dispositivo é provisionado usando o Serviço de Provisionamento de Dispositivos, pode ser criado um gêmeo para esse dispositivo nos Gêmeos Digitais do Azure com o mesmo nome que a ID de registro.

Crie uma instância do Serviço de Provisionamento de Dispositivos, que será usada para provisionar dispositivos IoT. Você pode usar as instruções da CLI do Azure abaixo ou usar o portal do Azure seguindo as instruções em Configurar o Serviço de Provisionamento de Dispositivos no Hub IoT com o portal do Azure.

O comando a seguir da CLI do Azure vai criar um Serviço de Provisionamento de Dispositivos. Você vai precisar especificar um nome do Serviço de Provisionamento de Dispositivos, um grupo de recursos e uma região. Para ver quais regiões dão suporte ao Serviço de Provisionamento de Dispositivos, visite Produtos do Azure disponíveis por região. É possível executar o comando no Cloud Shell ou localmente caso tenha a CLI do Azure instalada em seu computador.

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

Adicionar uma função a ser usada com o Serviço de Provisionamento de Dispositivos

Dentro do projeto do aplicativo de funções criado na Seção de pré-requisitos, você vai criar uma função para ser usada com o Serviço de Provisionamento de Dispositivos. Essa função será usada pelo Serviço de Provisionamento de Dispositivos em uma Política de Alocação Personalizada para provisionar um novo dispositivo.

Navegue para o projeto de aplicativo de funções em seu computador e siga as etapas abaixo.

  1. Primeiro, crie uma função do tipo Gatilho de HTTP no projeto do aplicativo de funções.

  2. Adicione um novo pacote NuGet ao projeto: Microsoft.Azure.Devices.Provisioning.Service. Talvez seja necessário adicionar mais pacotes ao projeto, caso os pacotes usados no código ainda não façam parte do projeto.

  3. No arquivo de código de função recém-criado, cole o código a seguir, nomeie a função como DpsAdtAllocationFunc.cs e salve o arquivo.

    // 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. Publique o projeto com a função DpsAdtAllocationFunc.cs para o aplicativo de funções no Azure.

    Para obter instruções sobre como publicar uma função usando o Visual Studio, confira Desenvolver Azure Functions com o Visual Studio. Para obter instruções sobre como publicar a função usando o Visual Studio Code, confira Criar uma função C# no Azure usando o Visual Studio Code. Para obter instruções sobre como publicar a função usando a CLI do Azure, confira Criar uma função C# no Azure da linha de comando.

Importante

Durante a criação do aplicativo de funções na seção Pré-requisitos, talvez você já tenha atribuído uma função de acesso para a função e definido as configurações do aplicativo para que ele acesse a instância dos Gêmeos Digitais do Azure. Isso precisa ser feito uma vez para o aplicativo de funções todo, portanto, verifique se eles foram concluídos em seu aplicativo antes de continuar. Você encontrará instruções na seção Configurar aplicativo publicado no artigo Gravar código de autenticação do aplicativo.

Criar registro de Provisionamento de Dispositivos

Em seguida, você vai precisar criar um registro no Serviço de Provisionamento de Dispositivos usando uma função de alocação personalizada. Para criar um registro, siga as instruções na seção Criar o registro do artigo de políticas de alocação personalizadas da documentação do Serviço de Provisionamento de Dispositivos.

Ao passar pelo fluxo, certifique-se de selecionar as opções a seguir para vincular o registro à função que você acabou de criar.

  • Selecione como você deseja atribuir dispositivos aos hubs: Personalizado (Usar a função do Azure).
  • Selecione os hubs IoT aos quais esse grupo pode ser atribuído: escolha o nome do hub IoT ou selecione o botão Vincular um novo hub IoT e escolha o hub IoT nas opções.

Em seguida, escolha o botão Selecionar uma nova função para vincular o aplicativo de funções ao grupo de registros. Preencha os seguintes valores:

  • Assinatura: a assinatura do Azure é preenchida automaticamente. Certifique-se de que é a assinatura correta.
  • Aplicativo de Funções: escolha o nome do aplicativo de funções.
  • Função: escolha DpsAdtAllocationFunc.

Salve os detalhes.

Uma captura de tela da janela Detalhes do grupo de registro da Contoso no portal do Azure.

Depois de criar a inscrição, selecione-a para exibir suas configurações. Copie a Chave Primária para o registro, que será usada posteriormente neste artigo para configurar o simulador de dispositivo.

Configurar o simulador do dispositivo

Este exemplo usa um simulador de dispositivo que inclui o provisionamento usando o Serviço de Provisionamento de Dispositivos. O simulador de dispositivo está localizado na Integração de exemplo dos Gêmeos Digitais do Azure e o Hub IoT baixado na Seção de pré-requisitos.

Fazer upload do modelo

O simulador de dispositivo é um dispositivo de tipo termostato que usa o modelo com a ID: dtmi:contosocom:DigitalTwins:Thermostat;1. Carregue esse modelo nos Gêmeos Digitais do Azure para criar um gêmeo desse tipo para o dispositivo.

O modelo se parece com este:

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

Para carregar esse modelo na instância de gêmeos, execute o seguinte comando CLI do Azure, que carrega o modelo acima como JSON embutido. Você pode executar o comando no Azure Cloud Shell em seu navegador (use o ambiente Bash) ou em seu computador se tiver a CLI instalada localmente. Há um espaço reservado para o nome do host da instância (você também pode usar o nome amigável da instância com uma pequena redução no desempenho).

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"    }  ]}' 

Observação

Se você estiver usando algo diferente de Cloud Shell no ambiente do Bash, talvez seja necessário fazer escape de determinados caracteres no JSON em linha para que ele seja analisado corretamente. Para obter mais informações, consulte Usar caracteres especiais em diferentes shells.

Para obter mais informações sobre modelos, consulte Gerenciar modelos.

Configurar e executar o simulador

Em uma janela de comando em seu computador local, navegue até o exemplo baixado de Gêmeos Digitais do Azure e Integração do Hub IoT que você descompactou anteriormente e, em seguida, para o diretório do simulador de dispositivo. Em seguida, instale as dependências para o projeto usando o seguinte comando:

npm install

No diretório do simulador do dispositivo, copie o arquivo .env.template para um novo arquivo chamado .env e obtenha os seguintes valores para preencher nas configurações:

  • PROVISIONING_IDSCOPE: para obter esse valor, navegue até o serviço de provisionamento de dispositivos no portal do Azure, selecione Visão Geral nas opções de menu e procure o campo Escopo de ID.

    Uma captura de tela da exibição do portal do Azure mostrando a página de visão geral do provisionamento de dispositivos realçando o valor do Escopo da ID.

  • PROVISIONING_REGISTRATION_ID: você pode escolher uma ID de registro para o dispositivo.

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

  • PROVISIONING_SYMMETRIC_KEY: essa variável de ambiente é a chave primária para o registro que você configurou anteriormente. Para obter esse valor novamente, navegue até o serviço de provisionamento de dispositivos no portal do Azure, selecione Gerenciar Registros, selecione o grupo de registros que você criou anteriormente e copie a Chave Primária.

    Uma captura de tela da exibição do portal do Azure mostrando a página de registros de gerenciamento de serviços de provisionamento de dispositivos realçando o valor da chave primária de SAS.

Use os valores acima para atualizar as configurações de arquivo .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>"

Salve e feche o arquivo.

Iniciar a execução do simulador de dispositivo

Ainda no diretório device-simulator na janela Comando, inicie o simulador de dispositivo usando o seguinte comando:

node .\adt_custom_register.js

Você deve ver o dispositivo ser registrado, conectar-se ao Hub IoT e começar a enviar mensagens. Uma captura de tela da janela Comando mostrando o registro do dispositivo e o envio de mensagens.

Validar

Como resultado do fluxo que você configurou neste artigo, o dispositivo será registrado automaticamente nos Gêmeos Digitais do Azure. Use o comando a seguir da CLI dos Gêmeos Digitais do Azure para localizar o gêmeo do dispositivo na instância dos Gêmeos Digitais do Azure que você criou. Há um espaço reservado para o nome do host da instância (você também pode usar o nome amigável da instância com uma pequena redução no desempenho) e um espaço reservado para a ID de registro do dispositivo.

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

Você deve ver o gêmeo do dispositivo ser encontrado na instância do Gêmeos Digitais do Azure. Captura de tela da janela Comando mostrando o gêmeo recém-criado.

Desativação automática de dispositivo usando eventos do ciclo de vida do Hub IoT

Nesta seção, você vai anexar eventos de ciclo de vida do Hub IoT aos Gêmeos Digitais do Azure para desativar automaticamente os dispositivos por meio do caminho abaixo. Este diagrama é um trecho da arquitetura completa exibida anteriormente.

Diagrama do fluxo de Desativação do dispositivo: um trecho do diagrama da arquitetura da solução seguindo os dados de uma exclusão de dispositivo para os Gêmeos Digitais do Azure.

Aqui está uma descrição do fluxo do processo:

  1. Um processo externo ou manual dispara a exclusão de um dispositivo no Hub IoT.
  2. O Hub IoT exclui o dispositivo e gera um evento de ciclo de vida do dispositivo que será roteado para um hub de eventos.
  3. Uma função do Azure exclui o gêmeo do dispositivo nos Gêmeos Digitais do Azure.

As seções a seguir percorrem as etapas para configurar esse fluxo de desativação automática de dispositivo.

Criar um hub de eventos

Você vai criar um Hub de eventos do Azure para receber eventos do ciclo de vida do Hub IoT.

Siga as etapas descritas na guia de início rápido Criar um hub de eventos. Nomeie o lifecycleevents do hub de eventos. Você usará esse nome de hub de eventos quando configurar a rota do Hub IoT e uma função do Azure nas próximas seções.

A captura de tela abaixo ilustra a criação do hub de eventos. Uma captura de tela da janela do portal do Azure mostrando de que modo criar um hub de eventos com o nome lifecycleevents.

Criar uma política de SAS para o hub de eventos

Em seguida, você vai precisar criar uma política de SAS (assinatura de acesso compartilhado) para configurar o hub de eventos com seu aplicativo de funções. Para criar a política de SAS:

  1. Navegue até o hub de eventos que você criou no portal do Azure e selecione Políticas de acesso compartilhado nas opções de menu à esquerda.
  2. Selecione Adicionar. Na janela Adicionar Política de SAS que vai abrir, insira um nome de política de sua escolha e marque a caixa de seleção Escutar.
  3. Selecione Criar.

Uma captura de tela do portal do Azure mostrando de que modo adicionar uma política de SAS do hub de eventos.

Configurar o hub de eventos com o aplicativo de funções

Depois configure o aplicativo de funções do Azure definido na Seção de pré-requisitos para funcionar com o novo hub de eventos. Faça isso definindo uma variável de ambiente dentro do aplicativo de funções com a cadeia de conexão do hub de eventos.

  1. Abra a política que você criou e copie o valor da Chave primária da cadeia de conexão.

    Uma captura de tela do portal do Azure mostrando de que modo copiar a chave primária da cadeia de conexão.

  2. Adicione a cadeia de conexão como uma variável nas configurações do aplicativo de funções com o comando da CLI do Azure a seguir. É possível executar o comando no Cloud Shell ou localmente caso tenha a CLI do Azure instalada em seu computador.

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

Adicionar uma função a ser desativada com eventos do ciclo de vida do Hub IoT

Dentro do projeto de aplicativo de funções criado na Seção de pré-requisitos, você vai criar uma função para desativar um dispositivo existente usando eventos do ciclo de vida do Hub IoT.

Para obter mais informações sobre eventos do ciclo de vida, consulte Eventos de não telemetria do Hub IoT. Para obter mais informações sobre como usar Hubs de eventos com as funções do Azure, consulte Gatilho de Hubs de Eventos do Azure para o Azure Functions.

Navegue para o projeto de aplicativo de funções em seu computador e siga as etapas abaixo.

  1. Primeiro, crie uma função do tipo Gatilho de Hub de Eventos no projeto do aplicativo de funções.

  2. Adicione um novo pacote NuGet ao projeto: Microsoft.Azure.Devices.Provisioning.Service. Talvez seja necessário adicionar mais pacotes ao projeto, caso os pacotes usados no código ainda não façam parte do projeto.

  3. No arquivo de código de função recém-criado, cole o código a seguir, nomeie a função como DeleteDeviceInTwinFunc.cs e salve o arquivo.

    // 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. Publique o projeto com a função DeleteDeviceInTwinFunc.cs para o aplicativo de funções no Azure.

    Para obter instruções sobre como publicar uma função usando o Visual Studio, confira Desenvolver Azure Functions com o Visual Studio. Para obter instruções sobre como publicar a função usando o Visual Studio Code, confira Criar uma função C# no Azure usando o Visual Studio Code. Para obter instruções sobre como publicar a função usando a CLI do Azure, confira Criar uma função C# no Azure da linha de comando.

Importante

Durante a criação do aplicativo de funções na seção Pré-requisitos, talvez você já tenha atribuído uma função de acesso para a função e definido as configurações do aplicativo para que ele acesse a instância dos Gêmeos Digitais do Azure. Isso precisa ser feito uma vez para o aplicativo de funções todo, portanto, verifique se eles foram concluídos em seu aplicativo antes de continuar. Você encontrará instruções na seção Configurar aplicativo publicado no artigo Gravar código de autenticação do aplicativo.

Criar uma rota do Hub IoT para eventos de ciclo de vida

Configure uma rota do Hub IoT para rotear eventos de ciclo de vida do dispositivo. Nesse caso, você vai escutar especificamente os eventos de exclusão de dispositivos, identificados por if (opType == "deleteDeviceIdentity"). Esse evento vai disparar a exclusão do item de gêmeo digital, finalizando a desativação de um dispositivo e do seu gêmeo digital.

Primeiro, crie um ponto de extremidade do hub de eventos no hub IoT. Em seguida, adicione uma rota no Hub IoT para enviar eventos de ciclo de vida para esse ponto de extremidade do hub de eventos. Siga estas etapas para criar um ponto de extremidade do hub de eventos:

  1. No portal do Azure, acesse o Hub IoT criado na Seção de pré-requisitos e selecione Roteamento de mensagens nas opções de menu à esquerda.

  2. Selecione a guia Pontos de extremidade personalizados.

  3. Selecione + Adicionar e escolha Hubs de Eventos para adicionar um ponto de extremidade de tipo Hubs de Eventos.

    Captura de tela do portal do Azure mostrando como adicionar um ponto de extremidade personalizado dos Hubs de Eventos.

  4. Na janela Adicionar um ponto de extremidade do hub de eventos que vai abrir, escolha os seguintes valores:

    • Nome do ponto de extremidade: escolha um nome de ponto de extremidade.
    • Namespace do hub de eventos: selecione o namespace do hub de eventos na lista suspensa.
    • Instância do hub de eventos: escolha o nome do hub de eventos que você criou na etapa anterior.
  5. Selecione Criar. Mantenha essa janela aberta para adicionar uma rota na próxima etapa.

    Captura de tela do portal do Azure mostrando como adicionar um ponto de extremidade do hub de eventos.

Em seguida, adicione uma rota que se conecta ao ponto de extremidade criado na etapa acima, com uma consulta de roteamento que envia os eventos de exclusão. Siga estas etapas para criar uma rota:

  1. Navegue até a guia Rotas e selecione Adicionar para adicionar uma rota.

    Captura de tela do portal do Azure mostrando de que modo adicionar uma rota para enviar eventos.

  2. Na página Adicionar uma rota que vai abrir, escolha os seguintes valores:

    • Nome: Escolha um nome para a rota.
    • Ponto de extremidade: escolha o ponto de extremidade dos Hubs de Eventos que você criou anteriormente na lista suspensa.
    • Fonte de dados: escolha Eventos de ciclo de vida do dispositivo.
    • Consulta de roteamento: insira opType='deleteDeviceIdentity'. Essa consulta limita os eventos de ciclo de vida do dispositivo para enviar apenas os eventos de exclusão.
  3. Selecione Salvar.

    Captura de tela do portal do Azure mostrando de que modo adicionar uma rota para enviar eventos de ciclo de vida.

Depois de passar por esse fluxo, estará tudo pronto para desativar dispositivos de ponta a ponta.

Validar

Para disparar o processo de desativação, você precisa excluir manualmente o dispositivo do Hub IoT.

Você pode excluir o dispositivo manualmente do hub IoT com um comando da CLI do Azure ou no portal do Azure. Siga as etapas abaixo para excluir o dispositivo no portal do Azure:

  1. Navegue até o Hub IoT e escolha Dispositivos IoT nas opções do menu à esquerda.
  2. Você vai ver um dispositivo com a ID de registro do dispositivo escolhida na primeira metade deste artigo. Como alternativa, você pode escolher qualquer outro dispositivo a ser excluído, desde que ele tenha um gêmeo nos Gêmeos Digitais do Azure, para que você possa verificar se o gêmeo também é excluído automaticamente depois da exclusão do dispositivo.
  3. Selecione o dispositivo e escolha Excluir.

Uma captura de tela do portal do Azure mostrando de que modo excluir um dispositivo gêmeo dos dispositivos de IoT.

Pode levar alguns minutos para ver as alterações refletidas nos Gêmeos Digitais do Azure.

Use o comando a seguir da CLI dos Gêmeos Digitais do Azure para verificar se o gêmeo do dispositivo na instância dos Gêmeos Digitais do Azure foi excluído. Há um espaço reservado para o nome do host da instância (você também pode usar o nome amigável da instância com uma pequena redução no desempenho) e um espaço reservado para a ID de registro do dispositivo.

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

Você verá que o gêmeo do dispositivo não pode mais ser encontrado na instância dos Gêmeos Digitais do Azure.

Uma captura de tela da janela Comando mostrando que o gêmeo não pode mais ser localizado.

Limpar os recursos

Se você não precisar mais dos recursos criados nesse artigo, siga estas etapas para excluí-los.

Usando o Azure Cloud Shell ou a CLI do Azure local, você pode excluir todos os recursos do Azure em um grupo de recursos com o comando az group delete. Esse comando remove o grupo de recursos, a instância dos Gêmeos Digitais do Azure, o hub IoT e o registro do dispositivo de hub, o tópico da Grade de Eventos e as assinaturas associadas, o namespace dos Hubs de Eventos e os dois aplicativos do Azure Functions, incluindo os recursos associados, como o armazenamento.

Importante

A exclusão de um grupo de recursos é irreversível. O grupo de recursos e todos os recursos contidos nele são excluídos permanentemente. Não exclua acidentalmente o grupo de recursos ou os recursos incorretos.

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

Exclua a pasta de exemplo do projeto que você baixou do computador local.

Próximas etapas

Os gêmeos digitais criados para os dispositivos são armazenados como uma hierarquia simples nos Gêmeos Digitais do Azure, mas eles podem ser enriquecidos com informações de modelo e uma hierarquia de vários níveis para a organização. Para saber mais sobre esse conceito, leia:

Para obter mais informações sobre como usar solicitações HTTP com as funções do Azure, consulte:

Você pode escrever uma lógica personalizada para fornecer automaticamente essas informações usando os dados do modelo e do grafo já armazenados nos Gêmeos Digitais do Azure. Para ler mais sobre como gerenciar, atualizar e recuperar informações do grafo dos gêmeos, confira os seguintes guias de instruções: