DEV Community

loading...
The 425 Show

Secure Azure deployments with Bicep and Azure Key Vault

christosmatskas profile image Christos Matskas ・6 min read

Bicep is a new (experimental) way for building and deploying infrastructure to Azure. It's a language that compiles down to standard Azure Resource Manager json templates. So instead of handcrafting hundreds (if not thousands) of lines of json, you can code it in Bicep and then let the compiler do the hard work :)

To start with Bicep, you need to install the Bicep CLI tool and ideally the VS Code extension so that you can get Intellisense and code syntax colorization. Side note: not all theme extensions support Bicep yet.

If you want to see a video of me setting it all up and writing my first Bicep program, check out this recording:

Our first Bicep template

For the purpose of this blog, we'll use Bicep to create a SQL Server and SQL database resource on Azure. Create a new directory and add a main.bicep file:

param serverName string = uniqueString('sql125486asdf')
param sqlDBName string = 'SampleDB'
param location string = resourceGroup().location
param administratorLogin string
param administratorLoginPassword string {
  secure: true
}

resource server 'Microsoft.Sql/servers@2019-06-01-preview' = {
  name: serverName
  location: location
  properties: {
    administratorLogin: administratorLogin
    administratorLoginPassword: administratorLoginPassword
  }
}

resource sqlDB 'Microsoft.Sql/servers/databases@2020-08-01-preview' = {
  name: '${server.name}/${sqlDBName}'
  location: location
  sku: {
    name: 'Standard'
    tier: 'Standard'
  }
}
Enter fullscreen mode Exit fullscreen mode

In this file, we define a few parameters and 2 resources. We will use a random name for the SQL Server and we'll inherit some properties (i.e the location) from the Azure resource group we're deploying to. uniqueString() and resourceGroup() are built-in Bicep functions. You can find more about Bicep functions here

You'll notice that we also have two uninitialized parameters:

  • administratorLogin
  • administratorLoginPassword

We left them blank for a good reason! We don't want to store secrets in our code as this is a security attack vector!

Let's build our code:

bicep build main.bicep
Enter fullscreen mode Exit fullscreen mode

