Partilhar via


Importar e exportar identidades do dispositivo do Hub IoT em massa

Cada hub IoT tem um registro de identidade que você pode usar para criar recursos de dispositivo no serviço. O registo de identidades também permite controlar o acesso aos pontos finais voltados para o dispositivo. Este artigo descreve como importar e exportar identidades de dispositivo em massa de e para um registro de identidade, usando o exemplo ImportExportDeviceSample incluído com o SDK do Microsoft Azure IoT para .NET. Para obter mais informações sobre como você pode usar esse recurso ao migrar um hub IoT para uma região diferente, consulte Como migrar manualmente um hub IoT do Azure usando um modelo do Azure Resource Manager.

Nota

O Hub IoT adicionou recentemente suporte a rede virtual em um número limitado de regiões. Esse recurso protege as operações de importação e exportação e elimina a necessidade de chaves de passagem para autenticação. Atualmente, o suporte de rede virtual está disponível apenas nestas regiões: WestUS2, EastUS e SouthCentralUS. Para saber mais sobre o suporte de rede virtual e as chamadas de API para implementá-lo, consulte Suporte do Hub IoT para redes virtuais.

As operações de importação e exportação ocorrem no contexto de trabalhos que permitem executar operações de serviço em massa em um hub IoT.

A classe RegistryManager no SDK inclui os métodos ExportDevicesAsync e ImportDevicesAsync que usam a estrutura Job . Esses métodos permitem exportar, importar e sincronizar a totalidade de um registro de identidade do hub IoT.

Este artigo discute o uso da classe RegistryManager e do sistema Job para executar importações e exportações em massa de dispositivos de e para o registro de identidade de um hub IoT. Você também pode usar o Serviço de Provisionamento de Dispositivo do Hub IoT do Azure para habilitar o provisionamento zero-touch, just-in-time para um ou mais hubs IoT. Para saber mais, consulte a documentação do serviço de provisionamento.

Nota

Alguns dos trechos de código neste artigo estão incluídos no exemplo de serviço ImportExportDevicesSample fornecido com o SDK do Microsoft Azure IoT para .NET. O exemplo está localizado na /iothub/service/samples/how to guides/ImportExportDevicesSample pasta do SDK e, quando especificado, trechos de código são incluídos do ImportExportDevicesSample.cs arquivo para esse exemplo de SDK. Para obter mais informações sobre o exemplo ImportExportDevicesSample e outros exemplos de serviço incluídos no for.NET SDK do Azure IoT, consulte Exemplos de serviço do hub IoT do Azure para C#.

O que são empregos?

As operações de registro de identidade usam o sistema de trabalho quando a operação:

  • Tem um tempo de execução potencialmente longo em comparação com as operações de tempo de execução padrão.

  • Retorna uma grande quantidade de dados para o usuário.

Em vez de uma única chamada de API aguardando ou bloqueando o resultado da operação, a operação cria de forma assíncrona um trabalho para esse hub IoT. Em seguida, a operação retorna imediatamente um objeto JobProperties .

Importante

Este artigo inclui etapas para se conectar a um serviço usando uma assinatura de acesso compartilhado. Esse método de autenticação é conveniente para teste e avaliação, mas autenticar em um serviço com ID do Microsoft Entra ou identidades gerenciadas é uma abordagem mais segura. Para saber mais, consulte Práticas > recomendadas de segurança Segurança na nuvem.

O seguinte trecho de código C# mostra como criar um trabalho de exportação:

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

Nota

Para usar a classe RegistryManager em seu código C#, adicione o pacote NuGet Microsoft.Azure.Devices ao seu projeto. A classe RegistryManager está no namespace Microsoft.Azure.Devices .

Você pode usar a classe RegistryManager para consultar o estado do Job usando os metadados JobProperties retornados. Para criar uma instância da classe RegistryManager , use o método CreateFromConnectionString .

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

Para localizar a cadeia de conexão para seu hub IoT, no portal do Azure:

  1. Navegue até ao seu hub IoT.

  2. Selecione Políticas de acesso compartilhado.

  3. Selecione uma política, tendo em conta as permissões de que necessita.

  4. Copie a cadeia de conexão dessa política.

O seguinte trecho de código C#, do método WaitForJobAsync no exemplo SDK, mostra como sondar a cada cinco segundos para ver se o trabalho terminou de ser executado:

