0

I'm deploying Cosmos via bicep templates. Mostly everything is parameterised. We have found a way to deploy multiple stored procedures described below.

Define an array in the bicep

param storedProcedureData array = []

then in the resource section loop around the data:

resource cosmosStoredProcedures 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/storedProcedures@2021-06-15' = [for storedProcedure in storedProcedureData: {
  parent: cosmosSQLContainers
  name: storedProcedure.name
  properties: {
    resource: {
      id: storedProcedure.name
      body: storedProcedure.body
    }
  }
}]

I have a json file with the stored procedure code in like this:

[
  {
    "name": "sp1",
    "body": "function sp1 etc"
  },
  {
    "name": "sp2",
    "body": "function sp2 etc"
  }
]

and call it like this from powershell using az commands:

az deployment group create --resource-group rgname `
--name testdeployment `
--template-file "C:\GitHub\bicep\cosmosdb\main.bicep" `
--parameters "C:\GitHub\bicep\cosmosdb\cosmosMain.parameters.json" `
--parameters storedProcedureData=$storedProcedureData

where the stored procedure data is a variable:

$storedProcedureData=(Get-Content "C:\GitHub\bicep\cosmosdb\storedproc.json")

Question is - is there a better way!!! And what if I have multiple containers ...

1 Answers1

0

I suggest that you make of Bicep's modules to accommodate your looping concerns, as Bicep can't itself loop over a nested loop, but can call a module within a loop that itself does other work within a loop.

As such, might I suggest something like the following:

// cosmos-CreateStoredProcedure.bicep

@description('The stored procedures to register')
// {name: '<name>', body: '<body>'}
param StoredProcedureData array

@description('The name of the Cosmos account')
param CosmosAccountName string

@description('The name of the Cosmos SQL container')
param CosmosContainerName string

resource CosmosContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2021-10-15' existing = {
  name: '${CosmosAccountName}/${CosmosContainerName}'
}

resource StoredProcs 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/storedProcedures@2021-06-15' = [for sp in StoredProcedureData: {
  parent: CosmosContainer
  name: sp.name
  properties: {
    resource: {
      id: sp.name
      body: sp.body
    }
  }
}]

Now you've got a modularized Bicep file to which you can pass any number of stored procedures and it'll run on any Cosmos account and container you want.

So now this means that you've got an outer module that'll actually run against each of the sets of containers you want to deploy to. If you want to use an external file, create another file that maps everything out in the same manner. Create an array in Powershell and push in an object of both the containerName value and the JSON value for each of the file paths you want to read in, then pass this array to set the StoredProcedures value in the template below.

This loops through each set of stored procedure objects in the array to call the inner module, then the inner module loops through the stored procs to actually store.

// cosmos-registerAllStoredProcedures.bicep

@description('The name of the Cosmos account')
param CosmosAccountName string

param StoredProcedures array = [
{
  containerName: 'containerA'
  storedProcecures: [
    'procedureAText'
    'procedureBText'
    'procedureCText'
  ]
}
{
  containerName: 'containerB'
  filePaths: [
    'procedureAText'
  ]
}
]

module DeployStoredProcedures './cosmos-CreateStoredProcedure.bicep' = [for (set, i) in StoredProcedures: {
  name: 'storedProcedures-${set.containerName}-${i}' //Might need to include the `i` value so the build knows this will yield a unique name
  params: {
    CosmosAccountName: CosmosAccountName 
    CosmosContainerName: set.containerName
    StoredProcedureData: set.filePaths
  }
}]

Whit Waldo
  • 4,806
  • 4
  • 48
  • 70