bicep icon indicating copy to clipboard operation
bicep copied to clipboard

Suggestion: Make it easier to import sets of constants

Open NeilMacMullen opened this issue 3 years ago • 6 comments

Is your feature request related to a problem? Please describe. I am currently attempting to set up a role-assignment. This currently looks like...

resource keyVaultAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  name: guid('Key Vault Secret User', siteName, subscription().subscriptionId)
  scope: keyVault
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
    principalId: site.identity.principalId
    principalType: 'ServicePrincipal'
  }
  dependsOn: [
    keyVault
  ]
}

The fact that I have to look up "magic" GUIDS for the role definition is frustrating. It would be far preferable to be able to write something like:

roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', rbac.keyVaultSecretsUser)

Describe the solution you'd like

Ideally, BICEP would already "know" about all the RBAC role guids and other constants that are likely to be used in deployments. However I can understand why this might be "out of scope". :-)

#1895 address the specific issue of RBAC guids. The rest of this suggestion list provides a mechanism for addressing other sources of constant-sets, including those not known by the BICEP team...

An alternative to built-in sets would be to make available a set of modules that allow users to manually import these. For example, see the attached file which attempts to "export" all known RBAC guids. These modules could be contributed to by the user community (i.e. available in the repo here) which would require a small amount of admin overhead and some policy decisions on naming/content/format etc.

The attached module simply contains code in this format...

output rbacRoles object = {

  // Grants full access to manage all resources, but does not allow you to assign roles in Azure RBAC, manage assignments in Azure Blueprints, or share image galleries.
  contributor : 'b24988ac-6180-42a0-ab88-20f7382dd24c'

   // Grants full access to manage all resources, including the ability to assign roles in Azure RBAC.
  owner : '8e3af657-a8ff-443c-a75c-2fe8c4bcb635'

  // and  so on....
}

Unfortunately consuming it requires an unfortunate amount of ceremony....

// import the module
module rbac './rbacRoles.bicep' = {
  name : ''
}
// set up shortcut to namespace
var roles =  rbac.outputs.rbacRoles

// now we can use  roles.keyVaultAzureSecretUser etc 

It would be desirable if there was a more convenient way to do this (perhaps with a different keyword). E.g.

import   rbacRoles from  './rbacRoles.bicep' as roles 
// now use  roles.keyVaultAzureSecretUser  etc

Further, it would be highly desirable if the VS Code intellisense could cope with this. At the moment it's unable to chain through the module import; ie it won't supply completions for roles in the example above.

It would also be desirable if some syntax were available to allow intellisense hints to be provided for variables so that VS Code could show the descriptions of the imported constants.

NeilMacMullen avatar Jun 04 '21 09:06 NeilMacMullen

After reading #2656 I think the attached example would be better written as

output rbacRoles object = {

  // Grants full access to manage all resources, but does not allow you to assign roles in Azure RBAC, manage assignments in Azure Blueprints, or share image galleries.
  contributor :   subscriptionResourceId('Microsoft.Authorization/roleDefinitions','b24988ac-6180-42a0-ab88-20f7382dd24c')

  // and  so on....
}

which changes the usage to roleDefinitionId: roles.AzureKeyVaultSecretUser

That doesn't make any effective difference to the other points in the suggestion but it does highlight how making it easier to import objects form a module can help with readability in the main file.

NeilMacMullen avatar Jun 04 '21 10:06 NeilMacMullen

@NeilMacMullen - would you consider this to be a dup of #893?

alex-frankel avatar Jun 10 '21 19:06 alex-frankel

@alex-frankel @NeilMacMullen

I'd say the most important thing here would be enabling intellisense for whatever "constants" are produced. I think the best way to do this would be to allow a bicep file that only has a var (or output) to be "imported" in a special way that won't actually run the bicep file or include it as a nested template, but read the vars and make the var available to intellisense, and then transpile the resulting match into the ARM. Whether that's a decorator on the file indicating it's meant to be a constant, or a special module or import syntax that specifies that as well, would be fine. Typescript syntax would work well here.

Example:

rbacRoles.bicep

output roles object = {
  Owner = 'aaaaa-bbbbb-cccc-dddd'
}

main.bicep

import { roles } from './rbacRoles.bicep'

output GUID string = roles.Owner

Result: 'aaaaa-bbbbb-cccc-dddd' and as soon as I typed roles. I would have Owner (as well as others) popping up for intellisense ARM result would replace the roles.Owner reference with simply a constant string that was provided.

If rbacroles.bicep contained any resource statements, it would immediately throw an error saying that a module imported as a constant can only contain vars, functions, and var outputs that can be evaluated locally.

JustinGrote avatar Jul 07 '21 00:07 JustinGrote

Related to #1895 , here's an example file I generate with Powershell that I would like to import in a way that it never actually ends up in the ARM, but would enable me to add intellisense to my bicep per the approach I discussed above. builtin.bicep.txt

JustinGrote avatar Jul 07 '21 00:07 JustinGrote

Using the JSON method, this is pretty nice and has intellisense: image

JustinGrote avatar Jul 14 '21 14:07 JustinGrote

Hey folks - this is something I would love to have available, in whatever form it takes. Just wondering if anybody knows there has been progress that might not be reflected here.

Example scenario: Say we have a centrally managed set of network configs -- IP ranges, subnet IDs, etc. -- that occasionally change, and are out of the hands of people using Bicep. If we have shared Bicep modules then those modules should to be able to refer to the centrally managed values somehow. So when we re-run the deployment, it picks up the changes and applies them.

If I put these constants into a module, which is then used by one or more other modules, I'm worried there will be name conflicts.

Using the loadXXX functions would be fine -- but currently it appears those functions only work with relative/local paths, which defeats the purpose.

rellis-of-rhindleton avatar Sep 30 '22 20:09 rellis-of-rhindleton

I am going to close this since we've largely completed the work with the load*Content() functions. @rellis-of-rhindleton - we will track the distinct ask of loading from a URI with #4922.

alex-frankel avatar Oct 18 '22 16:10 alex-frankel