Search code examples
azure-iot-hubazure-rm-templateazure-bicep

How to deploy an Azure IoT Hub storage account archiving in bicep?


I try to use Azure Resource Manager and bicep to deploy an IoT Hub and a storage account. IoT Hub has the feature to store all messages in an storage account for archiving purpose. The IoT Hub should access the storage account with an User-assigned Managed Identity.

I would like to deploy all these things in a single ARM deployment written in bicep. The problem is deploying the IoT Hub with a User-assigned Identity and setting up the archive custom route. I get the error:

{
    "code": "DeploymentFailed",
    "message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.",
    "details": [
        {
            "code": "400140",
            "message": "endpointName:messageArchive, exceptionMessage:Invalid operation: Managed identity is not enabled for IotHub ... errorcode: IH400140."
        }
    ]
}

My bicep file looks like this

resource messageArchive 'Microsoft.Storage/storageAccounts@2021-04-01' = {
  name: 'messagearchive4631'
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_GRS'
  }
  properties: {
    accessTier: 'Hot'
    supportsHttpsTrafficOnly: true
  }
}

resource messageArchiveBlobService 'Microsoft.Storage/storageAccounts/blobServices@2021-04-01' = {
  name: 'default'
  parent: messageArchive
  resource messageArchiveContainer 'containers@2021-02-01' = {
    name: 'iot-test-4631-container'
    properties: {
      publicAccess: 'None'
    }
  }
}

resource iotIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: 'iot-test-access-archive-4631'  
  location: resourceGroup().location
}

resource iotAccesToStorage 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
  name: guid(extensionResourceId(messageArchive.id, messageArchive.type, 'iot-test-access-archive-4631'))
  scope: messageArchive
  properties: {
    roleDefinitionId: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe'
    principalId: iotIdentity.properties.principalId
    description: 'Allow acces for IoT Hub'
  }
}

resource iothub 'Microsoft.Devices/IotHubs@2021-03-31' = {
  name: 'iot-test-4631'
  location: resourceGroup().location
  sku: {
    name: 'B1'
    capacity: 1
  }
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities:{
      '${iotIdentity.id}': {}
    }
  }
  dependsOn:[
    iotAccesToStorage
  ]
  properties: {
    features: 'None'
    eventHubEndpoints: {
      events: {
        retentionTimeInDays: 1
        partitionCount: 4
      }
    }
    routing: {
      endpoints: {
        storageContainers: [
          {
            name: 'messageArchive'
            endpointUri: 'https://messagearchive4631.blob.core.windows.net/'
            containerName: 'iot-test-4631-container'
            batchFrequencyInSeconds: 100
            maxChunkSizeInBytes: 104857600
            encoding: 'Avro'
            fileNameFormat: '{iothub}/{YYYY}/{MM}/{DD}/{HH}/{mm}_{partition}.avro'
            authenticationType: 'identityBased'
          }
        ]
      }
      routes: [
        {
          name: 'EventHub'
          source: 'DeviceMessages'
          endpointNames: [
            'events'
          ]
          isEnabled: true
        }
        {
          name: 'messageArchiveRoute'
          source: 'DeviceMessages'
          endpointNames: [
            'messageArchive'
          ]
          isEnabled: true
        }
      ]
      fallbackRoute: {
        source: 'DeviceMessages'
        endpointNames: [
          'events'
        ]
        isEnabled: true
      }
    }
  }
}

I tried removing the message routing block in IoT Hub

endpoints: {
  storageContainers: [
    {
      name: 'messageArchive'
      endpointUri: 'https://messagearchive4631.blob.core.windows.net/'
      containerName: 'iot-test-4631-container'
      batchFrequencyInSeconds: 100
      maxChunkSizeInBytes: 104857600
      encoding: 'Avro'
      fileNameFormat: '{iothub}/{YYYY}/{MM}/{DD}/{HH}/{mm}_{partition}.avro'
      authenticationType: 'identityBased'
    }
  ]
}

and deploy it one time. This deployment works. If I then include the message routing block and deploy it again, then it works as expected.

Is it possible to do this in a single deployment?


Solution

  • I figured it out by myself. I'm using a user-assigned Managed Identity and therfore I was missing this in IoT Hub endpoint storage container configuration:

    authenticationType: 'identityBased'
    identity: {
       userAssignedIdentity: iotIdentity.id
    }
    

    The complete IoT Hub endpoint configuration looks like this

    endpoints: {
      storageContainers: [
        {
          name: 'RawDataStore'
          endpointUri: 'https://${nameRawDataStore}.blob.${environment().suffixes.storage}/'
          containerName: nameIotHub
          batchFrequencyInSeconds: 100
          maxChunkSizeInBytes: 104857600
          encoding: 'Avro'
          fileNameFormat: '{iothub}/{YYYY}/{MM}/{DD}/{HH}/{mm}_{partition}.avro'
          authenticationType: 'identityBased'
          identity: {
            userAssignedIdentity: iotIdentity.id
          }
        }
      ]
    }