Hromadný import a export identit zařízení služby IoT Hub

Každé centrum IoT má registr identit, který můžete použít k vytváření prostředků zařízení ve službě. Registr identit také umožňuje řídit přístup ke koncovým bodům pro zařízení. Tento článek popisuje, jak importovat a exportovat identity zařízení hromadně do registru identit a z registru identit pomocí ukázky ImportExportDeviceSample, která je součástí sady Microsoft Azure IoT SDK pro .NET. Další informace o tom, jak tuto funkci použít při migraci centra IoT do jiné oblasti, najdete v tématu Postup ruční migrace centra Azure IoT pomocí šablony Azure Resource Manageru.

Poznámka:

IoT Hub nedávno přidal podporu virtuální sítě v omezeném počtu oblastí. Tato funkce zabezpečuje operace importu a exportu a eliminuje nutnost předávat klíče pro ověřování. V současné době je podpora virtuální sítě dostupná jenom v těchto oblastech: WestUS2, EastUS a SouthCentralUS. Další informace o podpoře virtuálních sítí a voláních rozhraní API pro její implementaci najdete v tématu Podpora služby IoT Hub pro virtuální sítě.

Operace importu a exportu probíhají v kontextu úloh , které umožňují spouštět hromadné operace služby v centru IoT.

Třída RegistryManager v sadě SDK zahrnuje metody ExportDevicesAsync a ImportDevicesAsync, které používají architekturu Úloh. Tyto metody umožňují exportovat, importovat a synchronizovat celý registr identit služby IoT Hub.

Tento článek popisuje použití třídy RegistryManager a systému úloh k provádění hromadného importu a exportu zařízení do a z registru identit služby IoT Hub. Službu Azure IoT Hub Device Provisioning můžete také použít k povolení bezdotykového zřizování za běhu pro jedno nebo více center IoT. Další informace najdete v dokumentaci ke službě zřizování.

Poznámka:

Některé fragmenty kódu v tomto článku jsou součástí ukázky služby ImportExportDevicesSample, která je součástí sady Microsoft Azure IoT SDK pro .NET. Ukázka se nachází ve /iothub/service/samples/how to guides/ImportExportDevicesSample složce sady SDK a tam, kde je uvedeno, fragmenty kódu se zahrnou ze souboru pro tuto ukázku ImportExportDevicesSample.cs sady SDK. Další informace o ukázce ImportExportDevicesSample a dalších ukázkách služeb, které jsou součástí sady Azure IoT SDK for.NET, najdete v ukázkách služby Azure IoT Hub pro jazyk C#.

Co jsou úlohy?

Operace registru identit používají systém úloh při operaci:

  • Má potenciálně dlouhou dobu provádění v porovnání se standardními operacemi za běhu.

  • Vrátí uživateli velké množství dat.

Místo jednoho volání rozhraní API čekající nebo blokující výsledek operace operace asynchronně vytvoří pro toto centrum IoT úlohu. Operace pak okamžitě vrátí objekt JobProperties .

Následující fragment kódu jazyka C# ukazuje, jak vytvořit úlohu exportu:

// Call an export job on the IoT hub to retrieve all devices
JobProperties exportJob = await 
  registryManager.ExportDevicesAsync(containerSasUri, false);

Poznámka:

Pokud chcete v kódu jazyka C# použít třídu RegistryManager , přidejte do projektu balíček NuGet Microsoft.Azure.Devices . Třída RegistryManager je v oboru názvů Microsoft.Azure.Devices .

Třídu RegistryManager můžete použít k dotazování stavu úlohy pomocí vrácených metadat JobProperties. Chcete-li vytvořit instanci RegistryManager třídy, použijte CreateFrom Připojení ionString metoda.

RegistryManager registryManager =
  RegistryManager.CreateFromConnectionString("{your IoT Hub connection string}");

