Share via


Gestione automatica dei dispositivi in Gemelli digitali di Azure con il servizio Device Provisioning

In questo articolo si apprenderà come integrare Gemelli digitali di Azure con il servizio Device Provisioning.In this article, you'll learn how to integrate Azure Digital Twins with Device Provisioning Service (DPS).

La soluzione descritta in questo articolo consentirà di automatizzare il processo di provisioning e ritiro dei dispositivi hub IoT in Gemelli digitali di Azure usando il servizio Device Provisioning.

Per altre informazioni sulle fasi di provisioning e ritiro e per comprendere meglio il set di fasi generali di gestione dei dispositivi comuni a tutti i progetti IoT aziendali, vedere la sezione Ciclo di vita del dispositivo della documentazione sulla gestione dei dispositivi di hub IoT.

Prerequisiti

Prima di poter configurare il provisioning, è necessario configurare le risorse seguenti:

  • Istanza di Gemelli digitali di Azure. Seguire le istruzioni in Configurare un'istanza e l'autenticazione per creare un'istanza di Gemelli digitali di Azure. Raccogliere il nome host dell'istanza nel portale di Azure (istruzioni).
  • Un hub IoT. Per istruzioni, vedere la sezione "Creare un hub IoT" della guida introduttiva hub IoT.
  • Funzione di Azure che aggiorna le informazioni del gemello digitale in base ai dati di hub IoT. Seguire le istruzioni in Inserire i dati dell'hub IoT per creare questa funzione di Azure. Raccogliere il nome della funzione da usare in questo articolo.

Questo esempio usa anche un simulatore di dispositivi che include il provisioning usando il servizio Device Provisioning. Il simulatore di dispositivi si trova qui: Gemelli digitali di Azure e hub IoT esempio di integrazione. Ottenere il progetto di esempio nel computer passando al repository GitHub per l'esempio, che è possibile scaricare come file ZIP selezionando il pulsante Codice e Scarica ZIP.

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

Decomprimere la cartella scaricata.

Nel computer sarà necessario installare Node.js . Il simulatore di dispositivi si basa su Node.js, versione 10.0.x o successiva.

Architettura della soluzione

Questa soluzione include i passaggi per il provisioning e il ritiro di un dispositivo in Gemelli digitali di Azure usando il servizio Device Provisioning.

Per allocare i dispositivi nella soluzione, i flussi di dati tra un dispositivo termostato e dps. I dati passano quindi da DPS a hub IoT e verso Gemelli digitali di Azure tramite una funzione di Azure.

Per ritirare un dispositivo, i dati di un'eliminazione manuale del dispositivo passano a Gemelli digitali di Azure tramite hub IoT, Hub eventi e una funzione di Azure.

L'immagine seguente illustra questa architettura.

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

Questo articolo è suddiviso in due sezioni, ognuna incentrata su una parte di questa architettura completa:

Provisioning automatico del dispositivo con il servizio Device Provisioning

In questa sezione si connetterà il servizio Device Provisioning a Gemelli digitali di Azure ai dispositivi di provisioning automatico tramite il percorso seguente. Questo diagramma è un estratto dell'architettura completa illustrata in precedenza.

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

Ecco una descrizione del flusso di processo:

  1. Il dispositivo contatta l'endpoint DPS, passando le informazioni di identificazione per dimostrare la propria identità.
  2. Dps convalida l'identità del dispositivo convalidando l'ID e la chiave di registrazione nell'elenco di registrazioni e chiama una funzione di Azure per eseguire l'allocazione.
  3. La funzione di Azure crea un nuovo gemello in Gemelli digitali di Azure per il dispositivo. Il gemello avrà lo stesso nome dell'ID di registrazione del dispositivo.
  4. Dps registra il dispositivo con un hub IoT e popola lo stato scelto del dispositivo gemello.
  5. L'hub IoT restituisce le informazioni sull'ID dispositivo e le informazioni di connessione dell'hub IoT al dispositivo. Il dispositivo può ora connettersi all'hub IoT.

