Azure Functions and Azure Storage: secure authentication with Managed Identities and without managing keys!
For detailed documentation of Azure topics covered in this article, take a look at Azure Functions, Managed Identities documentation, and Azure CLI. To follow through examples, get started with Free Trial.
In this article you will learn about using Managed Identities for secure and convenient approach to authenticate to Azure services from Serverless Functions without managing or storing any access keys or credentials!
We will create an Azure Function, obtain an access token from local service identity endpoint, and we will use the access token in the request to a file on Azure storage account.
To learn about why it is a good idea to use Managed Identities and how it can help make access to Azure resources more secure and less error-prone visit this page <- it has an overview and an example with Azure Linux VMs.
TL;DR: Managed Identities can significantly improve authentication process between cloud resources, reduce operational overhead and vulnerability of managing and storing authentication credentials.
Create a Storage Account
$storage_account to real values, the use the code below to create a new storage account and upload a file that we're going to request from the Azure Function later in the article.
az storage account create \ --resource-group $storage_group \ --name $storage_account \ --sku Standard_LRS# Take note of storage account Resource ID! export AZURE_STORAGE_CONNECTION_STRING=`az storage account show-connection-string --resource-group $storage_group --name $storage_account -o tsv`az storage container create --name test echo "hello" >> text.txtcat text.txtaz storage blob upload \ --container-name test \ --file text.txt --name text.txt
Create a new Azure Functions App
We will be working with Azure Functions App in F#. For more information about using F# in Azure Functions check out the details here.
I am a big fan of F#, but feel free to use any other language supported by Azure Functions, overall process should be identical with a few language specific differences.
By the end of this step remember the name and resource group of your Azure Function.
Enable Managed Identity for the newly created Azure Function
Azure Functions work with system-assigned Managed Identities. Before we can use it with Azure Functions we first need to enable the feature.
First snippet will simply enable it, but the function won’t be granted any roles to access any resources.
export functionName="<name-of-azure-function>" export functionGroup="<name-of-azure-function-resource-group>"az webapp identity assign \ --name $functionName \ --resource-group $functionGroup
If you’d like to enable Managed Identity and grant permissions to the Azure Function with one command, there’s another option.
export storageAccountResourceId="/subscriptions/XYZ/resourceGroups/<storage-account-resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account-name>"export storageAccountRoleToGrant="Storage Blob Data Reader (Preview)" export functionName="<name-of-azure-function>" export functionGroup="<name-of-azure-function-resource-group>"az webapp identity assign \ --resource-group $functionGroup \ --name $functionName \ --role $storageAccountRoleToGrant \ --scope $storageAccountResourceId
You will find two new environment variables called
MSI_SECRET after executing one of the snippets.
Code for the Azure Function in F#
As in the previous article, there are two main steps: requesting access token, and accessing the service providing the access token (a storage account in this case). However, there are a few differences with Azure Functions that are worth mentioning.
MSI_ENDPOINT is a URL from which an Azure Function can request tokens.
MSI_SECRET is required as a
secret parameter to HTTP GET request to this endpoint, along with
The following code shows end-to-end example of accessing Azure storage account through system-assigned Managed Identity and reading contents of a file stored on the storage account.
Full snippet is here.
Don’t forget to add FSharp.Data package to your dependencies.
... "FSharp.Data": "2.4.6" ...
For those of you interested in using an existing .NET library for requesting an access token, refer to this guide.
My example specifically uses REST API and not the Microsoft.Azure.Services.AppAuthentication library for .NET because this way you should be able to implement this functionality in any language other than .NET by just looking at what F# code does, as the implementation prototype is transparent.
Execution results with incorrectly assigned role and scope
Execution results with correctly assigned role and scope
Thank you for reading!
I hope this was useful, and if so — mention me on Twitter — I’ll be happy to hear your thoughts! In the next parts we will learn about managed identities with Azure Kubernetes Service!
Originally published at lenadroid.github.io.