Retrieve Azure Storage access keys in ARM template

Being a big fan of the Infrastructure as Code (IaC) paradigm, I create all my Azure resources as Azure Resource Manager (ARM) templates. Using an IaC approach, our Azure environments are described as code, allowing us to deploy services in an automated and consistent manner. Often different resources reference other resources, which can introduce the need for authentication. Sometimes we can use Azure Active Directory identities for this (explained more detailed in a later post), however at other times this may require some other form of secrets to set up communication.

Of course, we could create a resource, get the secrets out manually, and then pass them into the referencing resource’s template. However, the idea of Infrastructure as Code, especially when combined with a CI/CD strategy, is to have a minimal amount of manual steps. So instead, let’s have a look at how we can retrieve these secrets from our ARM templates. Consequently, this allows us to set these secrets at deployment time, without any manual interference needed. This article is the first in a series of blog posts, each focusing on a different service, starting with Azure Storage access keys in this post.

Azure Storage Connection String
The connection string which we are going to retrieve

Scenario

For our scenario, we create a Logic App API Connection called blobstorage, used to collect messages from a blob container. Seeing how API connections do not expose their secrets, this is an ideal candidate to pass in the connection string, created using an access key, via an ARM template. After all, we do not want to expose our confidential information to anyone not authorized to view these. The Logic App looks like the following, receiving a file, after which it starts processing.

Logic Apps flow retrieving files from Blob Storage using API connection.
Logic App with Blob Storage trigger

Retrieving secrets

To retrieve secrets in an ARM template, like the access key we are going to work with today, we use list* functions. These include operations such as listDetails, listkeys, and listsecrets, and allow us to fetch different properties, such as secrets, from various Azure services. In this case we use the Storage Accounts’ List Keys functionality, that is going to return the keys and their properties for our Storage account.

The listkeys function requires two inputs, the resource ID of the Storage account and the API version to use. We retrieve the resource ID using the resourceId function, while the API version is available for reference on Microsoft Docs.

"variables": {
    "storageAccountId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
}

We could also get the latest API version using the apiVersions property of the providers function. However, as the API outputs can change over time, and thus break our template, it is advised to use a static api-version instead. For reference though, we would implement retrieving the API version dynamically in a template as following.

"storageAccountApiVersion": "[providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]]"

Now that we have the input for the listkeys functions, we can use these to retrieve the primary key during the creation of the API connection using the following ARM snippet.

{
    "type": "Microsoft.Web/connections",
    "apiVersion": "2018-07-01-preview",
    "name": "[parameters('storageConnectionName')]",
    "location": "[parameters('location')]",
    "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
    ],
    "properties": {
        "api": {
            "id": "[concat(subscription().id,'/providers/Microsoft.Web/locations/', parameters('location'), '/managedApis/azureblob')]"
        },
        "parameterValues": {
            "accountName": "[parameters('storageAccountName')]",
            "accessKey": "[listKeys(variables('storageAccountId'), '2019-04-01').keys[0].value]"
        },
        "testLinks": [
            {
                "requestUri": "[uri('https://management.azure.com:443/', concat('subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/connections/', parameters('storageConnectionName'), '/extensions/proxy/testconnection?api-version=2018-07-01-preview'))]",
                "method": "get"
            }
        ]
    }
}

Summary

When deploying the solution, the template now reaches out to the Storage account, get the primary key, and use this to create the API connection for the Logic App. As such, the API connection is authenticated once deployed, meaning we don’t have any manual steps to do. The full template for this solution, including the storage account, API connection, and the Logic App, is available on Azure Resource Manager QuickStart Templates.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.