Pokud chcete najít připojovací řetězec pro centrum IoT, na webu Azure Portal:

  1. Přejděte do svého centra IoT.

  2. Vyberte zásady sdíleného přístupu.

  3. Vyberte zásadu s ohledem na potřebná oprávnění.

  4. Zkopírujte připojovací řetězec pro danou zásadu.

Následující fragment kódu jazyka C# z metody WaitForJobAsync v ukázce sady SDK ukazuje, jak se dotazovat každých pět sekund, abyste zjistili, jestli úloha dokončila provádění:

// Wait until job is finished
while (true)
{
    job = await registryManager.GetJobAsync(job.JobId);
    if (job.Status == JobStatus.Completed
        || job.Status == JobStatus.Failed
        || job.Status == JobStatus.Cancelled)
    {
        // Job has finished executing
        break;
    }
    Console.WriteLine($"\tJob status is {job.Status}...");

    await Task.Delay(TimeSpan.FromSeconds(5));
}

Poznámka:

Pokud má váš účet úložiště konfigurace brány firewall, které omezují připojení služby IoT Hub, zvažte použití výjimky důvěryhodné první strany Od Microsoftu (dostupné ve vybraných oblastech pro centra IoT s identitou spravované služby).

Limity úloh importu a exportu zařízení

Pro všechny úrovně služby IoT Hub je povolená vždy jenom jedna aktivní úloha importu nebo exportu zařízení. IoT Hub má také limity pro rychlost operací úloh. Další informace najdete v tématu Kvóty a omezování služby IoT Hub.

Export zařízení

K exportu celého registru identit služby IoT Hub do kontejneru objektů blob služby Azure Storage pomocí sdíleného přístupového podpisu (SAS) použijte metodu ExportDevicesAsync . Tato metoda umožňuje vytvářet spolehlivé zálohy informací o zařízení v kontejneru objektů blob, který řídíte.

Metoda ExportDevicesAsync vyžaduje dva parametry:

  • Řetězec, který obsahuje identifikátor URI kontejneru objektů blob. Tento identifikátor URI musí obsahovat token SAS, který uděluje přístup k zápisu do kontejneru. Úloha vytvoří v tomto kontejneru objekt blob bloku pro uložení serializovaných dat zařízení pro export. Token SAS musí obsahovat tato oprávnění:

    SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read 
       | SharedAccessBlobPermissions.Delete
    
  • Logická hodnota, která označuje, jestli chcete vyloučit ověřovací klíče z dat exportu. Pokud je false, ověřovací klíče jsou zahrnuté ve výstupu exportu. Jinak se klíče exportují jako null.

Následující fragment kódu jazyka C# ukazuje, jak zahájit úlohu exportu, která zahrnuje ověřovací klíče zařízení v datech exportu, a pak se dotazovat na dokončení:

// Call an export job on the IoT Hub to retrieve all devices
JobProperties exportJob = 
  await registryManager.ExportDevicesAsync(containerSasUri, false);

// Wait until job is finished
while(true)
{
    exportJob = await registryManager.GetJobAsync(exportJob.JobId);
    if (exportJob.Status == JobStatus.Completed || 
        exportJob.Status == JobStatus.Failed ||
        exportJob.Status == JobStatus.Cancelled)
    {
    // Job has finished executing
    break;
    }

    await Task.Delay(TimeSpan.FromSeconds(5));
}

Podobný kód najdete v metodě ExportDevicesAsync z ukázky sady SDK. Úloha ukládá výstup do zadaného kontejneru objektů blob jako objekt blob bloku s názvem devices.txt. Výstupní data se skládají z serializovaných dat zařízení JSON s jedním zařízením na řádek.

Následující příklad ukazuje výstupní data:

