Bicep, Keyvault & AppService
Tags: bicep, azure, keyvault
Published: 2022-11-02 09:00:00

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:

1. create an AppService with its AppServicePlan;

1. assign a managed identity to the AppService;

1. create a KeyVault;

1. assign the role "Key Vault Secrets User" to the AppService identity;

1. create a secret in the KeyVault;

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

```bash
az role definition list --name 'Key Vault Secrets User'
```

The command result is something like this:

```json
[
  {
    "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](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/bicep-functions). 

## 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](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/child-resource-name-type).

##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).