0

I am trying to assign RBAC role 'Azure Service Bus Data Receiver' to an Azure Service Bus Queue for a Function App by using Bicep. I have done this before just fine with Storage Account Queues but this is giving me some problems now with the Service Bus Queues.

Here is my Bicep so far:

var globalServiceBusName = 'sbns-global-nonprod'
var globalServiceBusQueueName = 'sbq-queue01'

resource serviceBusReceiverRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
  scope: subscription()
  name: '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' // Azure Service Bus Data Receiver
}

// Get a reference to a Service Bus in another Resource Group
resource globalServiceBus 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = {
  name: globalServiceBusName
}

// Get a reference to a Service Bus Queue in another Resource Group
resource globalServiceBusQueue 'Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview' existing = {
  parent: globalServiceBus
  name: globalServiceBusQueueName
}

// Assign RBAC role 'Azure Service Bus Data Receiver' to the Service Bus queue
resource RBACAzureServiceBusDataReceiver 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resourceGroup().id, functionApp.id, globalServiceBusQueue.id, serviceBusReceiverRoleDefinition.id) 
  scope: globalServiceBusQueue
  properties: {
    principalId: functionApp.identity.principalId 
    roleDefinitionId: serviceBusReceiverRoleDefinition.id 
    principalType: 'ServicePrincipal' 
  }
}

The ARM validation goes fine, but when it deploys it gives me the error:

"Can not perform requested operation on nested resource. Parent resource 'sbns-global-nonprod' not found."

When I output the names of the resources globalServiceBus with "globalServiceBus.name" and globalServiceBusQueue with "globalServiceBusQueue.name", I can see the actual names which means the Bicep has a reference to them.

Edit: This code works when I move the Service Bus and its queue to the same Resource Group where my Function App resides. So all resources are in 1 RG. Then it works. My desire is to have 1 shared RG where shared resources can be placed, like a Service Bus in Premium Tier, which can be used by Queue receivers like Function App from other RGs.

EDIT: I have tried this code as well:

resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = {
  scope: resourceGroup(globalResourceGroupName)
  name: globalServiceBusName
}

// Get a reference to global Service Bus Queue used for User Migration
resource serviceBusQueue 'Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview' existing = {
  name: globalServiceBusQueueName
  parent: serviceBusNamespace
}

// Assign RBAC role 'Azure Service Bus Data Receiver' to the Service Bus queue
resource RBACAzureServiceBusDataReceiver 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resourceGroup().id, functionApp.id, serviceBusQueue.id, serviceBusReceiverRoleDefinition.id) 
  scope: serviceBusQueue // throws error here: 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)
  properties: {
    principalId: functionApp.identity.principalId 
    roleDefinitionId: serviceBusReceiverRoleDefinition.id 
    principalType: 'ServicePrincipal'
  }
}

But I get at an error: "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)"

Oliver Nilsen
  • 1,017
  • 2
  • 12
  • 32
  • Are you sure the namespace `sbns-global-nonprod` exists ? Can you check `globalServiceBus.id` and make sure you are pointing to the right subscription and resource group ? – Thomas Apr 27 '23 at 19:12
  • It does exist as I can use the resource and display its properties in outputs. I can as I said display its name with globalServiceBus.name just fine. This indicates to me that my reference to it is correct and that it works. – Oliver Nilsen Apr 27 '23 at 19:35
  • So you confirmed that the subscription id and resource group name in the resourceid are correct ? – Thomas Apr 27 '23 at 20:21
  • If they won’t correct I wouldn’t be having any properties displayed in the outputs, which I do have so it means the reference to it is correct and can be found. – Oliver Nilsen Apr 28 '23 at 05:45
  • this code work when I move the Service Bus to the same Resource Group where my Function App is in. – Oliver Nilsen Apr 28 '23 at 07:52
  • you could just change the scope for the reference of the servicebus to another RG. – Thomas Apr 28 '23 at 19:32
  • What ist he scope of your deployment file? I believe as eluded to the issue is the scope on the existing is different then your deployment. Passing the scope property into the existing statement should resolve this: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/existing-resource#different-scope – DreadedFrost Apr 30 '23 at 02:41

1 Answers1

0

This needs to be done with a module. From Microsoft docs: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scope-extension-resources

*Use modules if you want to deploy an extension resource with the scope set to a resource in a different resource group. The role assignment is an extension resource type.

My main.bicep:

@description('Global Resource Group containing Azure resources shared across my services')
param globalRGname string     // contains Azure Service Bus Namespace

// Used to create a Function App and output its principalId which will be used as input to another module
module functionAppModule 'Modules/funcapp.bicep' = {
  name: 'functionAppModule'
  params: {
    environmentName: environmentName
    location: location
    globalResourceGroupName: globalRGname
  }
}

Here is the call in main.bicep that sets the scope to a Global Resource Group where my Service Bus Namespace is

// Assign RBAC role to Service Bus Queue
module roleAssignmentModule 'Modules/serviceBusQueue-roleassignment.bicep' = {
  name: 'addAzureServiceBusDataReceiverRoleModule'
  scope: resourceGroup(globalRGname)
  params: {
    funcAppPrincipalId: functionAppModule.outputs.funcAppPrincipalId
    serviceBusName: globalServiceBusName
    serviceBusQueueName: globalServiceBusQueueName
  }
}

My module serviceBusQueue-roleassignment.bicep to set RBAC role:

@description('The principalId of the Function App that will be used in RBAC assignment')
param funcAppPrincipalId  string

@description('Service Bus Namespace that contains the Service Bus Queue where RBAC role will be assigned to')
param serviceBusName string

@description('Service Bus Queue name where RBAC role will be assigned to')
param serviceBusQueueName string


@description('This is the built-in Azure Service Bus Data Receiver role. See https://learn.microsoft.com/en-gb/azure/role-based-access-control/built-in-roles#azure-service-bus-data-receiver')
resource serviceBusReceiverRoleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
  scope: subscription()
  name: '4f6d3b9b-027b-4f4c-9142-0e5a2a2247e0' // Azure Service Bus Data Receiver
}

// Creating a symbolic name for an existing resource
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2021-11-01' existing = {
  name: serviceBusName
}

// Get a reference to a global Service Bus Queue where RBAC will be applied to
resource serviceBusQueue 'Microsoft.ServiceBus/namespaces/queues@2022-10-01-preview' existing = {
  name: serviceBusQueueName
  parent: serviceBusNamespace
}

// Assign RBAC role 'Azure Service Bus Data Receiver' to the Service Bus queue
resource RBACAzureServiceBusDataReceiver 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(resourceGroup().id, serviceBusQueue.id, serviceBusReceiverRoleDefinition.id) 
  scope: serviceBusQueue  // Permission will be set ONLY at the SB Queue level. If scope property is omitted then it is set for the SB Namespace and inherited to all child SB Queues.
  properties: {
    principalId: funcAppPrincipalId 
    roleDefinitionId: serviceBusReceiverRoleDefinition.id
    principalType: 'ServicePrincipal'
  }
}

This works and the Azure Service Bus Data Receiver RBAC role is only applied to the Service Bus Queue for my Function App.

Oliver Nilsen
  • 1,017
  • 2
  • 12
  • 32