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!