My case is that I have a list of companies, as well as a list of queues that I want to pair together in my bicep file. I want to use the result to add queues to an service bus namespace.
This is an example of the queue array:
`var queues = [
'first-queue'
'second-queue'
'third-queue'
]``
And this is an example of the company array:
`var companies = [
'apple'
'intel'
'blizzard'
]``
I would like to loop through the company list with bicep syntax and insert the company name before each queue. In this case i want to have a result like bwlow:
`var res = [
'apple-first-queue'
'apple-second-queue'
'apple-third-queue'
'intel-first-queue'
'intel-second-queue'
'intel-third-queue'
'blizzard-first-queue'
'blizzard-second-queue'
'blizzard-third-queue'
]`
I've tried a few different ways but can't get it to work. This is my latest attempt below where I get an error message regarding the syntax of "queuesToCreate" in the form of* "Directly referencing a resource or module collection is not currently supported. Apply an array indexer to the expression"*.
Does anyone see what I'm doing wrong here and can point me in the right direction?
`// ## Service bus multiple companies module ## //
module queuesToCreate 'service-bus-loop.bicep' = [for company in companies: {
name: 'multipleQueues-${company}'
params: {
company: company
}
}]
`
``// ## Service bus namespace ## //
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces#2022-01-01-preview' = {
name: serviceBusNamespaceName
location: location
sku: {
name: skuName
}
}
// ## Queues ## //
resource queues 'Microsoft.ServiceBus/namespaces/queues#2022-01-01-preview' = [for queueName in queuesToCreate: {
parent: serviceBusNamespace
name: queueName
properties: {
maxDeliveryCount: 1
lockDuration: 'PT5M'
}
}]`
This service-bus-loop.bicep file look like below and just returns an array as output
param company string
`// Create an array with the names of the queues
var queues = [
'${company}-queue'
'${company}-queue'
'${company}-queue'
]
output res array = queues`
queuesToCreate variable has in above case an syntax error like "Directly referencing a resource or module collection is not currently supported. Apply an array indexer to the expression."
As suggested in the comment section, you could always use a module to create all the queues for a specific company.
// service-bus-queues.bicep
param serviceBusNamespaceName string
param company string
param queues array
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces#2022-01-01-preview' existing = {
name: serviceBusNamespaceName
}
resource queuesToCreate 'Microsoft.ServiceBus/namespaces/queues#2022-01-01-preview' = [for queue in queues: {
parent: serviceBusNamespace
name: '${company}-${queue}'
properties: {
maxDeliveryCount: 1
lockDuration: 'PT5M'
}
}]
Then from you main, loop through companies.
// main.bicep
param serviceBusNamespaceName string
param location string
param skuName string
var queues = [
'first-queue'
'second-queue'
'third-queue'
]
var companies = [
'apple'
'intel'
'blizzard'
]
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces#2022-01-01-preview' = {
name: serviceBusNamespaceName
location: location
sku: {
name: skuName
}
}
module queuesToCreate 'service-bus-queues.bicep' = [for company in companies: {
name: 'multipleQueues-${company}'
params: {
serviceBusNamespaceName: serviceBusNamespace.name
company: company
queues: queues
}
}]
Related
I am trying to create a service bus and topic (many topics and their subscriptions) with the azure bicep.
I have main bicep like below
param topics array;
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces#2018-01-01-preview' = {
name: serviceBusNamespaceName
location: location
sku: {
name: skuName
}
}
module topicSubscription './sb_topic.bicep' = [for topic in topics: {
name: 'topicSubscription${topic}'
params: {
topic: topic
}
}]
module file looks like
resource sbTopics 'Microsoft.ServiceBus/namespaces/topics#2022-01-01-preview' = {
name: topic.name
parent: ??
properties: topic.properties
resource symbolicname 'subscriptions#2022-01-01-preview' = [for subscription in topic.subscriptions: {
name: 'string'
properties: {}
}]
}
How can pass the parent serviceBusNamespace resource as parent to the child resource inside the module?
Kindly suggest..
In the module reference the namespace with an 'existing' declaration. Something like this
param namespaceName string
resource serviceBusNamespace 'Microsoft.ServiceBus/namespaces#2018-01-01-preview' existing = {
name: namespaceName
}
resource sbTopics 'Microsoft.ServiceBus/namespaces/topics#2022-01-01-preview' = {
name: topic.name
parent: serviceBusNamespace
properties: topic.properties
resource symbolicname 'subscriptions#2022-01-01-preview' = [for subscription in topic.subscriptions: {
name: 'string'
properties: {}
}]
}
I have a bicep file that accepts a param topic object object that looks like this:
name: 'topic name'
subscriptions: [
{
name: 'subscriptionName'
ruleName: 'name of the rule'
}
]
Next I am going to create the resources:
resource servicebus 'Microsoft.ServiceBus/namespaces#2021-11-01' existing = {
name: 'products'
}
resource topicResource 'Microsoft.ServiceBus/namespaces/topics#2021-11-01' = {
parent: servicebus
name: topic.topicName
}
resource subscriptions 'Microsoft.ServiceBus/namespaces/topics/subscriptions#2021-11-01' = [for obj in topic.subscriptions: {
parent: topicResource
name: obj.name
}]
resource rules 'Microsoft.ServiceBus/namespaces/topics/subscriptions/rules#2021-11-01' = [for (obj, i) in topic.subscriptions: {
parent: subscriptions[i]
name: obj.ruleName
properties: {
filterType: 'CorrelationFilter'
correlationFilter: {
label: obj.ruleName
}
}
}]
In some situations I don't want to create the rules resource. This is based on the ruleName property. If this property is empty, I don't want to create the resource.
But when I try to apply this condition, I get an error message:
{"code": "InvalidTemplate", "target": "[...]", "message": "Deployment template validation failed: 'The template resource '[..]' for type 'Microsoft.ServiceBus/namespaces/topics/subscriptions/rules' at line '1' and column '1763' has incorrect segment lengths. A nested resource type must have identical number of segments as its resource name. A root resource type must have segment length one greater than its resource name. Please see https://aka.ms/arm-template/#resources for usage details.'.", "additionalInfo": [{"type": "TemplateViolation", "info": {"lineNumber": 1, "linePosition": 1763, "path": "properties.template.resources[2].type"}}]}
What I think this error message is telling me, is that you need the same number of iterations as the parent, which in this case is subscriptions and because of the loop condition of the 'child' resource, the number now is decreased by one (which is odd to me, because why not continue with the next item in the array and increase the index with one... but ok).
So my question is, how can I skip the resource creation when ruleName is empty?
This is what I've tried and what gives the error above:
resource rules 'Microsoft.ServiceBus/namespaces/topics/subscriptions/rules#2021-11-01' = [for (obj, i) in topic.subscriptions: if (!empty(obj.ruleName)) {
The condition if (!empty(obj.ruleName)) will be converted to an ARM condition property.
Even if the resource won't be deployed it is still present in the generated ARM.
This mean the name property in the generated ARM is missing a segment:
topic-name/subscription-name/empty-rule-name
You will have to add the same condition (as a workaround) on the name property as well:
name: !empty(obj.ruleName) ? obj.ruleName : 'whatever'
In your code, it should look like that:
resource rules 'Microsoft.ServiceBus/namespaces/topics/subscriptions/rules#2021-11-01' = [for (obj, i) in topic.subscriptions: if (!empty(obj.ruleName)) {
parent: subscriptions[i]
name: !empty(obj.ruleName) ? obj.ruleName : 'whatever'
properties: {
filterType: 'CorrelationFilter'
correlationFilter: {
label: obj.ruleName
}
}
}]
the code below is my attempt at creating a template allowing easy deployment of a static Alert rule to multiple Azure subscriptions. To achieve this, I am looping through an array containing the subscriptions I want to deploy to, in order to update the name of the alert and scope of the deployment.
I'm encountering the following error when I attempt to run a deployment using my code:
{"code":"InvalidTemplateDeployment","details":[{"code":"BadRequest","target":"/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/Testing_RG/providers/Microsoft.Insights/metricAlerts/ExampleName_mytestalert","message":"Error converting value \"/subscriptions/11111111-1111-1111-1111-111111111111\" to type 'System.Collections.Generic.IList`1[System.String]'. Path 'scopes'."}],"message":"The template deployment 'Microsoft.Template-20220704132314' is not valid according to the validation procedure. The tracking id is 'fe912e5c-5fd5-4180-b24a-f49fa486b704'. See inner errors for details."}
Any pointers as to how I can fix this error would be much appreciated!
My code is as follows:
#description('Defines how often the alert criteria is evaluated')
#allowed([
'PT1M'
'PT5M'
'PT15M'
'PT30M'
'PT1H'
])
param evaluationFrequency string = 'PT5M'
#description('Defines the size of the window over which collected values are aggregated.')
#allowed([
'PT5M'
'PT15M'
'PT30M'
'PT1H'
])
param windowSize string = 'PT30M'
#description('Defines the threshhold value at which the alert will trigger')
param threshold int = 90
#description('Defines the resource type to be evaluated')
param metricNamespace string = 'microsoft.compute/virtualmachines'
#description('Defines the metric signal to be monitored. E.g. Percentage CPU, Disk Read Bytes etc.')
param metricName string = 'Percentage CPU'
#description('The operator used to compare the metric value against the threshhold')
#allowed([
'GreaterThan'
'GreaterThanOrEqualTo'
'LessThanOrEqualTo'
'LessThan'
])
param operator string = 'GreaterThan'
#description('Defines the aggregation function to apply to datapoints')
#allowed([
'Average'
'Maximum'
'Minimum'
'Total'
'Count'
])
param timeAggregation string = 'Average'
#description('Defines the subscriptions to which alerts will be scoped. An alert can only be scoped to a single subscription. In the event of multiple subscriptions being selected, an alert will be created for each.')
param subscriptions array = [
{
scope: '/subscriptions/11111111-1111-1111-1111-111111111111'
name: 'ExampleName'
}
{ scope: '/subscriptions/22222222-2222-2222-2222-222222222222'
name: 'ExampleName2'
}
{
scope: '/subscriptions/33333333-3333-3333-3333-333333333333'
name: 'ExampleName3'
}
{
scope: '/subscriptions/44444444-4444-4444-4444-444444444444'
name: 'ExampleName4'
}
]
#description('Defines the action group(s) to be notified when the alert triggers')
param actionGroups_example string = '/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/Example-Example-UKSouth/providers/microsoft.insights/actionGroups/ExampleActionGroup'
resource alertName_resource 'microsoft.insights/metricAlerts#2018-03-01' = [ for (subscription, i) in subscriptions: {
name: '${subscription.name}_mytestalert'
location: 'global'
properties: {
criteria: {
allOf: [
{
threshold: threshold
name: 'Metric1'
metricNamespace: metricNamespace
metricName: metricName
operator: operator
timeAggregation: timeAggregation
criterionType: 'StaticThresholdCriterion'
}
]
'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'
}
enabled: true
evaluationFrequency: evaluationFrequency
scopes: subscription.scope
severity: 3
windowSize: windowSize
autoMitigate: true
targetResourceType: 'microsoft.compute/virtualMachines'
targetResourceRegion: 'uksouth'
actions: [
{
actionGroupId: actionGroups_example
}
]
}
}]
output alertnameoutput array = [for (subscription, i) in subscriptions: {
myalertname: alertName_resource[i].name
}]
Looking at the documentation:
scopes: the list of resource id's that this metric alert is scoped to. string[] (required)
So in you case you should have something like that:
scopes: [
subscription.scope
]
In Bicep I am creating an array of origin groups with a for loop. I want to be able to reference specific values in this array as a parent for another resource.
I'm creating the array like this:
var originGroups = [
{
name: 'firstOriginGroup'
}
{
name: 'secondOriginGroup'
}
]
resource origin_groups 'Microsoft.Cdn/profiles/originGroups#2021-06-01' = [for group in originGroups :{
name: group.name
other properties...
}
Then I have an array of origin groups, "Microsoft.Cdn/profiles/originGroups#2021-06-01[]". I then want to make a origin with a parent. secondOriginGroup.
resource my_origin 'Microsoft.Cdn/profiles/originGroups/origins#2021-06-01' = {
name: 'myOrigin'
parent: origin_groups[ //select specific name here ]
other parameters...
}
Is it posible in bicep to select a specific indexer here or am i only able to index on ordinal numbers? Am I able to do, where name == 'secondOriginGroup'?
You could loop through the originGroups again and filter on 'secondOriginGroup':
resource my_origin 'Microsoft.Cdn/profiles/originGroups/origins#2021-06-01' = [for (group, i) in originGroups: if (group.name == 'secondOriginGroup') {
name: 'myOrigin'
parent: origin_groups[i]
other parameters...
}]
You'll need to use indexes in your loop, like this:
var originGroups = [
{
name: 'firstOriginGroup'
}
{
name: 'secondOriginGroup'
}
]
resource origin_groups 'Microsoft.Cdn/profiles/originGroups#2021-06-01' = [for (group, index) in originGroups :{
name: group.name
other properties...
}
resource my_origin 'Microsoft.Cdn/profiles/originGroups/origins#2021-06-01' = {
name: 'myOrigin'
parent: origin_groups[index]
other parameters...
}
Normally when creating Azure resources through Bicep modules I would have 2 files. One file designated to hold the parameterized resource and another file, the main file, which will consume that module.
As an example for creating an action group my resource file looks like:
action-group.bicep
param actionGroupName string
param groupShortName string
param emailReceivers array = []
// Alerting Action Group
resource action_group 'microsoft.insights/actionGroups#2019-06-01' = {
name: actionGroupName
location: 'global'
tags: {}
properties: {
groupShortName: groupShortName
enabled: true
emailReceivers: emailReceivers
}
}
This resource is then consumed as a module in the main file, main.bicep:
// Alerting Action Group
module actionGroup '../modules/alerts/alert-group.bicep' = {
name: 'action-group-dply'
params: {
actionGroupName: actionGroupName
groupShortName: actionGroupShortName
emailReceivers: [
{
name: '<Name of email receivers>'
emailAddress: alertEmailList
}
]
}
}
My pipeline references main.bicep and will deploy the listed resources in the file. My question is, is there a way to add a third file in the mix? One file to still hold the parameterized resource, one file to hold the associated resource modules, and the main.bicep file. The idea is to create various alerts throughout my existing resources but I don't want to add a ton of modules to main.bicep as it will quickly increase the complexity and amount of code in this file.
Is there a way that I can have this file of modules, and reference the entire file in main.bicep to still deployment from the original pipeline?
As an example:
alerts.bicep
param metricAlertsName string
param description string
param severity int
param enabled bool = true
param scopes array = []
param evaluationFrequency string
param windowSize string
param targetResourceRegion string = resourceGroup().location
param allOf array = []
param actionGroupName string
var actionGroupId = resourceId(resourceGroup().name, 'microsoft.insights/actionGroups', actionGroupName)
resource dealsMetricAlerts 'Microsoft.Insights/metricAlerts#2018-03-01' = {
name: metricAlertsName
location: 'global'
tags: {}
properties: {
description: description
severity: severity
enabled: enabled
scopes: scopes
evaluationFrequency: evaluationFrequency
windowSize: windowSize
targetResourceRegion: targetResourceRegion
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
allOf: allOf
}
actions: [
{
actionGroupId: actionGroupId
}
]
}
alert-modules.bicep
// Function/Web Apps 403 Error
module appServicePlan403Errors '../modules/alerts/alerts.bicep' = {
// Alert Logic
}
// Function/Web Apps 500 Error
module appServicePlan500Errors '../modules/alerts/alerts.bicep' = {
// Alert Logic
}
main.bicep
// Some reference to alert-modules.bicep so when the pipeline runs and looks for main.bicep, it will still deploy all the resources
You can call the module multiple times (in main.bicep or a module) using looping:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/loops
e.g.
param alertsCollection array = [
{
actionGroupName: 'group1'
groupShortName: 'g1'
emailReceivers: []
}
{
actionGroupName: 'group2'
groupShortName: 'g2'
emailReceivers: [
'foo#bar.com'
'bar#baz.com'
]
}
]
module alerts '../modules/alerts/alert-group.bicep' = [for alert in alertsCollection): {
name: '${alert.actionGroupName}'
params: {
actionGroupName: alert.actionGroupName
groupShortName: alert.groupShortName
emailReceivers: alert.emailReceivers
}
}]
You could simplify the params passing via:
module alerts '../modules/alerts/alert-group.bicep' = [for alert in alertsCollection): {
name: '${alert.actionGroupName}'
params: alert
}]
But you need to be really strict on the schema of the alertsCollection parameter...
That help?