Le sezioni seguenti illustrano i passaggi per configurare questo flusso di dispositivo di provisioning automatico.

Creare un servizio Device Provisioning

Quando viene effettuato il provisioning di un nuovo dispositivo usando il servizio Device Provisioning, è possibile creare un nuovo dispositivo gemello in Gemelli digitali di Azure con lo stesso nome dell'ID di registrazione.

Creare un'istanza del servizio Device Provisioning, che verrà usata per effettuare il provisioning dei dispositivi IoT. È possibile usare le istruzioni dell'interfaccia della riga di comando di Azure riportate di seguito oppure usare il portale di Azure seguendo Configurare il servizio Device Provisioning hub IoT con il portale di Azure.

Il comando seguente dell'interfaccia della riga di comando di Azure creerà un servizio Device Provisioning. È necessario specificare un nome, un gruppo di risorse e un'area del servizio Device Provisioning. Per informazioni sulle aree che supportano il servizio Device Provisioning, vedere Prodotti di Azure disponibili in base all'area. Il comando può essere eseguito in Cloud Shell o in locale se nel computer è installata l'interfaccia della riga di comando di Azure.

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

Aggiungere una funzione da usare con il servizio Device Provisioning

All'interno del progetto di app per le funzioni creato nella sezione Prerequisiti si creerà una nuova funzione da usare con il servizio Device Provisioning. Questa funzione verrà usata dal servizio Device Provisioning in un criterio di allocazione personalizzato per effettuare il provisioning di un nuovo dispositivo.

Passare al progetto dell'app per le funzioni nel computer e seguire questa procedura.

  1. Creare prima di tutto una nuova funzione di tipo HTTP-trigger nel progetto dell'app per le funzioni.

  2. Aggiungere un nuovo pacchetto NuGet al progetto: Microsoft.Azure.Devices.Provisioning.Service. Potrebbe essere necessario aggiungere anche altri pacchetti al progetto, se i pacchetti usati nel codice non fanno già parte del progetto.

  3. Nel file di codice della funzione appena creato incollare il codice seguente, denominare la funzione DpsAdtAllocationFunc.cs e salvare il file.

    // 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. Pubblicare il progetto con la funzione DpsAdtAllocationFunc.cs in un'app per le funzioni in Azure.

    Per istruzioni su come pubblicare la funzione con Visual Studio, vedere Sviluppare Funzioni di Azure con Visual Studio. Per istruzioni su come pubblicare la funzione con Visual Studio Code, vedere Creare una funzione C# in Azure con Visual Studio Code. Per istruzioni su come pubblicare la funzione usando l'interfaccia della riga di comando di Azure, vedere Creare una funzione C# in Azure dalla riga di comando.

Importante

Quando si crea l'app per le funzioni per la prima volta nella sezione Prerequisiti, potrebbe essere già stato assegnato un ruolo di accesso per la funzione e sono state configurate le impostazioni dell'applicazione per accedere all'istanza di Gemelli digitali di Azure. Queste operazioni devono essere eseguite una sola volta per l'intera app per le funzioni, quindi verificare che siano state completate nell'app prima di continuare. Le istruzioni sono disponibili nella sezione Configurare l'app pubblicata dell'articolo Scrivere il codice di autenticazione dell'app.

Creare la registrazione device provisioning

Sarà quindi necessario creare una registrazione nel servizio Device Provisioning usando una funzione di allocazione personalizzata. Per creare una registrazione, seguire le istruzioni nella sezione Creare la registrazione dell'articolo Criteri di allocazione personalizzati nella documentazione del servizio Device Provisioning.

