How to assign correct roles on Service Bus entities to Azure functions managed identity with Bicep?
Asked Answered
P

2

5

I have an Azure Functions project, with a Function that uses a Service Bus binding (that is used to Listen on a subscription and to Send to a topic).

The Azure functions deployment is running under a managed identity. And as we want to deploy everything automatically, using Azure Bicep, I want to automatically give the correct role assignment on the Service Bus namespace (or entities) for that managed identity, in an Azure Bicep file.

But I don't seem to find out how to do that. Would someone be able to indicate the correct bicep snippet to create the role assignments Azure Service Bus Data Receiver and Azure Service Bus Data Sender on a Service Bus entity for a specific managed identity?

(and even better : how can I find that out for myself, knowing that I am rather new to bicep)

Best regards

Publia answered 16/7, 2022 at 13:15 Comment(0)
T
6

Documentation to create RBAC using Bicep can be found here.
Azure built-in roles can be found here

So for ServiceBus and managed identity, you could create a module that looks like that

// servicebus-role-assignment.bicep

param serviceBusName string
param principalId string

@allowed([
  '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' // Azure Service Bus Data Receiver
  '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39' // Azure Service Bus Data Sender
])
param roleId string


// Get a reference to servicebus namespace
resource servicebus 'Microsoft.ServiceBus/namespaces@2022-01-01-preview' existing = {
  name: serviceBusName
}

// Grant permissions to the principalID to specific role to servicebus
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  name: guid(servicebus.id, roleId, principalId)
  scope: servicebus
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId)
    principalId: principalId
    principalType: 'ServicePrincipal'
  }
}

If you are using a user-assigned identity, you could invoke this module once the identity has been created:

param location string = resourceGroup().location
param identityName string
param serviceBusName string

// Create the identity
resource identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
  name: identityName
  location:location
}

// Do the role assignment
module serviceBusRoleAssignment 'servicebus-role-assignment.bicep' = {
  name: 'servicebus-role-assignment'
  params: {
    serviceBusName: serviceBusName
    roleId: '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' // Azure Service Bus Data Receiver    
    principalId: identity.properties.principalId
  }
}

If you are using a system-assigned identity, you would need to first create the function app:

param location string = resourceGroup().location
param functionAppName string
param serviceBusName string
...

// Create the function app
resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
  name: functionAppName
  identity: {
    type: 'SystemAssigned'
  }
  ...
}

// Do the role assignment
module serviceBusRoleAssignment 'servicebus-role-assignment.bicep' = {
  name: 'servicebus-role-assignment'
  params: {
    serviceBusName: serviceBusName
    roleId: '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' // Azure Service Bus Data Receiver    
    principalId: functionApp.identity.principalId
  }
}
Thorwald answered 16/7, 2022 at 21:4 Comment(5)
appreciated, Thomas! spot on answerPublia
Here you are assigning RBAC role to the namespace itself which will get inherited to every single Queue under that namespace. Is it not possible to assign RBAC to just the Queue itself with Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview? I am trying to do that but I get a validate error in Bicep.Luxurious
Yes you should be able to do that. Just the scope of the role assignment is different.Thorwald
I get this if I do that: A resource's scope must match the scope of the Bicep file for it to be deployable. You must use modules to deploy resources to a different scope.bicep(BCP139)Luxurious
learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/…Luxurious
L
1

If you want to assign a role at the Service Bus Topic level (or Queue level) here is how it can be done via the use of modules and the scope keyword.

The example below assigns an Azure Service Bus Sender role to a Service Bus Topic for a Web App that has been created with System Identity. The Service Bus Namespace and its child resource Topic is in a different resource group. The Web App is another resource group being deployed via the 'az deployment group' command.

main.bicep:

// Assign RBAC role to Service Bus Topic - Use modules if you want to deploy an extension resource (the role assignment is an extension resource type) 
// with the scope set to a resource in a different resource group, see more here https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scope-extension-resources
    module roleAssignmentModule 'Modules/roleAssignments.bicep' = {
      name: 'roleAssignmentsModule' // Azure Portal Deployments Inputs and Outputs shown under RG that contains the Service Bus Names
      scope: resourceGroup(differentResourceGroupName)
      params: {
        principalId: principalId
        serviceBusName: serviceBusName
        serviceBusTopicName: serviceBusTopicName
      }
    }

roleAssignments.bicep:

    @description('The principalId that will be assigned to the role')
    param principalId  string
    
    @description('Service Bus Namespace that contains the Service Bus Topic where RBAC role will be assigned to')
    param serviceBusName string
    
    @description('Service Bus Topic name where RBAC role will be assigned to')
    param serviceBusTopicName string
    
    @description('This is the built-in Azure Service Bus Data Sender role. See https://learn.microsoft.com/en-gb/azure/role-based-access-control/built-in-roles#azure-service-bus-data-sender')
    resource azureServiceBusDataSenderRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
      scope: subscription()
      name: '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39'
    }
    
    
    // Reference to an existing Service Bus Namespace resource
    resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = {
      name: serviceBusName
    }
    
    // Get a reference to an existing Service Bus Topic
    resource serviceBusTopic 'Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview' existing = {
      name: serviceBusTopicName
      parent: serviceBusNamespace
    }
    
    // Assign RBAC role 'Azure Service Bus Data Sender' to the Service Bus Topic
    resource roleAssignment_AzureServiceBusDataSender 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
      name: guid(subscription().id, principalId, serviceBusTopic.id, azureServiceBusDataSenderRoleDefinition.id) 
      scope: serviceBusTopic  // Role is assigned at the Topic level. If scope property is omitted, then role is assigned at the Service Bus Namespace level and inherited to all child Topics. We don't want that
      properties: {
        principalId: principalId
        roleDefinitionId: azureServiceBusDataSenderRoleDefinition.id 
        principalType: 'ServicePrincipal' 
      }
    }
Luxurious answered 30/10, 2023 at 15:51 Comment(2)
One issue I have is that the guid() function seems to generate a different guid for each machine it is run on. So if I'm deploying bicep on my dev box via az deployment group create then the role gets created OK the first time, and it works OK for any subsequent runs on my dev box. But then when I later deploy my bicep via Azure DevOps Pipeline, we get an error because RoleAssignmentExists. So I have to manually delete the role in the Azure Portal, and let the Pipeline create it. Which breaks it on my dev box :-(Cipolin
Looks like my comment above is still open at github.com/Azure/bicep/issues/5694Cipolin

© 2022 - 2025 — McMap. All rights reserved.