Massimo Bonanni
Posted on November 2, 2022
In this post I would like to show you how create an AppService with its configuration stored in a KeyVault using Bicep template.
In the next steps we make these tasks:
create an AppService with its AppServicePlan;
assign a managed identity to the AppService;
create a KeyVault;
assign the role "Key Vault Secrets User" to the AppService identity;
create a secret in the KeyVault;
add an app setting to the AppService with the KeyVault secret reference.
Create AppService and managed identity
The following snippet of Bicep allow you to create an AppService, its AppServicePlan, and assign a system assigned managed identity.
var location ='northeurope'
resource appService 'Microsoft.Web/sites@2021-01-01' = {
name: appName
location: location
kind: 'app'
properties: {
enabled: true
serverFarmId: appServicePlan.id
}
identity:{
type: 'SystemAssigned'
}
}
resource appServicePlan 'Microsoft.Web/serverfarms@2021-01-01' = {
name: appPlanName
location: location
sku: {
name: 'F1'
tier: 'Free'
size: 'F1'
family: 'F'
capacity: 0
}
}
In the previous snippet, appName
and appPlanName
are the names you want to assign to AppService and to AppServicePlan (probably, in your template, are parameters or variable generated starting from other parameters).
Create KeyVault
The following snippet is the KeyVault declaration:
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: keyVaultName
location: location
properties: {
accessPolicies: []
enableRbacAuthorization: true
enableSoftDelete: false
enabledForDeployment: false
enabledForDiskEncryption: false
enabledForTemplateDeployment: false
tenantId: subscription().tenantId
sku: {
name: 'standard'
family: 'A'
}
networkAcls: {
defaultAction: 'Allow'
bypass: 'AzureServices'
}
}
}
In the previous snippet, keyVaultName
is the name of your KeyVault resource.
Assign the role "Key Vault Secrets User" to the AppService
If we want to use secret references in the AppService settings, we need to give to the AppService the right role to access to the keyVault's secrets.
The role we are looking for is the "Key Vault Secrets User" role.
Unfortunately, the role name is not enough to assign it to the AppService but we need its name (it is a GUID).
To find the right GUID to use in the assignment, we can use the following az command:
az role definition list --name 'Key Vault Secrets User'
The command result is something like this:
[
{
"assignableScopes": [
"/"
],
"description": "Read secret contents. Only works for key vaults that use the 'Azure role-based access control' permission model.",
"id": "/subscriptions/b68ed859-8021-4876-8179-8dc97208bc2c/providers/Microsoft.Authorization/roleDefinitions/4633458b-17de-408a-b874-0445c86b69e6",
"name": "4633458b-17de-408a-b874-0445c86b69e6",
"permissions": [
{
"actions": [],
"dataActions": [
"Microsoft.KeyVault/vaults/secrets/getSecret/action",
"Microsoft.KeyVault/vaults/secrets/readMetadata/action"
],
"notActions": [],
"notDataActions": []
}
],
"roleName": "Key Vault Secrets User",
"roleType": "BuiltInRole",
"type": "Microsoft.Authorization/roleDefinitions"
}
]
We need to copy value of the name
property (4633458b-17de-408a-b874-0445c86b69e6
).
Using this value, we can assign the role to the AppService using the following Bicep snippet.
var roleId='4633458b-17de-408a-b874-0445c86b69e6'
resource appServiceKeyVaultAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: '${appService.name} Key Vault Secret User ${uniqueString(resourceGroup().id,appService.name)}'
scope: keyVault
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId) // this is the role "Key Vault Secrets User"
principalId: appService.identity.principalId
principalType: 'ServicePrincipal'
}
}
We generate the name of the assignment concatenating the name of the AppService, the role name and a unique string (we use the string function uniqueString
to be sure that the assignment name is unique).
To set the roleDefinitionId
property, we need to retrieve the unique identifier for that resource, and we can use the subscriptionResourceId
function.
To have more info about Bicep functions, I suggest to read this article.
Create a secret in the KeyVault
The following snippet allow us to create a secret in the KeyVault.
resource myKeyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
name: 'mySecret'
parent: keyVault
properties: {
attributes: {
enabled: true
}
value: 'this is the secret'
}
}
The secret is an inner resource of the KeyVault, so you can use the parent
keyword (as in the previous snippet) or adding the secret resource in the KeyVault definition. You can find all the different way to declare an inner resource reading this article.
Add the secret reference in the AppService settings
The following snipper allow you to create the whole app setting section inside the AppService resource:
resource appSettings 'Microsoft.Web/sites/config@2022-03-01' = {
name: 'appsettings'
parent: appService
properties: {
AppSecret: '@Microsoft.KeyVault(SecretUri=${myKeyVaultSecret.properties.secretUri})'
}
dependsOn:[
appServiceKeyVaultAssignment
]
}
The snippet creates a single key (named AppSecret
) in the AppService settings section.
In this case, even if you can use several ways to declare an inner resource, the best way is using the parent
keyword because you need to wait the AppService creation (for the managed identity), the role assignment (otherwise you receive an error while you create the settings) and the KeyVault secret.
The parent
keyword tell bicep that your appsettings must create after the AppService, the myKeyVaultSecret
reference tells bicep to wait for the secret creation but you need to force to wait the role assignment creation using the dependsOn
keyword.
If you put the appSettings definition inside the AppService resource definition, they are created at the same time the ARM creates the AppService and then you receive an error (just because the AppService hasn't the right role yet).
Posted on November 2, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.