bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Ability to treat a retrieved param value as param name

Open michaell104 opened this issue 7 months ago • 2 comments

Suppose I'm using the network-security-group AVM module with the main.bicep

module someSNGResource '../module/network-security-group/main.bicep' = {
  name: 'nsg-1'
  params: {
    name: 'nsg-webapp-subnet
    location: westus2
    securityRules: [for rule in rules: {
        name: rule.name
        properties: {
         <omitted>
          sourceApplicationSecurityGroupResourceIds: [
            rule.sourceasg
          ]
          destinationApplicationSecurityGroupResourceIds: [
            rule.destinationasg
          ]
        }
      }
    ]
  }
}

In my bicepparam file I would need to have the following

param rules = {
    {
<ommited>
      sourceasg: '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb1'
      destinationasg: '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb2'
    }
    {
<ommited>
      sourceasg: '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb2'
      destinationasg: '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb3'
    }
    {
<ommited>
      sourceasg: '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb3'
      destinationasg: '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb1'
    }
}

However this requires retrieving the application security group outside of bicep. What I would like to do is have a function that treats a string as a param. Lets call it asParam() for this example.

main.bicep

resource asg1 'Microsoft.Network/applicationSecurityGroups@2024-07-01' existing = {
  name: 'asgweb1'
}

resource asg2 'Microsoft.Network/applicationSecurityGroups@2024-07-01' existing = {
  name: 'asgweb2'
}

resource asg3 'Microsoft.Network/applicationSecurityGroups@2024-07-01' existing = {
  name: 'asgweb3'
}

module someSNGResource '../module/network-security-group/main.bicep' = {
  name: 'nsg-1'
  params: {
    name: 'nsg-webapp-subnet
    location: westus2
    securityRules: [for rule in rules: {
        name: rule.name
        properties: {
         <omitted>
          sourceApplicationSecurityGroupResourceIds: [
            asParam(rule.sourceasg)
          ]
          destinationApplicationSecurityGroupResourceIds: [
            asParam(rule.destinationasg)
          ]
        }
      }
    ]
  }
}

main.bicepparam

param rules = {
    {
<ommited>
      sourceasg: 'asg1.id'
      destinationasg: 'asg2.id'
    }
    {
<ommited>
      sourceasg: 'asg2.id'
      destinationasg: 'asg3.id
    }
    {
<ommited>
      sourceasg: 'asg3.id
      destinationasg: 'asg1.id'
    }
}

With this change existing resources could be referenced with their symbolic name inside the parameter file.

michaell104 avatar Jun 12 '25 13:06 michaell104

Unfortunately this is how we designed modules to work with Bicep and aren't planning on building direct support for this.

However, we are planning on working on the ability to pass a reference to a resource as an input or output for a module here: https://github.com/Azure/bicep/issues/2246

Would this help your use case?

stephaniezyen avatar Jun 18 '25 20:06 stephaniezyen

I'm not sure if this is what you want, but here are two options that might do what you are looking for that works already today.

Option 1: Use variables in the bicepparam file.

This would look something like this:

main.bicep:

param rules object[]

module someSNGResource 'nsg.bicep' = {
  params: {
    securityRules: [for rule in rules: {
        name: rule.name
        properties: {
          sourceApplicationSecurityGroupResourceIds: [
            rule.sourceasg
          ]
          destinationApplicationSecurityGroupResourceIds: [
            rule.destinationasg
          ]
        }
      }
    ]
  }
}

main.bicepparam

using 'main.bicep'

var asg1 = '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb1'
var asg2 = '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb2'
var asg3 = '/subscriptions/xxxx-xxxx-xx-xx-x-xxxx/resourceGroups/rg-vnet-uks/providers/Microsoft.Network/applicationSecurityGroups/asgweb3'

param rules = [
  {
    sourceasg: asg1
    destinationasg: asg2
  }
  {
    sourceasg: asg2
    destinationasg: asg3
  }
  {
    sourceasg: asg3
    destinationasg: asg1
  }
]

You would still have to define in the full resourceIds in the param file, but only once and you can then refer to them inside the rules parameter using just the variable names.


Option 2: Using the resourceId() function

If you are certain that all ASG's are in a specific resource group, another option could be to use the resourceId function inside your main.bicep, that could look something like this:

main.bicep

param rules object[]
param asgResourceGroup string = resourceGroup().name

module someSNGResource 'nsg.bicep' = {
  params: {
    securityRules: [for rule in rules: {
        name: rule.name
        properties: {
          sourceApplicationSecurityGroupResourceIds: [
            resourceId(asgResourceGroup, 'Microsoft.Network/applicationSecurityGroups', rule.sourceasg)
          ]
          destinationApplicationSecurityGroupResourceIds: [
            resourceId(asgResourceGroup, 'Microsoft.Network/applicationSecurityGroups', rule.destinationasg)
          ]
        }
      }
    ]
  }
}

main.bicepparam

using 'main.bicep'

param asgResourceGroup = 'rg-vnet-uks'

param rules = [
  {
    sourceasg: 'asgweb1'
    destinationasg: 'asgweb1'
  }
    {
    sourceasg: 'asgweb1'
    destinationasg: 'asgweb3'
  }
    {
    sourceasg: 'asgweb3'
    destinationasg: 'asgweb1'
  }
]

This option comes with the benefit of just having to supply the name of each ASG in the parameter file. If the ASG resides in another resource group than you are deploying to, you also need to supply the name of that resource group as asgResourceGroup.

SimonWahlin avatar Jun 18 '25 21:06 SimonWahlin

Option 2: Using the resourceId() function

If you are certain that all ASG's are in a specific resource group, another option could be to use the resourceId function inside your main.bicep, that could look something like this:

main.bicep

param rules object[] param asgResourceGroup string = resourceGroup().name

module someSNGResource 'nsg.bicep' = { params: { securityRules: [for rule in rules: { name: rule.name properties: { sourceApplicationSecurityGroupResourceIds: [ resourceId(asgResourceGroup, 'Microsoft.Network/applicationSecurityGroups', rule.sourceasg) ] destinationApplicationSecurityGroupResourceIds: [ resourceId(asgResourceGroup, 'Microsoft.Network/applicationSecurityGroups', rule.destinationasg) ] } } ] } }

main.bicepparam

using 'main.bicep'

param asgResourceGroup = 'rg-vnet-uks'

param rules = [ { sourceasg: 'asgweb1' destinationasg: 'asgweb1' } { sourceasg: 'asgweb1' destinationasg: 'asgweb3' } { sourceasg: 'asgweb3' destinationasg: 'asgweb1' } ] This option comes with the benefit of just having to supply the name of each ASG in the parameter file. If the ASG resides in another resource group than you are deploying to, you also need to supply the name of that resource group as asgResourceGroup.

This will do exactly what I want, thank you.

michaell104 avatar Jun 24 '25 11:06 michaell104