This should generate an output file main.json that contains the compiled ARM template json:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "serverName": {
      "type": "string",
      "defaultValue": "[uniqueString('sql125486asdf')]"
    },
    "sqlDBName": {
      "type": "string",
      "defaultValue": "SampleDB"
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    },
    "administratorLogin": {
      "type": "string"
    },
    "administratorLoginPassword": {
      "type": "secureString"
    }
  },
  "functions": [],
  "resources": [
    {
      "type": "Microsoft.Sql/servers",
      "apiVersion": "2019-06-01-preview",
      "name": "[parameters('serverName')]",
      "location": "[parameters('location')]",
      "properties": {
        "administratorLogin": "[parameters('administratorLogin')]",
        "administratorLoginPassword": "[parameters('administratorLoginPassword')]"
      }
    },
    {
      "type": "Microsoft.Sql/servers/databases",
      "apiVersion": "2020-08-01-preview",
      "name": "[format('{0}/{1}', parameters('serverName'), parameters('sqlDBName'))]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Standard",
        "tier": "Standard"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Sql/servers', parameters('serverName'))]"
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Let's add the Sec in DevSecOps

First and foremost, we need to ensure that the account deploying this ARM template has the right enough privilege to do so. You don't want to use a user account as these tend to have elevated permissions and can cause some serious damage. The recommended practice is to use a Service Principal account. You can create these via the Azure Portal, PowerShell or the CLI. In this instance, we'll use the Azure CLI

az login
az ad sp create-for-rbac 
Enter fullscreen mode Exit fullscreen mode

Take a note of the output. Next, we need to assign the Contributor role with a very specific scope: the resource group we'll be deploying our resources to.

az role assignment create --assignee <the SP Client ID> --scope /subscriptions/<Your Subscription ID>/resourceGroups/<Your Resource Group Name> --role contributor
Enter fullscreen mode Exit fullscreen mode

For our account to also be able to retrieve the necessary secrets from Azure Key Vault, we need to create a very specific role. You can find more information about how to create this very custom RBAC role here.

Following the above docs, we'll create a new keyvaultrole.json file and paste the following json:

{
  "Name": "Key Vault CM resource manager template deployment operator",
  "IsCustom": true,
  "Description": "Lets you deploy a resource manager template with the access to the secrets in the Key Vault.",
  "Actions": [
    "Microsoft.KeyVault/vaults/deploy/action"
  ],
  "NotActions": [],
  "DataActions": [],
  "NotDataActions": [],
  "AssignableScopes": [
"/subscriptions/<Your Subscription ID>/resourceGroups/<Your Resource Group Name>/providers/Microsoft.KeyVault/vaults/<Your Key Vault name>"
  ]
}
Enter fullscreen mode Exit fullscreen mode

We now need to create this role in our Azure Subscription (if one doesn't exist already) and assign it to our Service Principal. In the Azure CLI, type the following:

az role definition create --role-definition ./keyvaultrole.json
az role assignment create --role "Key Vault CM resource manager template deployment operator" --assignee <your SP client Id> --resource-group bicep-demo
Enter fullscreen mode Exit fullscreen mode

Now that we have an account with the right privileges, we need to sign out (as ourselves) and sign in with the newly created Service Principal account:

az account clear
az login --service-principal -u <Service Principal Id> -p <Servie Principal password> --tenant <Azure AD Tenant ID>
az account show
Enter fullscreen mode Exit fullscreen mode

Alt Text

The final step is to configure our Key Vault to allow ARM deployments as well as create the necessary secrets that will be referenced by our Bicep-generated ARM templates

In Key Vault, navigate to the Access Policies tab and ensure that the Azure Resource Manager for template deployment option under Enable Access to: is checked. Make sure to press the Save button if you make any changes:

Alt Text

Finally, we want to check that our Service Principal has been assigned the right role to be able to read secrets from Key Vault. Navigate to the Access Control (IAM) tab and check under Role Assignments to ensure that the script we run earlier worked:

Alt Text

We now have a locked down account and the all the secrets stored in Key Vault. Let's finalize our Bicep/ARM deployment.

Referencing Key Vault in ARM templates

At this point we have a main.json that contains our ARM Template resources. Since there is no built-in way to integrate with Key Vault in Bicep, we need to use a separate file for our Key Vault-referenced parameters. Create a new params.json file:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "administratorLogin": {
            "reference": {
                "keyVault": {
                    "id": "/subscriptions/<Subscription ID>/resourceGroups/<Resource Group Name>/providers/Microsoft.KeyVault/vaults/<Key Vault name>"
                },
                "secretName": "sqlAdministratorLogin"
            }
        },
        "administratorLoginPassword": {
            "reference": {
                "keyVault": {
                    "id": "/subscriptions/<Subscription ID>/resourceGroups/<Resource Group Name>/providers/Microsoft.KeyVault/vaults/<Key Vault name>"
                },
                "secretName": "sqlAdministratorLoginPassword"
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This json references secrets in Key Vault instead of having hard-coded, plain-text secrets - how awesome is this?

NOTE: The latest Azure CLI (2.20.0 or later) and Azure PowerShell (5.6.0 or later) have built-in support for bicep so there no need to build/compile the Bicep file. The tooling will automatically transpile the Bicep code to ARM

We can now go ahead and deploy our infrastructure as code using the Azure CLI:

az deployment group create -f ./main.bicep -g bicep-demo --parameters ./params.json --name cmdeployment425show
Enter fullscreen mode Exit fullscreen mode

The output will be a bunch of json with information about the deployment like the heavily omitted output below:

{
  "id": "/subscriptions/e42acc2d-8462-4fb5-bf0d-d983c0017584/resourceGroups/bicep-demo/providers/Microsoft.Resources/deployments/cmdeployment425show",
  "location": null,
    ...
    "provisioningState": "Succeeded",
    "templateHash": "18120650064530837360",
    "templateLink": null,
    "timestamp": "2021-03-02T00:16:56.630918+00:00",
    "validatedResources": null
  },
  "resourceGroup": "bicep-demo",
  "tags": null,
  "type": "Microsoft.Resources/deployments"
}
Enter fullscreen mode Exit fullscreen mode

Building more elaborate Bicep code

The GitHub repo is thin at the moment and for a moment I thought I was stuck with the tutorial sample code. But then, I came across the glorious Bicep Playground and all was great again. The Playground displays Bicep and compiled json side by side and provides you with a lot of sample code to help you build your own templates. My suspicion is that this is all driven by the Azure ARM Samples repo:

Alt Text

Source Code

This GitHub repo contains all the files and code referenced in this blob post.

Summary

It's early but promising days for Bicep. There are still a few things that need work before it's ready for mass adoption but I believe that it's a great tool when working directly with Azure Resources. Unlike Pulumi and Terraform, it's native Azure and therefore provides better support for Azure resources. However, there are pros and cons with every tool so choose the one that truly makes you happy.

Discussion (0)

pic
Editor guide