Durante l'esecuzione del flusso, assicurarsi di selezionare le opzioni seguenti per collegare la registrazione alla funzione creata.

  • Selezionare la modalità di assegnazione dei dispositivi agli hub: personalizzata (usare la funzione di Azure).
  • Selezionare gli hub IoT a cui è possibile assegnare questo gruppo: scegliere il nome dell'hub IoT o selezionare il pulsante Collega un nuovo hub IoT e scegliere l'hub IoT dalle opzioni.

Scegliere quindi il pulsante Selezionare una nuova funzione per collegare l'app per le funzioni al gruppo di registrazione. Compilare quindi i valori seguenti:

  • Sottoscrizione: la sottoscrizione di Azure viene popolata automaticamente. Assicurarsi che sia la sottoscrizione corretta.
  • App per le funzioni: scegliere il nome dell'app per le funzioni.
  • Funzione: scegliere DpsAdtAllocationFunc.

Salvare i dettagli.

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

Dopo aver creato la registrazione, selezionarla per visualizzarne le impostazioni. Copiare la chiave primaria per la registrazione, che verrà usata più avanti in questo articolo per configurare il simulatore di dispositivi.

Configurare il simulatore di dispositivi

Questo esempio usa un simulatore di dispositivi che include il provisioning usando il servizio Device Provisioning. Il simulatore di dispositivi si trova in Gemelli digitali di Azure e nell'esempio di integrazione hub IoT scaricato nella sezione Prerequisiti.

Caricare il modello

Il simulatore di dispositivi è un dispositivo di tipo termostato che usa il modello con questo ID: dtmi:contosocom:DigitalTwins:Thermostat;1. È necessario caricare questo modello in Gemelli digitali di Azure prima di poter creare un gemello di questo tipo per il dispositivo.

Il modello ha un aspetto simile al seguente:

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

Per caricare questo modello nell'istanza di gemelli, eseguire il comando seguente dell'interfaccia della riga di comando di Azure, che carica il modello precedente come JSON inline. È possibile eseguire il comando in Azure Cloud Shell nel browser (usare l'ambiente Bash) o nel computer se l'interfaccia della riga di comando è installata in locale. Esiste un segnaposto per il nome host dell'istanza(è anche possibile usare il nome descrittivo dell'istanza con un lieve calo delle prestazioni).

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

Nota

Se si usa qualcosa di diverso da Cloud Shell nell'ambiente Bash, potrebbe essere necessario eseguire l'escape di determinati caratteri nel codice JSON inline in modo che venga analizzato correttamente. Per altre informazioni, vedere Usare caratteri speciali in shell diverse.

Per altre informazioni sui modelli, vedere Gestire i modelli.

Configurare ed eseguire il simulatore

In una finestra di comando del computer locale passare all'esempio scaricato di Gemelli digitali di Azure e hub IoT Integrazione decompressa in precedenza e quindi nella directory device-simulator. Installare quindi le dipendenze per il progetto usando il comando seguente:

npm install

Quindi, nella directory del simulatore di dispositivi copiare il file con estensione env.template in un nuovo file denominato .env e raccogliere i valori seguenti per compilare le impostazioni:

  • PROVISIONING_IDSCOPE: per ottenere questo valore, passare al servizio device provisioning nel portale di Azure, quindi selezionare Panoramica nelle opzioni di menu e cercare l'ambito ID campo.

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

  • PROVISIONING_REGISTRATION_ID: è possibile scegliere un ID di registrazione per il dispositivo.

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

  • PROVISIONING_SYMMETRIC_KEY: questa variabile di ambiente è la chiave primaria per la registrazione configurata in precedenza. Per ottenere di nuovo questo valore, passare al servizio device provisioning nel portale di Azure, selezionare Gestisci registrazioni, quindi selezionare il gruppo di registrazione creato in precedenza e copiare la chiave primaria.

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

Usare ora i valori precedenti per aggiornare le impostazioni del file con estensione 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>"

Salva e chiudi il file.

Avviare l'esecuzione del simulatore di dispositivi

Sempre nella directory device-simulator nella finestra di comando avviare il simulatore di dispositivi usando il comando seguente:

