Azure, Azure DevOps, Azure Pipelines

Create an Azure Key Vault using ARM templates, Resource Functions, and Azure Pipelines

Recently I’ve needed to create an Azure Key Vault using ARM templates in an Azure pipeline, and I hit a couple of interesting snags – the ARM template requires parameters called “tenantId” and “objectId” – these are GUID values, but it wasn’t immediately obvious to me what these meant and where they come from.

I’ve included below part of my original ARM template that generated my Azure Key Vault:

"resources": [
    {
        "type": "Microsoft.KeyVault/vaults",
        "name": "myKeyVault",
        "apiVersion": "2015-06-01",
        "location": "UK South",
        "properties": {
          "sku": {
            "family": "A",
            "name": "Standard"
          },
          "tenantId": "DD1AA6D2-981F-4158-B30E-2511639CDB22",
          "accessPolicies": [
            {
              "tenantId": "DD1AA6D2-981F-4158-B30E-2511639CDB22",
              "objectId": "22FF9FC0-2832-46D7-8BAB-3855609C6AC1",
              "permissions": {
                "keys": [ "All" ],
                "secrets": [ "All" ]
              }
            }
          ]
        }
    }
]

I’ve included syntax for giving permission to all keys and secrets just to make the code a bit shorter – in a production application I’d obviously restrict access more.

I’ve highlighted in red a few things that I don’t like about this template:

  • The name of the key vault is “myKeyVault”. There’s nothing wrong with that name particularly, except that the generated resource will have a default DNS name of https://myKeyVault.vault.azure.net, which probably isn’t unique and that would cause the build process to fail. I’d prefer this name to be unique.
  • The location of the key vault is hardcoded to be “UK South”. I’d prefer to remove this hardcoding, and generate location at build-time to be the same as the parent resource group.
  • There are two fields – tenantId and objectId – that have GUID values. What are these properties, and can I avoid having GUID’s hardcoded into my ARM template?

Fortunately I can solve all of these problems, mostly using Azure Resource functions.

Generating a unique resource name

In my original ARM template I had specified the name as shown below:

"name": "myKeyVault"

Obviously this has the problem that it’s probably not unique. But I can make the name unique by suffixing the text with a unique value, such as one generated based on the parent resource group:

"name": "[concat('myKeyVault-', uniqueString(resourceGroup().id))]"

Generating the location based on the containing resource group

In my original ARM template, I had the region hardcoded in the way shown below:

"location": "UK South"

But there’s a helpful resource function built in which detects the location of the parent resource group, as shown below:

"location": "[resourceGroup().location]"

Generating the tenantId based on the subscription

The tenant Id is a Guid associated with the connected Azure account – you can find it out by opening a PowerShell instance and running the command below:

Connect-AzureRmAccount

You’ll be asked to log in, and after you present your credentials to Azure, the PowerShell prompt will display information like the tenantId. This is the GUID which appears in the original ARM template.

"tenantId": "DD1AA6D2-981F-4158-B30E-2511639CDB22"

But I don’t really want to commit this to my source code repository – and fortunately there’s a resource function that gets the tenant ID based on the account running the ARM template:

"tenantId": "[subscription().tenantId]"

What about the objectId?

The “objectId” GUID is associated with the user (or Principal) who has permission to interact with the Azure Key Vault.

I couldn’t find a Resource function that gave this to me – I’d love to know if there is one, please let me know in the comments if you find one! – so I managed this by creating a pipeline variable and passing it to the pipeline job.

How can I find the objectId value?

I found my principal Id using the following steps:

  • Open a PowerShell prompt, and run the command below:
Connect-AzureRmAccount
  • Log in as the user to whom you want to give Key vault permissions, and then run the command below
Get-AzureRmADUser

This will return information about the principals associated with this account, including the information listed below:

  • UserPrincipalName
  • DisplayName
  • Id
  • Type

The “Id” value is the GUID that I need to associate with my Azure Key Vault’s “objectId” parameter.

Parameterising the objectId in the ARM template

I altered the “parameters” section of my ARM template to look like this code:

"parameters": {
    "principalId": {
        "defaultValue": "null",
        "type": "string"
    }
},

So my ARM template’s resource section now looks like this:

"resources": [
    {
        "type": "Microsoft.KeyVault/vaults",
        "name": "[concat('myKeyVault-', uniqueString(resourceGroup().id))]",
        "apiVersion": "2015-06-01",
        "location": "[resourceGroup().location]",
        "properties": {
          "sku": {
            "family": "A",
            "name": "Standard"
          },
          "tenantId": "[subscription().tenantId]",
          "accessPolicies": [
            {
              "tenantId": "[subscription().tenantId]",
              "objectId": "[parameters('principalId')]",
              "permissions": {
                "keys": [ "All" ],
                "secrets": [ "All" ]
              }
            }
          ]
        }
    }
],

Now I can edit my pipeline and create a variable, which I’ve called “userprincipalid”, and populate this variable with my own principal identifier GUID:

azure pipeline principal id

And then I can reference this in my pipeline in the “Override template parameters” section, as highlighted below, with the code:

-principalId $(userprincipalid)

pipeline screenshot

Now when I run my pipeline, the stage that deploys an ARM template will create an Azure Key Vault with a unique DNS value, and I’ve removed enough hard-coded values that I’m happy to push the ARM template to my source code repository.

Wrapping up

Often the exported ARM template includes hard coded references to regions, subscriptions, and even GUIDs related to the user currently logged in – this isn’t really suitable for committing and pushing to a source code respository. But often Resource functions are available which can replace these hard-wired values, which leads to more maintainable ARM templates.

2 thoughts on “Create an Azure Key Vault using ARM templates, Resource Functions, and Azure Pipelines

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s