{"id":"Device1","eTag":"MA==","status":"enabled","authentication":{"symmetricKey":{"primaryKey":"abc=","secondaryKey":"def="}}}
{"id":"Device2","eTag":"MA==","status":"enabled","authentication":{"symmetricKey":{"primaryKey":"abc=","secondaryKey":"def="}}}
{"id":"Device3","eTag":"MA==","status":"disabled","authentication":{"symmetricKey":{"primaryKey":"abc=","secondaryKey":"def="}}}
{"id":"Device4","eTag":"MA==","status":"disabled","authentication":{"symmetricKey":{"primaryKey":"abc=","secondaryKey":"def="}}}
{"id":"Device5","eTag":"MA==","status":"enabled","authentication":{"symmetricKey":{"primaryKey":"abc=","secondaryKey":"def="}}}

Pokud má zařízení data dvojčete, data dvojčete se také exportují společně s daty zařízení. Následující příklad ukazuje tento formát. Všechna data z řádku twinETag až do konce jsou data dvojčete.

{
   "id":"export-6d84f075-0",
   "eTag":"MQ==",
   "status":"enabled",
   "authentication":null,
   "twinETag":"AAAAAAAAAAI=",
   "tags":{
      "Location":"LivingRoom"
   },
   "properties":{
      "desired":{
         "Thermostat":{
            "Temperature":75.1,
            "Unit":"F"
         },
      },
      "reported":{}
   }
}

Pokud potřebujete přístup k datům v kódu, můžete deserializovat tato data pomocí třídy ExportImportDevice . Následující fragment kódu jazyka C# z metody ReadFromBlobAsync v ukázce sady SDK ukazuje, jak číst informace o zařízení, které byly dříve exportovány z ExportImportDevice do instance BlobClient :

private static async Task<List<string>> ReadFromBlobAsync(BlobClient blobClient)
{
    // Read the blob file of devices, import each row into a list.
    var contents = new List<string>();

    using Stream blobStream = await blobClient.OpenReadAsync();
    using var streamReader = new StreamReader(blobStream, Encoding.UTF8);
    while (streamReader.Peek() != -1)
    {
        string line = await streamReader.ReadLineAsync();
        contents.Add(line);
    }

    return contents;
}

Import zařízení

Metoda ImportDevicesAsync ve třídě RegistryManager umožňuje provádět hromadné operace importu a synchronizace v registru identit služby IoT Hub. Podobně jako metoda ExportDevicesAsync používá metoda ImportDevicesAsync architekturu Úloh.

Dejte pozor pomocí metody ImportDevicesAsync , protože kromě zřizování nových zařízení v registru identit může také aktualizovat a odstranit existující zařízení.

Upozorňující

Operaci importu nelze vrátit zpět. Před hromadnou změnou registru identit vždy zálohujte stávající data pomocí metody ExportDevicesAsync do jiného kontejneru objektů blob.

Metoda ImportDevicesAsync má dva parametry:

  • Řetězec, který obsahuje identifikátor URI kontejneru objektů blob služby Azure Storage, který se použije jako vstup do úlohy. Tento identifikátor URI musí obsahovat token SAS, který uděluje přístup ke čtení kontejneru. Tento kontejner musí obsahovat objekt blob s názvem devices.txt , který obsahuje serializovaná data zařízení pro import do vašeho registru identit. Data importu musí obsahovat informace o zařízení ve stejném formátu JSON, který úloha ExportImportDevice používá při vytváření objektu blob devices.txt . Token SAS musí obsahovat tato oprávnění:

    SharedAccessBlobPermissions.Read
    
  • Řetězec, který obsahuje identifikátor URI kontejneru objektů blob služby Azure Storage, který se má použít jako výstup úlohy. Úloha vytvoří v tomto kontejneru objekt blob bloku pro uložení všech informací o chybách z dokončené úlohy importu. Token SAS musí obsahovat tato oprávnění:

    SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read 
       | SharedAccessBlobPermissions.Delete
    

Poznámka:

Dva parametry můžou odkazovat na stejný kontejner objektů blob. Samostatné parametry jednoduše umožňují větší kontrolu nad daty, protože výstupní kontejner vyžaduje další oprávnění.