node .\adt_custom_register.js

Dovrebbe essere visualizzato il dispositivo registrato e connesso a hub IoT e quindi iniziare a inviare messaggi. Screenshot of the Command window showing device registration and sending messages.

Convalida

Il flusso configurato in questo articolo comporterà la registrazione automatica del dispositivo in Gemelli digitali di Azure. Usare il comando seguente dell'interfaccia della riga di comando di Gemelli digitali di Azure per trovare il gemello del dispositivo nell'istanza di Gemelli digitali di Azure creata. Esiste un segnaposto per il nome host dell'istanza (è anche possibile usare il nome descrittivo dell'istanza con un lieve calo delle prestazioni) e un segnaposto per l'ID registrazione del dispositivo.

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

Verrà visualizzato il gemello del dispositivo trovato nell'istanza di Gemelli digitali di Azure. Screenshot of the Command window showing newly created twin.

Autoretire il dispositivo usando gli eventi del ciclo di vita di hub IoT

In questa sezione si allegheranno hub IoT eventi del ciclo di vita a Gemelli digitali di Azure ai dispositivi di autoretire tramite il percorso seguente. Questo diagramma è un estratto dell'architettura completa illustrata in precedenza.

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

Ecco una descrizione del flusso di processo:

  1. Un processo esterno o manuale attiva l'eliminazione di un dispositivo in hub IoT.
  2. hub IoT elimina il dispositivo e genera un evento del ciclo di vita del dispositivo che verrà indirizzato a un hub eventi.
  3. Una funzione di Azure elimina il gemello del dispositivo in Gemelli digitali di Azure.

Le sezioni seguenti illustrano i passaggi per configurare questo flusso di dispositivo autoretire.

Creare un hub eventi

Si creerà quindi un hub eventi di Azure per ricevere hub IoT eventi del ciclo di vita.

Seguire i passaggi descritti nella guida introduttiva Creare un hub eventi. Assegnare un nome agli eventi del ciclo di vita dell'hub eventi. Questo nome dell'hub eventi verrà usato quando si configura hub IoT route e una funzione di Azure nelle sezioni successive.

Lo screenshot seguente illustra la creazione dell'hub eventi. Screenshot of the Azure portal window showing how to create an event hub with the name lifecycleevents.

Creare criteri di firma di accesso condiviso per l'hub eventi

Successivamente, è necessario creare un criterio di firma di accesso condiviso per configurare l'hub eventi con l'app per le funzioni. Per creare i criteri di firma di accesso condiviso:

  1. Passare all'hub eventi creato nella portale di Azure e selezionare Criteri di accesso condiviso nelle opzioni di menu a sinistra.
  2. Seleziona Aggiungi. Nella finestra Aggiungi criteri di firma di accesso condiviso visualizzata immettere un nome di criterio di propria scelta e selezionare la casella di controllo Listen (Ascolta).
  3. Seleziona Crea.

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

Configurare l'hub eventi con l'app per le funzioni

Configurare quindi l'app per le funzioni di Azure configurata nella sezione Prerequisiti per usare il nuovo hub eventi. Si configurerà la funzione impostando una variabile di ambiente all'interno dell'app per le funzioni con il stringa di connessione dell'hub eventi.

  1. Aprire il criterio creato e copiare il valore della chiave primaria della stringa di Connessione ion.

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

  2. Aggiungere il stringa di connessione come variabile nelle impostazioni dell'app per le funzioni con il comando dell'interfaccia della riga di comando di Azure seguente. Il comando può essere eseguito in Cloud Shell o in locale se nel computer è installata l'interfaccia della riga di comando di Azure.

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

Aggiungere una funzione per ritirarsi con eventi del ciclo di vita hub IoT

All'interno del progetto dell'app per le funzioni creato nella sezione Prerequisiti si creerà una nuova funzione per ritirare un dispositivo esistente usando hub IoT eventi del ciclo di vita.