// 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));
}

Nota

Se sua conta de armazenamento tiver configurações de firewall que restrinjam a conectividade do Hub IoT, considere usar a exceção de primeira parte confiável da Microsoft (disponível em regiões selecionadas para hubs IoT com identidade de serviço gerenciado).

Limites de trabalhos de importação/exportação de dispositivos

Apenas é permitido um trabalho de importação ou exportação de dispositivos ativo de cada vez para todos os escalões do Hub IoT. O Hub IoT também tem limites para a taxa de operações de trabalhos. Para saber mais, veja Limitação e quotas do Hub IoT.

Exportar dispositivos

Use o método ExportDevicesAsync para exportar a totalidade de um registro de identidade do hub IoT para um contêiner de blob de Armazenamento do Azure usando uma assinatura de acesso compartilhado (SAS). Esse método permite que você crie backups confiáveis das informações do seu dispositivo em um contêiner de blob que você controla.

O método ExportDevicesAsync requer dois parâmetros:

  • Uma cadeia de caracteres que contém um URI de um contêiner de blob. Esse URI deve conter um token SAS que conceda acesso de gravação ao contêiner. O trabalho cria um blob de bloco nesse contêiner para armazenar os dados serializados do dispositivo de exportação. O token SAS deve incluir estas permissões:

    SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read 
       | SharedAccessBlobPermissions.Delete
    
  • Um booleano que indica se você deseja excluir chaves de autenticação de seus dados de exportação. Se false, as chaves de autenticação são incluídas na saída de exportação. Caso contrário, as chaves serão exportadas como null.

O seguinte trecho de código C# mostra como iniciar um trabalho de exportação que inclui chaves de autenticação de dispositivo nos dados de exportação e, em seguida, pesquisar para conclusão:

// 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));
}

Você pode encontrar código semelhante no método ExportDevicesAsync do exemplo SDK. O trabalho armazena sua saída no contêiner de blob fornecido como um blob de bloco com o nome devices.txt. Os dados de saída consistem em dados de dispositivo serializados JSON, com um dispositivo por linha.

O exemplo a seguir mostra os dados de saída:

{"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="}}}

Se um dispositivo tiver dados duplos, os dados gêmeos também serão exportados juntamente com os dados do dispositivo. O exemplo a seguir mostra esse formato. Todos os dados da linha "twinETag" até ao final são dados gémeos.

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

Se você precisar acessar esses dados no código, poderá desserializar esses dados usando a classe ExportImportDevice . O seguinte trecho de código C#, do método ReadFromBlobAsync no exemplo SDK, mostra como ler informações de dispositivo que foram exportadas anteriormente de ExportImportDevice para uma instância 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;
}

Importar dispositivos

O método ImportDevicesAsync na classe RegistryManager permite executar operações de importação e sincronização em massa em um registro de identidade do hub IoT. Como o método ExportDevicesAsync , o método ImportDevicesAsync usa a estrutura Job .

Tenha cuidado ao usar o método ImportDevicesAsync porque, além de provisionar novos dispositivos em seu registro de identidade, ele também pode atualizar e excluir dispositivos existentes.

Aviso

Uma operação de importação não pode ser desfeita. Sempre faça backup de seus dados existentes usando o método ExportDevicesAsync em outro contêiner de blob antes de fazer alterações em massa no seu registro de identidade.

O método ImportDevicesAsync usa dois parâmetros:

  • Uma cadeia de caracteres que contém um URI de um contêiner de blob de Armazenamento do Azure para usar como entrada para o trabalho. Esse URI deve conter um token SAS que conceda acesso de leitura ao contêiner. Esse contêiner deve conter um blob com o nome devices.txt que contém os dados do dispositivo serializado a serem importados para o registro de identidade. Os dados de importação devem conter informações do dispositivo no mesmo formato JSON que o trabalho ExportImportDevice usa quando cria um blob devices.txt . O token SAS deve incluir estas permissões:

    SharedAccessBlobPermissions.Read
    
  • Uma cadeia de caracteres que contém um URI de um contêiner de blob de Armazenamento do Azure para usar como saída do trabalho. O trabalho cria um blob de bloco neste contêiner para armazenar quaisquer informações de erro do trabalho de importação concluído. O token SAS deve incluir estas permissões:

    SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read 
       | SharedAccessBlobPermissions.Delete
    

