Loading...

Using secretless Azure Functions from within AKS

Using secretless Azure Functions from within AKS

I recently implemented a change in KEDA (currently evaluated as a potential pull request), consisting of leveraging managed identities in a more granular way, in order to adhere to the least privilege principle. While I was testing my changes, I wanted to use managed identities not only for KEDA itself but also for the Azure Functions I was using in my tests. I found out that although there are quite a few docs on the topic, none is targeting AKS:

 

https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-queue-trigger?tabs=csharp#identity-based-connections

https://docs.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob#connecting-to-host-storage-with-an-identity-preview

 

You can find many articles showing how to grab a token from an HTTP triggered function, or using identity-based triggers, but in the context of a function hosted in Azure itself. It's not rocket science to make this work in AKS but I thought it was a good idea to recap it here as I couldn't find anything on that.

 

Quick intro to managed identities

Here is a quick reminder for those who would still not know about MI. The value proposition of MI is: no password in code (or config). MI are considered best practices because the credentials used by identities are entirely managed by Azure itself. Workloads can refer to identities without the need to store credentials anywhere. On top of this, you can manage authorization with Azure AD (single pane of glasses), unlike shared access signatures and alternate authorization methods.

AKS & MI

For MI to work in AKS, you need to enable them. You can find a comprehensive explanation on how to do this here. In a nutshell, MI works the following way in AKS:

 

aksnmi.png

 

 

An AzureIdentity and AzureIdentityBinding resource must be defined. They target a user-assigned identity, which is attached to the cluster's VM scale set. The identity can be referred to by deployments through the aadpodbinding annotation. The function (or anything else) container makes a call to the MI system endpoint http://169..., that is intercepted by the NMI pod, which in turn, performs a call to Azure Active Directory to get an access token for the calling container.  The calling container can present the returned token to the Azure resource to gain access.

 

Using the right packages for the function

The packages you have to use depend on the Azure resource you interact with. In my example, I used storage account queues as well as service bus queues. To leverage MI from within the function, you must:

  • use the Microsoft.Azure.WebJobs.Extensions.Storage >= 5.0.0
  • use the Microsoft.Azure.WebJobs.Extensions.ServiceBus >= 5.0.0
  • use the Microsoft.NET.Sdk.Functions >= 4.1.0

Note that the storage package is not really an option because Azure Functions need an Azure Storage account for the most part.

Passing the right settings to the function

Azure functions takes their configuration from the local settings and from their host's configuration. When using Azure Functions hosted on Azure, we can simply use the function app settings. In AKS, this is slightly different as we have to pass the settings through a ConfigMap or a Secret. To target both the Azure Storage account and the Service Bus, you'll have to define a secret like the following:

 

data: AzureWebJobsStorage__accountName: <base64 value of storage account name> ServiceBusConnection__fullyQualifiedNamespace: <base64 value of the service bus FQDN> FUNCTIONS_WORKER_RUNTIME: <base64 value of the function language> apiVersion: v1 kind: Secret metadata: name: <secret name> ---
In the above example, I use the same storage account for my storage-queue trigger as well as the storage account that is required by functions to work. In case I was using a different storage account for the queue trigger, I'd declare an extra setting with the account name. The service bus queue-triggered function relies on the __fullyQualifiedNamespace to start listening to the service bus. Paradoxally, although I create a K8s secret, there is no secret information here, thanks to the MI.
 
For your reference, I'm pasting the entire YAML here:
 
