Ken Muse

Connecting Azure APIM and AppInsights Using Bicep


If you’re using Bicep templates, then you are probably trying to create idempotent definitions. You want each deployment to result in the same resources being published in the same state. Unfortunately, some resources need more help than others. Azure API Management (APIM) is one of those. To illustrate, let’s state with a few simple resources: Application Insights (connected to a Log Analytics workspace) and APIM:

  1   param location string = resourceGroup().location
  2   
  3   // Create a workspace
  4   resource workspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
  5     name: 'myworkspace'
  6     location: location
  7   }
  8   
  9   // Create an App Insights resources connected to the workspace
 10   resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
 11     name: 'apiminsights'
 12     location: location
 13     kind: 'web'
 14     properties:{
 15       Application_Type:'web'
 16       WorkspaceResourceId: workspace.id
 17     }
 18   }
 19   
 20   // Create an APIM 
 21   resource apim 'Microsoft.ApiManagement/service@2019-12-01' = {
 22     name: 'myapim'
 23     location: location
 24     sku:{
 25       capacity: 0
 26       name: 'Consumption'
 27     }
 28     identity:{
 29       type:'SystemAssigned'
 30     }
 31     properties:{
 32       publisherName: 'DemoOps'
 33       publisherEmail: 'publisher@demoops.com'
 34     }
 35   }

So far, easy enough. Next, you might think the right thing to do is to create a Logger in your template. For example:

  1   resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-04-01-preview' = {
  2     parent: apim
  3     name: 'apimlogger'
  4     properties:{
  5       resourceId: appInsights.id
  6       description: 'Application Insights for APIM'
  7       loggerType: 'applicationInsights'
  8       credentials:{
  9         instrumentationKey: appInsights.properties.InstrumentationKey
 10       }
 11     }
 12   }

This approach works and will deploy correctly, but it has a side effect. Each time the template is deployed, you’ll notice a new Named Value entry added to the APIM instance. Worse than that, each new entry will have the same value! Clearly, this isn’t an idempotent deployment. Time for a new strategy.

The best way to solve this issue is to create a Named Value in your template. You can then reference the Named Value in the Logger using the reference notation, {{namedValueName}}. Adding this to our example, we can create a Named Value called instrumentationKey and then reference it as {{instrumentationKey}} in the Logger:

  1   resource namedValueAppInsightsKey 'Microsoft.ApiManagement/service/namedValues@2020-06-01-preview' = {
  2     parent: apim
  3     name: 'instrumentationKey'
  4     properties: {
  5       tags: []
  6       secret: false
  7       displayName: 'instrumentationKey'
  8       value: appInsightsApim.properties.InstrumentationKey
  9     }
 10   }
 11   
 12   resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-04-01-preview' = {
 13     parent: apim
 14     name: 'apimlogger'
 15     properties:{
 16       resourceId: appInsights.id
 17       description: 'Application Insights for APIM'
 18       loggerType: 'applicationInsights'
 19       credentials:{
 20         instrumentationKey: '{{instrumentationKey}}'
 21       }
 22     }
 23   }

Now, each deployment will behave exactly as expected. We’ll have a single NamedValue entry, even after multiple deployments.

If we want to create a Named Value from a Key Vault entry, then we just need to make two adjustments. First, we mark the key as a secret (using secret: true). Next, we add a keyVault.secretIdentifier and provide a Secret URI for the value to be retrieved from Key Vault. The code looks like this:

  1   resource namedValueAppInsightsSecret 'Microsoft.ApiManagement/service/namedValues@2020-06-01-preview' = {
  2     parent: apim
  3     name: 'instrumentationKey'
  4     properties: {
  5       tags: []
  6       secret: true
  7       keyVault: {
  8         secretIdentifier: secretAppInsights.properties.secretUri
  9       }
 10       displayName: 'instrumentationKey'
 11     }
 12   }
 13   
 14   // Create a Key Vault instance
 15   resource keyvault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = {
 16     name: keyVaultName
 17     location: location
 18     properties: {
 19       enabledForDeployment: true
 20       enabledForTemplateDeployment: true
 21       enableRbacAuthorization: true
 22       enableSoftDelete: true
 23       softDeleteRetentionInDays: 7
 24       networkAcls: {
 25         defaultAction: 'Allow'
 26         bypass: 'AzureServices'
 27       }
 28       sku: {
 29         name: 'standard'
 30         family: 'A'
 31       }
 32       tenantId: tenant().tenantId
 33     }
 34   }
 35   
 36   // Create a Secret within the KeyVault
 37   resource secretAppInsights 'Microsoft.KeyVault/vaults/secrets@2021-06-01-preview' = {
 38     name: 'AppInsightsKeyApim'
 39     parent: keyvault
 40     properties: {
 41       value: appInsights.properties.InstrumentationKey
 42     }
 43   }

That’s all there is to referencing App Insights from a Bicep template. Pretty simple, right?

Have fun working with Bicep and Happy DevOp’ing!