Siegfried
Siegfried

Reputation: 705

How to pass CosmosDb SQL Account as bicep module parameter?

How would I create a cosmos db account and pass it as a parameter to a bicep module? I would like enhance this sample bicep script by moving the role definition and role assignment to a separate module.

Here is my attempt at creating a module (that compiles and creates a CosmosDBAccount with no errors):

//@description ('cosmosDbAccount')
//param cosmosDbAccount object

@description ('cosmosDbAccountId')
param cosmosDbAccountId string

@description ('cosmosDbAccountName')
param cosmosDbAccountName string

@description('iteration')
param it int

@description('Principal ID of the managed identity')
param principalId string

var roleDefId = guid('sql-role-definition-', principalId, cosmosDbAccountId)
var roleDefName = 'Custom Read/Write role-${it}'
var roleAssignId = guid(roleDefId, principalId, cosmosDbAccountId)

resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2021-06-15' = {
  name: '${cosmosDbAccountName}/${roleDefId}'
  properties: {
    roleName: roleDefName
    type: 'CustomRole'
    assignableScopes: [
      cosmosDbAccountId
    ]
    permissions: [
      {
        dataActions: [
          'Microsoft.DocumentDB/databaseAccounts/readMetadata'
          'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
        ]
      }
    ]
  }
}

resource roleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2021-06-15' = {
  name: '${cosmosDbAccountName}/${roleAssignId}'
  properties: {
    roleDefinitionId: roleDefinition.id
    principalId: principalId
    scope: cosmosDbAccountId
  }
}

Here is my attempt at calling the module:

@batchSize(1)
module cosmosRole 'cosmosRole.bicep' = [for (princId, jj) in principals: {
  name: 'cosmos-role-definition-and-assignment-${jj}'
  params: {
//    cosmosDbAccount: cosmosDbAccount
    cosmosDbAccountId: cosmosDbAccount.id
    cosmosDbAccountName: cosmosDbAccount.name
    principalId: princId
    it: jj
  }
}]

As you can see, the original code uses cosmosDbAccount.id and I have replaced this with a string called cosmosDbAccountId. When I try un-comment the above code and pass the cosmosDbObject and use the original syntax ("cosmosDbAccount.id" and "cosmosDbAccount.name") I get this error

ERROR: ..."Deployment template validation failed: 'The template variable 'roleDefId' is not valid: The language expression property 'id' doesn't exist, available properties are 'apiVersion, location, tags, identity, kind, properties, condition, deploymentResourceLineInfo, existing, isConditionTrue, subscriptionId, resourceGroupName, scope, resourceId, referenceApiVersion, isTemplateResource, isAction, provisioningOperation'.. 

Questions:

  1. I would prefer the original syntax (fewer parameters) inside my new module. How do I do this?

  2. How do I confirm the script created the roleAssignment and roleDefinition? I cannot find these in the azure portal. When I use the bicep output statement they appear but I would like to see them using the portal web page.

Upvotes: 1

Views: 1096

Answers (1)

Thomas
Thomas

Reputation: 29562

Few things here. Passing a resource type parameter is an experimental feature, you will have to enable it (see Proposal - simplifying resource referencing for more details)

Before deploying your bicep file, you will need to set this environment variable:

# powershell example
$env:BICEP_RESOURCE_TYPED_PARAMS_AND_OUTPUTS_EXPERIMENTAL="true"

It will still show errors in visual studio code but the deployment was successful.

Here is the modified module with a parameter of type resource:

param cosmosDbAccount resource 'Microsoft.DocumentDB/databaseAccounts@2021-11-15-preview'

param it int
param principalId string

var roleDefId = guid('sql-role-definition-', principalId, cosmosDbAccount.id)
var roleDefName = 'Custom Read/Write role-${it}'
var roleAssignId = guid(roleDefId, principalId, cosmosDbAccount.id)

resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2021-06-15' = {
  name: '${cosmosDbAccount.name}/${roleDefId}'
  properties: {
    roleName: roleDefName
    type: 'CustomRole'
    assignableScopes: [
      cosmosDbAccount.id
    ]
    permissions: [
      {
        dataActions: [
          'Microsoft.DocumentDB/databaseAccounts/readMetadata'
          'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
        ]
      }
    ]
  }
}

resource roleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2021-06-15' = {
  name: '${cosmosDbAccount.name}/${roleAssignId}'
  properties: {
    roleDefinitionId: roleDefinition.id
    principalId: principalId
    scope: cosmosDbAccount.id
  }
}

In the main bicep file, we can then pass the cosmosDbAccount as a parameter:

@batchSize(1)
module cosmosRole 'cosmosRole.bicep' = [for (princId, jj) in principals: if (!empty(principals)) {
  name: 'cosmos-role-definition-and-assignment-${jj}'
  params: {
    cosmosDbAccount: cosmosDbAccount
    principalId: princId
    it: jj
  }
}]

This solution is still experimental and while running the az deployment group create, you will see this big warning:
WARNING: Resource-typed parameters and outputs in ARM are experimental, and should be enabled for testing purposes only. Do not enable this setting for any production usage, or you may be unexpectedly broken at any time!

If you don't want to pass two parameters, you could declare an existing resource in your module and just pass the cosmosDbAccountName parameter:

param cosmosDbAccountName string

param it int
param principalId string

var roleDefId = guid('sql-role-definition-', principalId, cosmosDbAccount.id)
var roleDefName = 'Custom Read/Write role-${it}'
var roleAssignId = guid(roleDefId, principalId, cosmosDbAccount.id)

resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2021-11-15-preview' existing = {
  name: cosmosDbAccountName
}

resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2021-06-15' = {
  name: roleDefId
  parent: cosmosDbAccount
  properties: {
    roleName: roleDefName
    type: 'CustomRole'
    assignableScopes: [
      cosmosDbAccount.id
    ]
    permissions: [
      {
        dataActions: [
          'Microsoft.DocumentDB/databaseAccounts/readMetadata'
          'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*'
        ]
      }
    ]
  }
}

resource roleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2021-06-15' = {
  name: roleAssignId
  parent: cosmosDbAccount
  properties: {
    roleDefinitionId: roleDefinition.id
    principalId: principalId
    scope: cosmosDbAccount.id
  }
}

You main file will look like that:

@batchSize(1)
module cosmosRole 'cosmos-module.bicep' = [for (princId, jj) in principals: if (!empty(principals)) {
  name: 'cosmos-role-definition-and-assignment-${jj}'
  params: {
    cosmosDbAccountName: cosmosDbAccount.name
    principalId: princId
    it: jj
  }
}]

Regarding your second question, if you navigate to your cosmos db account and then click on the export template button, you should be able to see the created roles and related assignments (I know it s not ideal...): enter image description here

Upvotes: 1

Related Questions