Search code examples
azureazure-devopsdevopsazureservicebusazure-bicep

Bicep how to refer a parent in module when using loop


So I'm trying to write something that will allow me to create topics and subscriptions in a loop and while I think I've achieved that thanks to code founded here, I'm facing some problems and I'n not sure how to resolve them. It looks like sometimes bicep fails because it try to create subscription when topics creation is not yet finished. When I re-run my pipeline, it works just fine. I guess this is the reason:

https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/linter-rule-use-parent-property

The error I get is: The incoming request is not recognized as a namespace policy put request. ttps://aka.ms/sbResourceMgrExceptions. TrackingId:e1d2c50d-fe92-43d8-abfe-8eb2c1917a70_G5, SystemTracker:ihdev-nonprod-servicebus.servicebus.windows.net:dev-sb-topic-test/subscriptions/dev-sb-sub-test, Timestamp:2024-04-08T13:33:29 (Code: MessagingGatewayNotFoundStatusCode

I'm not sure how to avoid this error. Is there a way to create particular subscription only if corresponding topic was created? I guess I cannot simply add dependsOn[i] as I would have to change from for topic in topicsAndSubscriptions to something like for (topic, i) in topicsAndSubscriptions but maybe I'm wrong?

Main.bicepparam:

using './main.bicep'

param serviceBusNamespaceName = '#{SBUS_NAMESPACE_NAME}#'
param topicsAndSubscriptions = [
  {
    name: 'dev-sb-top-failure'
    subscriptions: [
      'dev-sb-sub-failure'
    ]
  }
  {
    name: 'xrm-dev-sb-report-completion'
    subscriptions: [
      'dev-sb-sub-report-completion'
    ]
  }
  {
    name: 'dev-sb-top-completion'
    subscriptions: [
      'dev-sb-sub-completion'
    ]
  }
  {
    name: 'dev-sb-top-discharge'
    subscriptions: [
      'dev-sb-sub-discharge'
    ]
  }
]

Here is the code for main.bicep

@description('Name of the Service Bus namespace')
param serviceBusNamespaceName string

@description('Location for all resources.')
param location string = resourceGroup().location
param topicsAndSubscriptions array

resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-01-01-preview' = {
  name: serviceBusNamespaceName
  location: location
  sku: {
    name: 'Premium'
  }
  properties: {}
}

resource serviceBusTopic 'Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview' = [
  for topic in topicsAndSubscriptions: {
    parent: serviceBusNamespace
    name: topic.name
    properties: {}
  }
]

module subs 'modules/subscription.bicep' = [
  for topic in topicsAndSubscriptions: {
    name: '${topic.name}-subs'
    params: {
      servicebusNamespaceName: serviceBusNamespaceName
      topicName: topic.name
      subscriptions: topic.subscriptions
    }
  }
]

module file:

param servicebusNamespaceName string
param topicName string
param subscriptions array = ['asubscription', 'anotherone']

resource sub 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview' = [
  for i in subscriptions: {
    name: '${servicebusNamespaceName}/${topicName}/${i}'
    properties: {}
  }
]

Solution

  • Implicit dependency is recommended in your circumstance

    main.bicep

    @description('Name of the Service Bus namespace')
    param serviceBusNamespaceName string
    
    @description('Location for all resources.')
    param location string = resourceGroup().location
    param topicsAndSubscriptions array
    
    resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-01-01-preview' = {
      name: serviceBusNamespaceName
      location: location
      sku: {
        name: 'Premium'
      }
      properties: {}
    }
    
    resource serviceBusTopic 'Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview' = [ for topic in topicsAndSubscriptions: {
        parent: serviceBusNamespace
        name: topic.name
        properties: {}
      }
    ]
    
    module subs 'modules/subscription.bicep' = [ for (topic, i) in topicsAndSubscriptions: {
        name: '${topic.name}-subs'
        params: {
          servicebusNamespaceName: serviceBusNamespaceName
          topicName: serviceBusTopic[i].name
          subscriptions: topic.subscriptions
        }
      }
    ]
    
    

    subscription.bicep

    param servicebusNamespaceName string
    param topicName string
    param subscriptions array = ['asubscription', 'anotherone']
    
    resource sub 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview' = [ for i in subscriptions: {
        name: '${servicebusNamespaceName}/${topicName}/${i}'
        properties: {}
      }
    ]
    
    

    main.bicepparam

    using './main.bicep'
    
    param serviceBusNamespaceName = '#{SBUS_NAMESPACE_NAME}#'
    param topicsAndSubscriptions = [
      {
        name: 'dev-sb-top-failure'
        subscriptions: [
          'dev-sb-sub-failure'
        ]
      }
      {
        name: 'xrm-dev-sb-report-completion'
        subscriptions: [
          'dev-sb-sub-report-completion'
        ]
      }
      {
        name: 'dev-sb-top-completion'
        subscriptions: [
          'dev-sb-sub-completion'
        ]
      }
      {
        name: 'dev-sb-top-discharge'
        subscriptions: [
          'dev-sb-sub-discharge'
        ]
      }
    ]
    

    topicName: serviceBusTopic[i].name here will allow topic deployment before corresponding subscription. I have test it, works well.


    update to modules

    main.bicep

    @description('Name of the Service Bus namespace')
    param serviceBusNamespaceName string
    
    @description('Location for all resources.')
    param location string = resourceGroup().location
    param topicNames array
    param subscriptionNames array
    
    resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces@2022-01-01-preview' = {
      name: serviceBusNamespaceName
      location: location
      sku: {
        name: 'Premium'
      }
      properties: {}
    }
    
    module topics 'modules/topic.bicep' = {
      name: 'topicDeployment'
      params: {
        serviceBusNamespaceName: serviceBusNamespaceName
        topicNames: topicNames
      }
      dependsOn:[
        serviceBusNamespace
      ]
    }
    
    module subscriptions 'modules/subscription.bicep' = {
      name: 'subsDeployment'
      params: {
        servicebusNamespaceName: serviceBusNamespaceName
        subscriptionNames: subscriptionNames
        topicNames: topicNames
      }
      dependsOn:[
        serviceBusNamespace
        topics
      ]
    }
    
    

    topic.bicep

    param serviceBusNamespaceName string
    
    param topicNames array
    
    resource topicModule 'Microsoft.ServiceBus/namespaces/topics@2022-10-01-preview' = [for (item, i) in topicNames: {
      name: '${serviceBusNamespaceName}/${item}'
    }]
    
    

    subscription.bicep

    param servicebusNamespaceName string
    param topicNames array
    param subscriptionNames array
    
    resource sub 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2022-10-01-preview' = [ for (item, i) in subscriptionNames: {
        name: '${servicebusNamespaceName}/${topicNames[i]}/${item}'
        properties: {}
      }
    ]
    
    

    main.bicepparam

    using './main.bicep'
    
    param serviceBusNamespaceName = 'wbevent'
    
    param topicNames = [
      'dev-sb-top-failure'
      'xrm-dev-sb-report-completion'
      'dev-sb-top-completion'
      'dev-sb-top-discharge'
    ]
    
    param subscriptionNames = [
      'dev-sb-sub-failure'
      'dev-sb-sub-report-completion'
      'dev-sb-sub-completion'
      'dev-sb-sub-discharge'
    ]