data: AzureWebJobsStorage__accountName: <base64 value of the storage account name> ServiceBusConnection__fullyQualifiedNamespace: <base64 value of the service bus FQDN> FUNCTIONS_WORKER_RUNTIME: <base64 value of the function code> apiVersion: v1 kind: Secret metadata: name: misecret --- apiVersion: aadpodidentity.k8s.io/v1 kind: AzureIdentity metadata: name: storageandbushandler annotations: aadpodidentity.k8s.io/Behavior: namespaced spec: type: 0 resourceID: /subscriptions/.../resourceGroups/.../providers/Microsoft.ManagedIdentity/userAssignedIdentities/storageandbushandler clientID: <client ID of the user-assigned identity> --- apiVersion: aadpodidentity.k8s.io/v1 kind: AzureIdentityBinding metadata: name: storageandbushandler-binding spec: azureIdentity: storageandbushandler selector: storageandbushandler --- apiVersion: apps/v1 kind: Deployment metadata: name: busandstoragemessagehandlers labels: app: busandstoragemessagehandlers spec: selector: matchLabels: app: busandstoragemessagehandlers template: metadata: labels: app: busandstoragemessagehandlers aadpodidbinding: storageandbushandler spec: containers: - name: secretlessfunc image: stephaneey/secretlessfunc:dev imagePullPolicy: Always envFrom: - secretRef: name: misecret ---
You can see that the secret is passed to the function through the envFrom attribute. If you want to give it a test, you can use the docker image I pushed to Docker Hub.
 
and the code of both functions, embedded in the above docker image (nothing special):
[FunctionName("StorageQueue")] public void StorageQueue([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {myQueueItem}"); } [FunctionName("ServiceBusQueue")] public void ServiceBusQueue([ServiceBusTrigger("myqueue-items", Connection = "ServiceBusConnection")] string myQueueItem, ILogger log) { log.LogInformation($"C# Queue trigger function processed: {myQueueItem}"); }
 
You just need to make sure the connection string names you mention in the triggers correspond to the settings you specify in the K8s secret.

Published on:

Learn more
Azure Developer Community Blog articles
Azure Developer Community Blog articles

Azure Developer Community Blog articles

Share post:

Related posts

This Month in Azure Static Web Apps | 09/2024

    We are back with another edition of the Azure Static Web Apps Community! :party_popper:   September was yet another month ...

1 year ago

GitHub Copilot for Azure: 6 Must-Try Features

As developers, we are constantly seeking tools that streamline our workflows and boost productivity. … Enter GitHub Copilot for Azure, now in ...

1 year ago

Responsible AI Mitigation Layers

Generative AI is increasingly being used in various kinds of systems to augment humans and infuse intelligent behavior into existing and new a...

1 year ago

Streamline Your Azure Workflow: Introducing GitHub Copilot for Azure in VS Code

I'm excited to announce the public preview of GitHub Copilot for Azure - a new addition to your toolkit that seamlessly integrates with G...

1 year ago

Build Intelligent Apps Code-First with Prompty and Azure AI

      Building Generative AI applications can feel daunting for traditional app developers. What does the end-to-end applicati...

1 year ago

Certificación AI-900 (Fundamentos de IA) con Chicas en IA

La inteligencia artificial ha llegado para quedarse, ¡y más aún con la revolucionaria IA generativa! Para ayudar a los profesionales a especia...

1 year ago

Get certified with Learn Live GitHub series!

GitHub Universe is coming, and Microsoft and GitHub are partnering to offer a new special Learn Live series in Brazilian Portuguese, English a...

1 year ago

Certifícate con Learn Live GitHub en Español

Microsoft y GitHub se han unido para ofrecer una nueva serie especial de Learn Live en inglés y español: GitHub 2024. Del 10 al 24 de Octubre,...

1 year ago

Evaluating generative AI: Best practices for developers

As a developer working with generative AI, you've likely marveled at the impressive outputs your models can produce. But how do you ensure the...

1 year ago

Introducing Azure Product Retirement Livestreams

The Azure Retirements team, in collaboration with key partner groups, is excited t...

1 year ago

Newsletter

Get the latest Dynamics 365 and Power Platform content in your inbox

A curated digest of community blogs, product news, videos, and podcasts — delivered without the noise.

Weekly updates Unsubscribe anytime Fresh community picks
We use your email only for the newsletter and you can unsubscribe at any time.
By subscribing, you agree to the privacy policy.