Následující fragment kódu jazyka C# ukazuje, jak zahájit úlohu importu:

JobProperties importJob = 
   await registryManager.ImportDevicesAsync(containerSasUri, containerSasUri);

Tuto metodu lze také použít k importu dat dvojčete zařízení. Formát pro vstup dat je stejný jako formát zobrazený v části ExportDevicesAsync . Tímto způsobem můžete znovu importovat exportovaná data.

Chování importu

K provedení následujících hromadných operací v registru identit můžete použít metodu ImportDevicesAsync :

  • Hromadná registrace nových zařízení
  • Hromadné odstranění stávajících zařízení
  • Hromadné změny stavu (povolení nebo zakázání zařízení)
  • Hromadné přiřazení nových ověřovacích klíčů zařízení
  • Hromadná automatická regenerace ověřovacích klíčů zařízení
  • Hromadná aktualizace dat dvojčat

V rámci jednoho volání ImportDevicesAsync můžete provést libovolnou kombinaci předchozích operací. Můžete například zaregistrovat nová zařízení a současně odstranit nebo aktualizovat stávající zařízení. Při použití společně s metodou ExportDevicesAsync můžete úplně migrovat všechna zařízení z jednoho centra IoT do jiného.

Pro každé zařízení použijte volitelnou vlastnost importMode v datech serializace importu pro každé zařízení. Vlastnost importMode má následující možnosti:

  • Vytvořit
  • CreateOrUpdate (výchozí)
  • CreateOrUpdateIfMatchETag
  • Odstranění
  • DeleteIfMatchETag
  • Aktualizace
  • UpdateIfMatchETag
  • UpdateTwin
  • UpdateTwinIfMatchETag

Podrobnosti o jednotlivých těchto možnostech režimu importu najdete v tématu ImportMode.

Řešení potíží s úlohami importu

Použití úlohy importu k vytvoření zařízení může selhat s problémem s kvótou, když se blíží limitu počtu zařízení centra IoT. K tomuto selhání může dojít i v případě, že celkový počet zařízení je stále nižší než limit kvóty. Chyba IotHubQuotaExceeded (403002) se vrátí s následující chybovou zprávou: Celkový počet zařízení na IotHubu překročil přidělenou kvótu.

Pokud se zobrazí tato chyba, můžete pomocí následujícího dotazu vrátit celkový počet zařízení zaregistrovaných ve službě IoT Hub:

SELECT COUNT() as totalNumberOfDevices FROM devices

Informace o celkovém počtu zařízení, která je možné zaregistrovat do centra IoT, najdete v tématu Omezení služby IoT Hub.

Pokud je stále k dispozici kvóta, můžete zkontrolovat výstupní objekt blob úlohy pro zařízení, která selhala, s chybou IotHubQuotaExceeded (403002). Pak můžete zkusit přidat tato zařízení jednotlivě do centra IoT. Můžete například použít metody AddDeviceAsync nebo AddDeviceWithTwinAsync . Nepokoušejte se přidat zařízení pomocí jiné úlohy, protože se může zobrazit stejná chyba.

Příklad importu zařízení – hromadné zřizování zařízení

Následující fragment kódu jazyka C# z metody GenerateDevicesAsync v ukázce sady SDK ukazuje, jak vygenerovat více identit zařízení, které:

  • Zahrňte ověřovací klíče.
  • Zapište informace o zařízení do objektu blob bloku.
  • Importujte zařízení do registru identit.