Nota

Os dois parâmetros podem apontar para o mesmo contêiner de blob. Os parâmetros separados simplesmente permitem mais controle sobre seus dados, pois o contêiner de saída requer permissões adicionais.

O seguinte trecho de código C# mostra como iniciar um trabalho de importação:

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

Este método também pode ser usado para importar os dados para o dispositivo gêmeo. O formato para a entrada de dados é o mesmo que o formato mostrado na seção ExportDevicesAsync . Dessa forma, você pode reimportar os dados exportados.

Comportamento de importação

Você pode usar o método ImportDevicesAsync para executar as seguintes operações em massa em seu registro de identidade:

  • Registo em massa de novos dispositivos
  • Exclusões em massa de dispositivos existentes
  • Alterações de status em massa (habilitar ou desabilitar dispositivos)
  • Atribuição em massa de novas chaves de autenticação de dispositivo
  • Regeneração automática em massa de chaves de autenticação de dispositivo
  • Atualização em massa de dados duplos

Você pode executar qualquer combinação das operações anteriores em uma única chamada ImportDevicesAsync . Por exemplo, você pode registrar novos dispositivos e excluir ou atualizar dispositivos existentes ao mesmo tempo. Quando usado junto com o método ExportDevicesAsync , você pode migrar completamente todos os seus dispositivos de um hub IoT para outro.

Use a propriedade importMode opcional nos dados de serialização de importação para cada dispositivo para controlar o processo de importação por dispositivo. A propriedade importMode tem as seguintes opções:

  • Criar
  • CreateOrUpdate (padrão)
  • CreateOrUpdateIfMatchETag
  • Eliminar
  • DeleteIfMatchETag
  • Atualização
  • UpdateIfMatchETag
  • UpdateTwin
  • UpdateTwinIfMatchETag

Para obter detalhes sobre cada uma dessas opções de modo de importação, consulte ImportMode

Solucionar problemas de importação de trabalhos

Usar um trabalho de importação para criar dispositivos pode falhar com um problema de cota quando estiver próximo do limite de contagem de dispositivos do hub IoT. Essa falha pode acontecer mesmo se a contagem total de dispositivos ainda for inferior ao limite de cota. O erro IotHubQuotaExceeded (403002) é retornado com a seguinte mensagem de erro: "Número total de dispositivos no IotHub excedeu a cota alocada."

Se você receber esse erro, poderá usar a seguinte consulta para retornar o número total de dispositivos registrados em seu hub IoT:

SELECT COUNT() as totalNumberOfDevices FROM devices

Para obter informações sobre o número total de dispositivos que podem ser registrados em um hub IoT, consulte Limites do Hub IoT.

Se ainda houver cota disponível, você poderá examinar o blob de saída do trabalho para dispositivos que falharam com o erro IotHubQuotaExceeded (403002 ). Em seguida, você pode tentar adicionar esses dispositivos individualmente ao hub IoT. Por exemplo, você pode usar os métodos AddDeviceAsync ou AddDeviceWithTwinAsync . Não tente adicionar os dispositivos usando outro trabalho, pois você pode encontrar o mesmo erro.

Exemplo de importação de dispositivos – provisionamento de dispositivos em massa

O seguinte trecho de código C#, do método GenerateDevicesAsync no exemplo SDK, ilustra como gerar várias identidades de dispositivo que:

  • Inclua chaves de autenticação.
  • Escreva as informações do dispositivo em um blob de bloco.
  • Importe os dispositivos para o registro de identidade.
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}.");
}

Exemplo de importação de dispositivos – exclusão em massa

O seguinte trecho de código C#, do método DeleteFromHubAsync no exemplo SDK, mostra como excluir todos os dispositivos de um hub IoT:

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}");
}

Obter o URI SAS do contêiner

O exemplo de código a seguir mostra como gerar um URI SAS com permissões de leitura, gravação e exclusão para um contêiner de 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;
}

Próximos passos

Neste artigo, você aprendeu como executar operações em massa no registro de identidade em um hub IoT. Muitas dessas operações, incluindo como mover dispositivos de um hub para outro, são usadas na seção Gerenciar dispositivos registrados no hub IoT de Como migrar manualmente um hub IoT do Azure usando um modelo do Azure Resource Manager.