In this article, I introduce bicep scripts which provision
- IoT Hub
- with managed identity enabled
- routing to storage account for all telemetry
- disable fallback for routing
- Storage Account (v2)
- disable public access
- allow access only via the IoT Hub managed identity
As you can see, IoT Hub needs to know storage account for routing, and storage account needs to know IoT Hub managed identity, which makes circular reference. So I use bicep module to:
- Create IoT Hub and Storage Account without routing configuration
- Set IAM for storage account by using IoT Hub managed identity
- Set routing feature for IoT Hub to point to the storage account
bicep script
I have four bicep scripts for this.
main.bicep
The main.bicep calls modules by passing parameter. I use dependsOn
to make sure the executing order.
@description('Define the project name or prefix for all objects.')
param projectName string = 'myawesomeproject'
@description('The datacenter to use for the deployment.')
param location string = resourceGroup().location
module iotHubStorage './iotHubStorage.bicep' = {
name: 'iotHubStorageDeployment'
params: {
projectName: projectName
location: location
}
}
module role './role.bicep' = {
name: 'roleDeployment'
params: {
projectName: projectName
IoTHubPrincipalId: iotHubStorage.outputs.iotPrincipalId
}
dependsOn: [iotHubStorage]
}
module iotHubRouting './iotHubRouting.bicep' = {
name: 'iotHubRoutingDeployment'
params: {
projectName: projectName
location: location
}
dependsOn: [role]
}
iotHubStorage.bicep
This bicep create IoT Hub and Storage Account (V2) with minimum settings. As I need managed identity information for next module, use output
to pass the id. The storage account is lock down to allow only IoT Hub.
param projectName string
param location string
@description('The SKU to use for the IoT Hub.')
param skuName string = 'S1'
@description('The number of IoT Hub units.')
param skuUnits int = 1
@description('Partitions used for the event stream.')
param d2cPartitions int = 4
var iotHubName = '${projectName}-IoTHub'
var storageAccountName = '${toLower(projectName)}st'
var storageContainerName = '${toLower(projectName)}results'
resource iotHub 'Microsoft.Devices/IotHubs@2021-07-02' = {
name: iotHubName
location: location
sku: {
name: skuName
capacity: skuUnits
}
identity:{
type: 'SystemAssigned'
}
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: storageAccountName
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties:{
allowBlobPublicAccess: false
allowSharedKeyAccess: false
networkAcls:{
bypass: 'AzureServices'
defaultAction: 'Deny'
resourceAccessRules:[{
resourceId: iotHub.id
tenantId: tenant().tenantId
}]
}
}
}
resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = {
name: '${storageAccountName}/default/${storageContainerName}'
properties: {
publicAccess: 'None'
}
dependsOn: [
storageAccount
]
}
output iotPrincipalId string = iotHub.identity.principalId
role.bicep
I setup IAM for storage account by using generated managed identity of IoT Hub. I set Storage Blob Data Contributor
role for now.
param projectName string
param IoTHubPrincipalId string
var storageAccountName = '${toLower(projectName)}st'
resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
name: storageAccountName
}
//https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
name:'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
scope: storageAccount
}
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
scope: storageAccount
name: guid(storageAccount.id, IoTHubPrincipalId, roleDefinition.id)
properties: {
roleDefinitionId: roleDefinition.id
principalId: IoTHubPrincipalId
principalType: 'ServicePrincipal'
}
}
iotHubRouting.bicep
Finally, I setup routing and custom endpoint pointing to the storage account.
param projectName string
param location string
@description('The SKU to use for the IoT Hub.')
param skuName string = 'S1'
@description('The number of IoT Hub units.')
param skuUnits int = 1
@description('Partitions used for the event stream.')
param d2cPartitions int = 4
var iotHubName = '${projectName}-IoTHub'
var storageAccountName = '${toLower(projectName)}st'
var storageEndpoint = '${projectName}StorageEndpont'
var storageContainerName = '${toLower(projectName)}results'
var storageRouteName = '${projectName}Route'
resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
name: storageAccountName
}
resource iotHub 'Microsoft.Devices/IotHubs@2021-07-02' = {
name: iotHubName
location: location
sku: {
name: skuName
capacity: skuUnits
}
identity:{
type: 'SystemAssigned'
}
properties: {
eventHubEndpoints: {
events: {
retentionTimeInDays: 1
partitionCount: d2cPartitions
}
}
routing: {
endpoints: {
storageContainers: [
{
authenticationType: 'identityBased'
endpointUri: storageAccount.properties.primaryEndpoints.blob
containerName: storageContainerName
fileNameFormat: '{iotHub}/{partition}/{YYYY}/{MM}/{DD}/{HH}/{mm}'
batchFrequencyInSeconds: 60
maxChunkSizeInBytes: 104857600
encoding: 'JSON'
name: storageEndpoint
}
]
}
routes: [
{
name: storageRouteName
source: 'DeviceMessages'
condition: 'true'
endpointNames: [
storageEndpoint
]
isEnabled: true
}
]
}
messagingEndpoints: {
fileNotifications: {
lockDurationAsIso8601: 'PT1M'
ttlAsIso8601: 'PT1H'
maxDeliveryCount: 10
}
}
enableFileUploadNotifications: false
cloudToDevice: {
maxDeliveryCount: 10
defaultTtlAsIso8601: 'PT1H'
feedback: {
lockDurationAsIso8601: 'PT1M'
ttlAsIso8601: 'PT1H'
maxDeliveryCount: 10
}
}
}
}
Run bicep
You can use az
cli to run it.
az login
az deployment group create --resource-group <your resource group> --template-file .\main.bicep
Once deployment is done, confirm telemetry routed to storage account.
Summary
It was a bit tricky to handle circular reference and I am not 100% sure this is correct way to solve it, but it works as expected. Please give me any feedback if you know better way to achieve the same!!
Reference
Find out Role Definition Id:
https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
Top comments (0)