private async Task GenerateDevicesAsync(RegistryManager registryManager, int numToAdd)
{
    var stopwatch = Stopwatch.StartNew();

    Console.WriteLine($"Creating {numToAdd} devices for the source IoT hub.");
    int interimProgressCount = 0;
    int displayProgressCount = 1000;
    int totalProgressCount = 0;

    // generate reference for list of new devices we're going to add, will write list to this blob
    BlobClient generateDevicesBlob = _blobContainerClient.GetBlobClient(_generateDevicesBlobName);

    // define serializedDevices as a generic list<string>
    var serializedDevices = new List<string>(numToAdd);

    for (int i = 1; i <= numToAdd; i++)
    {
        // Create device name with this format: Hub_00000000 + a new guid.
        // This should be large enough to display the largest number (1 million).
        string deviceName = $"Hub_{i:D8}_{Guid.NewGuid()}";
        Debug.Print($"Adding device '{deviceName}'");

        // Create a new ExportImportDevice.
        var deviceToAdd = new ExportImportDevice
        {
            Id = deviceName,
            Status = DeviceStatus.Enabled,
            Authentication = new AuthenticationMechanism
            {
                SymmetricKey = new SymmetricKey
                {
                    PrimaryKey = GenerateKey(32),
                    SecondaryKey = GenerateKey(32),
                }
            },
            // This indicates that the entry should be added as a new device.
            ImportMode = ImportMode.Create,
        };

        // Add device to the list as a serialized object.
        serializedDevices.Add(JsonConvert.SerializeObject(deviceToAdd));

        // Not real progress as you write the new devices, but will at least show *some* progress.
        interimProgressCount++;
        totalProgressCount++;
        if (interimProgressCount >= displayProgressCount)
        {
            Console.WriteLine($"Added {totalProgressCount}/{numToAdd} devices.");
            interimProgressCount = 0;
        }
    }

    // Now have a list of devices to be added, each one has been serialized.
    // Write the list to the blob.
    var sb = new StringBuilder();
    serializedDevices.ForEach(serializedDevice => sb.AppendLine(serializedDevice));

    // Write list of serialized objects to the blob.
    using Stream stream = await generateDevicesBlob.OpenWriteAsync(overwrite: true);
    byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString());
    for (int i = 0; i < bytes.Length; i += BlobWriteBytes)
    {
        int length = Math.Min(bytes.Length - i, BlobWriteBytes);
        await stream.WriteAsync(bytes.AsMemory(i, length));
    }
    await stream.FlushAsync();

    Console.WriteLine("Running a registry manager job to add the devices.");

    // Should now have a file with all the new devices in it as serialized objects in blob storage.
    // generatedListBlob has the list of devices to be added as serialized objects.
    // Call import using the blob to add the new devices.
    // Log information related to the job is written to the same container.
    // This normally takes 1 minute per 100 devices (according to the docs).

    // First, initiate an import job.
    // This reads in the rows from the text file and writes them to IoT Devices.
    // If you want to add devices from a file, you can create a file and use this to import it.
    //   They have to be in the exact right format.
    try
    {
        // The first URI is the container to import from; the file defaults to devices.txt, but may be specified.
        // The second URI points to the container to write errors to as a blob.
        // This lets you import the devices from any file name. Since we wrote the new
        // devices to [devicesToAdd], need to read the list from there as well.
        var importGeneratedDevicesJob = JobProperties.CreateForImportJob(
            _containerUri,
            _containerUri,
            _generateDevicesBlobName);
        importGeneratedDevicesJob = await registryManager.ImportDevicesAsync(importGeneratedDevicesJob);
        await WaitForJobAsync(registryManager, importGeneratedDevicesJob);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Adding devices failed due to {ex.Message}");
    }

    stopwatch.Stop();
    Console.WriteLine($"GenerateDevices, time elapsed = {stopwatch.Elapsed}.");
}

Příklad importu zařízení – hromadné odstranění

Následující fragment kódu jazyka C# z metody DeleteFromHubAsync v ukázce sady SDK ukazuje, jak odstranit všechna zařízení ze služby IoT Hub:

