John McArthur
John McArthur

Reputation: 1024

Using BICEP can I find a specific element in an array by a substring of the name?

I have a bicep module storageAccounts.bicep, which is creating multiple storage accounts and outputing an array of their names:

@description('Name of Environment')
param environment string

param storageAccounts array = [
  {
    name : 'api${environment}${uniqueString(resourceGroup().id)}'
    skuName : 'Standard_LRS'
    accessTier : 'Hot'
  }
  {
    name : 'web${environment}${uniqueString(resourceGroup().id)}'
    skuName : 'Standard_LRS'
    accessTier : 'Hot'
  }
]

resource storage_resource 'Microsoft.Storage/storageAccounts@2021-06-01' = [for storage in storageAccounts: {
  name : storage.name
  location : resourceGroup().location
  sku:{
    name : storage.skuName
  }
  kind : 'StorageV2'
  properties:{
    accessTier: storage.accessTier
    allowBlobPublicAccess: true
    minimumTlsVersion: 'TLS1_2'
    supportsHttpsTrafficOnly: true
    encryption:{
      keySource: 'Microsoft.Storage'
      services:{
        blob:{
          keyType: 'Account'
          enabled: true
        }
        file:{
          keyType: 'Account'
          enabled: true
        }
      }
    }    
  }
}]

output storageAccounts array = [for (name, i) in storageAccounts:{
    storageAccountName : name
}]

I'm passing that array into another module to create a function app, and I'm trying to find a particular item in the array, something like this:

@description('array of storage account names')
param storageAccounts array

var storageAccountName = storagesAccounts.where(function(storageAccount) {
  return storageAccount.name.startsWith('api')
})


resource storageAccount 'Microsoft.Storage/storageAccounts@2019-04-01' existing = {
  name: storageAccountName
}

Is this something I can do? Or would I be better creating the storage accounts separately and passing the name directly?

Upvotes: 2

Views: 4545

Answers (2)

Boris Lipschitz
Boris Lipschitz

Reputation: 9526

Bicep supports filter lambda function. In your case the code could look like this

var storageAccountName = first(filter(storagesAccounts, x => x.name.startsWith('api'))).name

More details in this blog, which I wrote when I had a similar problem to solve https://arinco.com.au/blog/azure-bicep-find-an-item-in-an-array-of-objects/

Upvotes: 4

Ansuman Bal
Ansuman Bal

Reputation: 11451

Currently its not supported to find the element of an array in bicep . There are Github Issues on the same Issue 1 and Issue 2 .

As a workaround you can just pass the storage account which you need to the module and use it then in the module i.e. nested template.

I tested the below to create the two storage account and then referring the api storage account in the 2nd template and storing the id of the storage account in a keyvault secret like below:

multiplestorage.bicep:

@description('Name of Environment')
param environment string = 'test'

param storageAccounts array = [
  {
    name : 'api${environment}${uniqueString(resourceGroup().id)}'
    skuName : 'Standard_LRS'
    accessTier : 'Hot'
  }
  {
    name : 'web${environment}${uniqueString(resourceGroup().id)}'
    skuName : 'Standard_LRS'
    accessTier : 'Hot'
  }
]

resource storage_resource 'Microsoft.Storage/storageAccounts@2021-06-01' = [for storage in storageAccounts: {
  name : storage.name
  location : resourceGroup().location
  sku:{
    name : 'Standard_LRS'
  }
  kind : 'StorageV2'
  properties:{
    accessTier: 'Hot'
    allowBlobPublicAccess: true
    minimumTlsVersion: 'TLS1_2'
    supportsHttpsTrafficOnly: true
    encryption:{
      keySource: 'Microsoft.Storage'
      services:{
        blob:{
          keyType: 'Account'
          enabled: true
        }
        file:{
          keyType: 'Account'
          enabled: true
        }
      }
    }    
  }
}]

output storageAccounts array = [for (name, i) in storageAccounts:{
    storageAccountName : name
}]

module connectionString './functionapp.bicep'=[for (name,i) in storageAccounts :if(startsWith(storage_resource[i].name,'api')){
  name: 'functionappNested${i}'
  params: {
    storageAccounts:storage_resource[i].name
}
}]

functionapp.bicep

param storageAccounts string

resource storageAccount 'Microsoft.Storage/storageAccounts@2019-04-01' existing = {
  name: storageAccounts
}

resource keyVaultShared 'Microsoft.KeyVault/vaults@2021-06-01-preview' existing = {
  name: 'keyvaulttest1234ans'
}
resource storageAccountConnectionString 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = {
  parent:keyVaultShared
  name: '${keyVaultShared.name}-test'
  properties:{
    contentType: 'Storage Account Connection String'
    value: storageAccount.id
}
}

Output:

enter image description here

enter image description here

enter image description here

enter image description here

Note: Similarly you can pass the storage account name starting with web to another nested template as module as use it for another resource.

Upvotes: 1

Related Questions