Use BICEP for your Logic Apps and Key Vault scenarios

Access secrets securely from Logic Apps using Azure Key Vault, and deploy it all using BICEP

I have been working on an Azure implementation where got an opportunity to work with Bicep to automate the azure infrastructure and code deployments. The experience is rewarding and different from creating ARM templates for IAC deployments. In this article I will try to explain a very recurring scenario in most of the Azure Integration projects and explain how Bicep is used in creating the automated deployment scripts.

Scenario:

Often when we build workflows using Azure Logic Apps, we need to use environment specific and sensitive values to make connection to external applications we integrate to or authenticate to internal/external APIs. One of the recommended way access these sensitive parameters securely during runtime, is to store them in Key Vault and use the Key Vault connectors or rest APIs to retrieve them. Key Vault connector in Logic app has lots of new improvements, it now supports Managed Identity in addition to the service principal options. Key Vault itself has introduced RBAC options as an alternative to access policies, which adds more resiliency to the Key Vault IAC deployment making it more idempotent and repeatable.

The following sections will talk about how a key vault is created using Bicep, adding secrets, adding RBAC roles and also creating key vault API connections for logic apps to interact with key vault.

Now its time to talk about BICEP. It is a declarative language, an alternative to ARM templates. Moving away from JSON format templates, this is an abstraction to the ARM templates, when the .bicep files are built, it creates the equivalent ARM files and performs the deployment. But this language has a lot of support for cleaner code syntax with better support for modularity and code re-use.

https://github.com/Azure/bicep this is a good starting point to get started and learn as per your own pace.

Create Key Vault

Let’s start creating the key vault itself; key vault can be created as a module (keyvault.bicep) and this module can be called from a main.bicep file. main.bicep behaves like an orchestrator; calling each modules parallelly if there are no dependencies. If module 1 refers an output from any other module, module 1 waits for the completion of the other modules. Explicit dependency can be declared using the below syntax.

dependsOn:[
apim_namedvalues_module
]

Below bicep script will deploy a key vault with RBAC enabled and allows template deployment from pipelines. It also outputs the key vault name and ID for the next modules in the main.bicep to use.

resource keyvault 'Microsoft.KeyVault/vaults@2021-04-01-preview' = {
name: ${keyvaultName}-${env}
location: location
tags:tagValues
properties:{
enabledForTemplateDeployment: true
enableRbacAuthorization: true
tenantId: subscription().tenantId
sku: {
family: 'A'
name: 'standard'
}
}
}
output keyVaultName string = keyvault.name
output keyVaultId string = keyvault.id

Create secrets

Let’s assume a scenario where you are deploying the IAC in a separate pipeline and any further actions on key vault is done as part of an application pipeline, the key vault can be referred in the application pipeline using the keyword “existing”.

For example, If key vault is deployed in IAC pipeline, but adding a role to an application or adding secrets to it is part of an application pipeline, key vault created above can be referred using the keyword “existing”

resource keyVault 'Microsoft.KeyVault/vaults@2021-04-01-preview' existing = {
name: ${keyvaultName}-${env}

resource secrets 'secrets' = [for secret in kvSecrets:{
name: '${secret.keyname}'
properties: {
value: '${secret.value}'
}
}]

secrets are passed as an array

param kvSecrets array = [
{
keyname : 'nzb-dataverse-sp-appid'
value : 'temp'
}
{
keyname : 'nzb-dataverse-sp-secret'
value : 'temp'
}
{
keyname : 'nzb-salesforce-ia-username'
value : 'temp'
}
{
keyname : 'nzb-salesforce-ia-password'
value : 'temp'
}
{
keyname : 'nzb-sendgrid-api-key'
value : 'temp'
}
]

One of the challenge from the security point of view is that these secret values are sensitive and probably don't want to store in source code and also not want to be exposing them from the pipeline logs. I have used secure files options in Azure DevOps and downloaded the files during the pipeline runtime. Curious to hear if there are any other options to achieve this!

Accessing keys from Logic Apps:

Consider a scenario where logic apps needs to communicate to an external system or APIs, workflows could retrieve the authentication parameters from key vault. Using out of the box connectors, key vault API connection can be created either using service principal or managed identity.

Create API connection using service principal:

Below script uses a service principal application id and secret to create an API connection.

resource la_keyvault_con 'Microsoft.Web/connections@2016-06-01' = {
name: keyvaultConnectionName
location:location
tags: tagValues
properties: {
api: {
id: 'subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/keyvault'
}
displayName:keyvaultConnectionName
parameterValues: {
'token:clientId': spApplicationId
'token:clientSecret': spSecret
'token:TenantId': ${subscription().tenantId}
'token:grantType': 'client_credential'
'vaultName': '${keyvaultName}-${env}'
}
}
}

These sensitive values can be referred from the parameter files using key vault reference as below:

"appId": {
"reference": {
"keyVault": {
"id": "/subscriptions/<subscriptionId>/resourceGroups/<resourceGroupName>/providers/Microsoft.KeyVault/vaults/<keyVaultName>"
},
"secretName": "<secretName>"
}
}

Create API connection using managed identity

resource la_keyvault_con 'Microsoft.Web/connections@2016-06-01' = {
name: keyvaultConnectionName
location:location
tags: tagValues
properties: {
api: {
id: 'subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/keyvault'
}
displayName:keyvaultConnectionName
parameterValueType: 'Alternative'
alternativeParameterValues: {
'vaultName': '${keyvaultName}-${env}'
}
}
}

Visual studio bicep plugin may not like the alternative parameter values for managed identity connection type. But it is not Bicep build error/warning, it creates the API connection with managed identity.

Add Key Vault Secret User role to logic app

With the introduction of RBAC roles in key vault, if any applications or service principal need to read secrets, Key Vault Secret User role is assigned. There are few more purpose built roles introduced. Please refer to the below link for the complete list.

The below script will add key vault secret user role to a list of logic apps.

resource logicappsKeyVaultAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = [for logicAppName in logicAppsNames:{
name: guid('Key Vault Secret User', logicAppName, subscription().subscriptionId)
scope: keyVault
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
principalId: logicAppName.identity.principalId
principalType: 'ServicePrincipal'
}
}]

Conclusion:

Bicep is still evolving but it can now achieve almost everything ARM template deployment supports. Its more modular and It provides concise syntax, reliable type safety, and support for code reuse. VS Code extension for Bicep is very helpful and it makes the scripting experience enjoyable.

Finally, Bicep playground is an awesome place to quickly check out the available templates in Bicep and ARM templates in a split screen. you can also upload an ARM template to convert to Bicep for a quick start.

https://bicepdemo.z22.web.core.windows.net/

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store