private async Task DeleteFromHubAsync(RegistryManager registryManager, bool includeConfigurations)
{
    var stopwatch = Stopwatch.StartNew();

    Console.WriteLine("Deleting all devices from an IoT hub.");

    Console.WriteLine("Exporting a list of devices from IoT hub to blob storage.");

    // Read from storage, which contains serialized objects.
    // Write each line to the serializedDevices list.
    BlobClient devicesBlobClient = _blobContainerClient.GetBlobClient(_destHubDevicesImportBlobName);

    Console.WriteLine("Reading the list of devices in from blob storage.");
    List<string> serializedDevices = await ReadFromBlobAsync(devicesBlobClient);

    // Step 1: Update each device's ImportMode to be Delete
    Console.WriteLine("Updating ImportMode to be 'Delete' for each device and writing back to the blob.");
    var sb = new StringBuilder();
    serializedDevices.ForEach(serializedEntity =>
    {
        // Deserialize back to an ExportImportDevice and change import mode.
        ExportImportDevice device = JsonConvert.DeserializeObject<ExportImportDevice>(serializedEntity);
        device.ImportMode = ImportMode.Delete;

        // Reserialize the object now that we've updated the property.
        sb.AppendLine(JsonConvert.SerializeObject(device));
    });

    // Step 2: Write the list in memory to the blob.
    BlobClient deleteDevicesBlobClient = _blobContainerClient.GetBlobClient(_hubDevicesCleanupBlobName);
    await WriteToBlobAsync(deleteDevicesBlobClient, sb.ToString());

    // Step 3: Call import using the same blob to delete all devices.
    Console.WriteLine("Running a registry manager job to delete the devices from the IoT hub.");
    var importJob = JobProperties.CreateForImportJob(
        _containerUri,
        _containerUri,
        _hubDevicesCleanupBlobName);
    importJob = await registryManager.ImportDevicesAsync(importJob);
    await WaitForJobAsync(registryManager, importJob);

    // Step 4: delete configurations
    if (includeConfigurations)
    {
        BlobClient configsBlobClient = _blobContainerClient.GetBlobClient(_srcHubConfigsExportBlobName);
        List<string> serializedConfigs = await ReadFromBlobAsync(configsBlobClient);
        foreach (string serializedConfig in serializedConfigs)
        {
            try
            {
                Configuration config = JsonConvert.DeserializeObject<Configuration>(serializedConfig);
                await registryManager.RemoveConfigurationAsync(config.Id);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed to deserialize or remove a config.\n\t{serializedConfig}\n\n{ex.Message}");
            }
        }
    }

    stopwatch.Stop();
    Console.WriteLine($"Deleted IoT hub devices and configs: time elapsed = {stopwatch.Elapsed}");
}

Získání identifikátoru URI SAS kontejneru

Následující ukázka kódu ukazuje, jak vygenerovat identifikátor URI SAS s oprávněními ke čtení, zápisu a odstranění kontejneru objektů blob:

static string GetContainerSasUri(CloudBlobContainer container)
{
  // Set the expiry time and permissions for the container.
  // In this case no start time is specified, so the
  // shared access signature becomes valid immediately.
  var sasConstraints = new SharedAccessBlobPolicy();
  sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24);
  sasConstraints.Permissions = 
    SharedAccessBlobPermissions.Write | 
    SharedAccessBlobPermissions.Read | 
    SharedAccessBlobPermissions.Delete;

  // Generate the shared access signature on the container,
  // setting the constraints directly on the signature.
  string sasContainerToken = container.GetSharedAccessSignature(sasConstraints);

  // Return the URI string for the container,
  // including the SAS token.
  return container.Uri + sasContainerToken;
}

Další kroky

V tomto článku jste zjistili, jak provádět hromadné operace s registrem identit v centru IoT. Mnoho z těchto operací, včetně toho, jak přesunout zařízení z jednoho centra do jiného, se používá v části Správa zařízení zaregistrovaných v části Centrum IoT v části Jak ručně migrovat centrum Azure IoT pomocí šablony Azure Resource Manageru.