Per altre informazioni sugli eventi del ciclo di vita, vedere hub IoT eventi non di telemetria. Per altre informazioni sull'uso di Hub eventi con funzioni di Azure, vedere Hub eventi di Azure trigger per Funzioni di Azure.

Passare al progetto dell'app per le funzioni nel computer e seguire questa procedura.

  1. Creare prima di tutto una nuova funzione di tipo Trigger hub eventi nel progetto dell'app per le funzioni.

  2. Aggiungere un nuovo pacchetto NuGet al progetto: Microsoft.Azure.Devices.Provisioning.Service. Potrebbe essere necessario aggiungere anche altri pacchetti al progetto, se i pacchetti usati nel codice non fanno già parte del progetto.

  3. Nel file di codice della funzione appena creato incollare il codice seguente, denominare la funzione DeleteDeviceInTwinFunc.cs e salvare il file.

    // 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. Pubblicare il progetto con la funzione DeleteDeviceInTwinFunc.cs in un'app per le funzioni in Azure.

    Per istruzioni su come pubblicare la funzione con Visual Studio, vedere Sviluppare Funzioni di Azure con Visual Studio. Per istruzioni su come pubblicare la funzione con Visual Studio Code, vedere Creare una funzione C# in Azure con Visual Studio Code. Per istruzioni su come pubblicare la funzione usando l'interfaccia della riga di comando di Azure, vedere Creare una funzione C# in Azure dalla riga di comando.

Importante

Quando si crea l'app per le funzioni per la prima volta nella sezione Prerequisiti, potrebbe essere già stato assegnato un ruolo di accesso per la funzione e sono state configurate le impostazioni dell'applicazione per accedere all'istanza di Gemelli digitali di Azure. Queste operazioni devono essere eseguite una sola volta per l'intera app per le funzioni, quindi verificare che siano state completate nell'app prima di continuare. Le istruzioni sono disponibili nella sezione Configurare l'app pubblicata dell'articolo Scrivere il codice di autenticazione dell'app.

Creare una route hub IoT per gli eventi del ciclo di vita

A questo punto si configurerà una route hub IoT per instradare gli eventi del ciclo di vita del dispositivo. In questo caso, si ascolteranno in modo specifico gli eventi di eliminazione del dispositivo, identificati da if (opType == "deleteDeviceIdentity"). Questo evento attiverà l'eliminazione dell'elemento del gemello digitale, completando il processo di ritiro di un dispositivo e del gemello digitale.

Prima di tutto, è necessario creare un endpoint dell'hub eventi nell'hub IoT. Si aggiungerà quindi una route nell'hub IoT per inviare gli eventi del ciclo di vita a questo endpoint dell'hub eventi. Seguire questa procedura per creare un endpoint dell'hub eventi:

  1. Nella portale di Azure passare all'hub IoT creato nella sezione Prerequisiti e selezionare Routing dei messaggi nelle opzioni di menu a sinistra.

  2. Selezionare la scheda Endpoint personalizzati.

  3. Selezionare + Aggiungi e scegliere Hub eventi per aggiungere un endpoint del tipo di Hub eventi.

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

  4. Nella finestra Aggiungi un endpoint dell'hub eventi che si apre scegliere i valori seguenti:

    • Nome endpoint: scegliere un nome endpoint.
    • Spazio dei nomi dell'hub eventi: selezionare lo spazio dei nomi dell'hub eventi dall'elenco a discesa.
    • Istanza dell'hub eventi: scegliere il nome dell'hub eventi creato nel passaggio precedente.
  5. Seleziona Crea. Mantenere aperta questa finestra per aggiungere una route nel passaggio successivo.

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

