Azure Verified Modules
Glossary GitHub GitHub Issues Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Last updated: 05 Aug 2024

Interfaces

This page details the Bicep-specific interfaces/schemas for AVM Resource Modules features/extension resources.


Secrets export

Secrets used inside a module can be exported to a Key Vault reference provided as per the below schema. This implementation provides a secure way around the current limitation of Bicep on providing a secure template output (that can be used for secrets).

The user must

  • provide the resource Id to a Key Vault. The principal used for the deployment must be allowed to set secrets in this Key Vault.
  • provide a name for each secret they want to store (opt-in). The module will suggest which secrets are available via the implemented user-defined type.

The module returns an output table where the key is the name of the secret the user provided, and the value contains both the secret’s resource Id and URI.

The feature must be implemented as per the below schema. Diversions are only allowed in places marked as >text< to ensure a consistent user experience across modules.

  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Optional. Key vault reference and secret settings for the module\'s secrets export.')
  param secretsExportConfiguration secretsExportConfigurationType?
  
  // ============= //
  //   Resources   //
  // ============= //
  
  module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) {
    name: '${uniqueString(deployment().name, location)}-secrets-kv'
    scope: resourceGroup(
      split((secretsExportConfiguration.?keyVaultResourceId ?? '//'), '/')[2],
      split((secretsExportConfiguration.?keyVaultResourceId ?? '////'), '/')[4]
    )
    params: {
      keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId ?? '//', '/'))
      secretsToSet: union(
        [],
        contains(secretsExportConfiguration!, '>secretToExport1<Name')
          ? [
              {
                name: secretsExportConfiguration!.>secretToExport1<Name
                value: >secretReference1< // e.g., >singularMainResourceType<.listKeys().primaryMasterKey
              }
            ]
          : [],
        contains(secretsExportConfiguration!, '>secretToExport2<Name')
          ? [
              {
                name: secretsExportConfiguration!.>secretToExport2<Name
                value:>secretReference2<  // e.g., >singularMainResourceType<.listKeys().secondaryMasterKey
              }
            ]
          : []
          // (...)
      )
    }
  }
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  @description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.')
  output exportedSecrets secretsOutputType = (secretsExportConfiguration != null)
    ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret)
    : {}
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  type secretsExportConfigurationType = {
    @description('Required. The resource ID of the key vault where to store the secrets of this module.')
    keyVaultResourceId: string
  
    @description('Optional. The >secretToExport1< secret name to create.')
    >secretToExport1<Name: string?
  
    @description('Optional. The >secretToExport2< secret name to create.')
    >secretToExport2<Name: string?
  
    // (...)
  }
  
  import { secretSetType } from 'modules/keyVaultExport.bicep'
  type secretsOutputType = {
    @description('An exported secret\'s references.')
    *: secretSetType
  }
  
secretsExportConfiguration: {
    keyVaultResourceId: '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.KeyVault/vaults/{keyVaultName}'
    >secretToExport1<Name: 'myPrimarySecret'
    >secretToExport2<Name: 'mySecondarySecret'
    // (...)
  }
  

  // ============== //
  //   Parameters   //
  // ============== //
  
  @description('Required. The name of the Key Vault to set the secrets in.')
  param keyVaultName string
  
  @description('Required. The secrets to set in the Key Vault.')
  param secretsToSet secretToSetType[]
  
  // ============= //
  //   Resources   //
  // ============= //
  
  resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = {
    name: keyVaultName
  }
  
  resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [
    for secret in secretsToSet: {
      name: secret.name
      parent: keyVault
      properties: {
        value: secret.value
      }
    }
  ]
  
  // =========== //
  //   Outputs   //
  // =========== //
  
  @description('The references to the secrets exported to the provided Key Vault.')
  output secretsSet secretSetType[] = [
    #disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value
    for index in range(0, length(secretsToSet ?? [])): {
      secretResourceId: secrets[index].id
      secretUri: secrets[index].properties.secretUri
    }
  ]
  
  // =============== //
  //   Definitions   //
  // =============== //
  
  @export()
  type secretSetType = {
    @description('The resourceId of the exported secret.')
    secretResourceId: string
  
    @description('The secret URI of the exported secret.')
    secretUri: string
  }
  
  type secretToSetType = {
    @description('Required. The name of the secret to set.')
    name: string
  
    @description('Required. The value of the secret to set.')
    @secure()
    value: string
  }
  

When using a module that implements the above interface, you can access its outputs for example in the following ways:


  // Get all exported secret references
  output exportedSecrets object = >deploymentReference<.outputs.exportedSecrets
  
  // Get the resource Id of just the secret with name >secretToExport1<Name
  output specificSecret string = >deploymentReference<.outputs.exportedSecrets.>secretToExport1<Name.secretResourceId
  
  // Get the resource Ids of all secrets set
  output exportedSecretResourceIds array = map(
    items(>deploymentReference<.outputs.exportedSecrets),
    item => item.value.secretResourceId
  )
  

Which returns a JSON-formatted output like

{
    "exportedSecrets": {
      "Type": "Object",
      "Value": {
        ">secretToExportName1<": {
          "secretResourceId": "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName1<",
          "secretUri": "https://<vaultName>.vault.azure.net/secrets/>secretToExportName1<"
        },
        ">secretToExportName2<": {
          "secretResourceId": "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName2<",
          "secretUri": "https://<vaultName>.vault.azure.net/secrets/>secretToExportName2<"
        }
      }
    },
    "specificSecret": {
      "Type": "String",
      "Value": "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName1<"
    },
    "exportedSecretResourceIds": {
      "Type": "Array",
      "Value": [
        "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName1<",
        "/subscriptions/<subId>/resourceGroups/<rgName>providers/Microsoft.KeyVault/vaults/<vaultName>/secrets/>secretToExportName2<"
      ]
    }
  }