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:

 1param location string = resourceGroup().location
 2
 3// Create a workspace
 4resource 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
10resource 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 
21resource 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: '[email protected]'
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:

 1resource 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:

 1resource 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
12resource 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:

 1resource 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
15resource 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
37resource 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!