Si aggiungerà quindi una route che si connette all'endpoint creato nel passaggio precedente, con una query di routing che invia gli eventi di eliminazione. Per creare una route, seguire questa procedura:

  1. Passare alla scheda Route e selezionare Aggiungi per aggiungere una route.

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

  2. Nella pagina Aggiungi una route visualizzata scegliere i valori seguenti:

    • Nome: scegliere un nome per la route.
    • Endpoint: scegliere l'endpoint di Hub eventi creato in precedenza dall'elenco a discesa.
    • Origine dati: scegliere Eventi del ciclo di vita del dispositivo.
    • Query di routing: immettere opType='deleteDeviceIdentity'. Questa query limita gli eventi del ciclo di vita del dispositivo per inviare solo gli eventi di eliminazione.
  3. Seleziona Salva.

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

Dopo aver completato questo flusso, tutto è impostato per ritirare i dispositivi end-to-end.

Convalida

Per attivare il processo di ritiro, è necessario eliminare manualmente il dispositivo da hub IoT.

È possibile eliminare manualmente il dispositivo da hub IoT con un comando dell'interfaccia della riga di comando di Azure o nella portale di Azure. Seguire questa procedura per eliminare il dispositivo nel portale di Azure:

  1. Passare all'hub IoT e scegliere Dispositivi IoT nelle opzioni di menu a sinistra.
  2. Verrà visualizzato un dispositivo con l'ID di registrazione del dispositivo scelto nella prima metà di questo articolo. È anche possibile scegliere qualsiasi altro dispositivo da eliminare, purché abbia un gemello in Gemelli digitali di Azure, in modo da poter verificare che il gemello venga eliminato automaticamente dopo l'eliminazione del dispositivo.
  3. Selezionare il dispositivo e scegliere Elimina.

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

Potrebbero essere necessari alcuni minuti per visualizzare le modifiche riflesse in Gemelli digitali di Azure.

Usare il comando seguente dell'interfaccia della riga di comando di Gemelli digitali di Azure per verificare che il dispositivo gemello nell'istanza di Gemelli digitali di Azure sia stato eliminato. Esiste un segnaposto per il nome host dell'istanza (è anche possibile usare il nome descrittivo dell'istanza con un lieve calo delle prestazioni) e un segnaposto per l'ID registrazione del dispositivo.

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

Si noterà che il gemello del dispositivo non è più disponibile nell'istanza di Gemelli digitali di Azure.

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

Pulire le risorse

Se le risorse create in questo articolo non sono più necessarie, seguire questa procedura per eliminarle.

Usando Azure Cloud Shell o l'interfaccia della riga di comando di Azure locale, è possibile eliminare tutte le risorse di Azure in un gruppo di risorse con il comando az group delete . Questo comando rimuove il gruppo di risorse; l'istanza di Gemelli digitali di Azure; l'hub IoT e la registrazione del dispositivo hub; l'argomento di Griglia di eventi e le sottoscrizioni associate; lo spazio dei nomi di Hub eventi e le app Funzioni di Azure, incluse le risorse associate, ad esempio l'archiviazione.

Importante

L'eliminazione di un gruppo di risorse è irreversibile. Il gruppo di risorse e tutte le risorse in esso contenute vengono eliminati in modo permanente. Assicurarsi di non eliminare accidentalmente il gruppo di risorse sbagliato o le risorse errate.

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

Eliminare quindi la cartella di esempio del progetto scaricata dal computer locale.

Passaggi successivi

I gemelli digitali creati per i dispositivi vengono archiviati come gerarchia piatta in Gemelli digitali di Azure, ma possono essere arricchiti con informazioni sul modello e una gerarchia multilivello per l'organizzazione. Per altre informazioni su questo concetto, leggere:

Per altre informazioni sull'uso delle richieste HTTP con funzioni di Azure, vedere:

È possibile scrivere logica personalizzata per fornire automaticamente queste informazioni usando il modello e i dati del grafo già archiviati in Gemelli digitali di Azure. Per altre informazioni sulla gestione, l'aggiornamento e il recupero di informazioni dal grafico dei gemelli, vedere le guide pratiche seguenti: