Azure Security Terraform

Azure Functions with Managed identity in Terraform

Azure Functions with Managed identity in Terraform

TL;DR
You can run Azure Functions with Managed Identity for assigning Azure and API permissions.

Jump to recipe

Azure Functions or Function Apps (I will use both in this post to mean the same resource) run in an App Service Plan, much like a regular Azure Web App. They use resources from this plan, and in my example a Consumption plan (Y1) is chosen for “pay as you go” or pay for what you use. If you use a Basic/Standard/Premium plan here you will get a fixed cost if you function app is running or not.

Mostly you would use Basic/Standard/Premium if you need something like virtual network integration, but Consumption plans are more than good enough for testing and ad-hoc purposes. In extreme cases you can also run Function Apps in an isolated environment.

You can read more about function apps here. If you need more long-running tasks, you can also use durable functions, which runs stateful functions in a serverless compute environment as opposed to the regular stateless functions.

Managed Identities

Managed Identities provide many Azure resources with an associated Azure AD identity. This works by Microsoft creating, updating, and deleting an invisible service principal for us in Azure AD. We don’t create Client Secrets here, and neither do we need them! Authentication is done in different ways, and you can read more about the flow here.

System Assigned Managed Identities

System Assigned Managed Identity means that the identity is entirely created, updated, and deleted by Azure automatically. You enable the feature on a supported resource, and the resource gets automatically registered in Azure AD, and it can authenticate against other Azure resources (like Azure Key Vaults, ARM API, or Azure Storage). You get a principal_id, and this can be used to grant access on Azure resources. This identity is associated to the Azure resource lifecycle, and will be removed if the resource is deleted.

User Assigned Managed Identities

User Assigned Managed Identity enable you to deploy a managed identity as a standalone resource in Azure. This  could then be re-used for several other resources, and you manage the resource yourself in that you can create, update, or delete it. Microsoft still handles any secret/certificate rotations behind the scenes. No Client Secrets to be found here!

Deployment with Terraform

For deploying these resources I have put them all into a module called “app”. This is to abstract away some of the complexity, and input only necessary information to the module. I can also redeploy this module in different environments with different naming, and they will be deployed identically. Please note that this module is not production ready, and I would make it more flexible for use in a real scenario. You can use it as a starting point, or just an example.

This module creates:

  • A Resource Group for containing resources
  • An App Service Plan for compute and scaling
  • Azure Functions resource for running your code
  • Application Insights for logging
  • Key Vault for storing secrets
  • Virtual Network for vnet integration
  • Storage Account for storing the function app runtime and files

You call the module with the bare necessary variables because of some smart defaults.

Deployment steps:

  • Download the functionapp folder with the web based VSCode from github
  • Navigate to the downloaded folder.
  • Authenticate with Az CLI to your subscription.
  • Run terraform init
  • Run terraform plan
  • Run terraform apply

After this your Function Apps with supporting infrastructure have been created!

Managed Identity

The managed identity part is configured by the identity block in the app-module:

In this case I added a “System Assigned” on the dev-app and a “User Assigned” on the test-app for examples sake. The module allows for some flexibility here, but could be further improved to allow no identity deployed.

Runtime deployment with VSCode

After the function app resources are deployed, you have the bare bones necessary for deploying an actual PowerShell function app. The resources are only there to enable deployment of an app, and there is no actual runtime code there yet.

The function app runtime code is not something you want to deploy with Terraform, and is better suited handled in a separate folder. I have opted to keep the code in the same repository, but this is not necessary. This function app could have been deployed from a different repository altogether.

This example will list all your resource groups, and tell you if they are empty. Just a simple example of how it can be used for automating tasks.

The Function App

When the function app is deployed, it will be triggered periodically by cron notation. This is a way of noting schedules which can be confusing for some. Luckily @techparida has made a nice cheat sheet for us, which plainly states how to use it.

I will not go into the actual inner working of this function app runtime. It has been created with the VSCode Azure Functions extension, which makes creating new functions a breeze! Just create the skeleton for your function, and add PowerShell runtime.

In the function app all you need to connect using the managed identity is these simple commands:

Disable-AzContextAutosave -Scope Process | Out-Null Connect-AzAccount -Identity

The “Disable-AzContextAutosave” does exactly what it says. It disables autosave of your login. These lines are added automatically to your profile.ps1, but I have had some difficulties making it run predictably for my function app. Therefore I sometimes also add them to my function app code to be sure.

Deploy Function App

Download the src-folder to a local folder on your client, and open the folder in VSCode.

Sign in to Azure with the Azure Account VSCode extension.

Use Ctrl+Shift+P (The Command Palette) and choose “Deploy to function app”:

If your deployment fails (mine failed with ECONNRESET), you may need to update your Az CLI to the latest version. I had version 2.29.0 (old, I know..), and had to upgrade to 2.39.0 for it to work correctly.

In summary

You have now deployed an Azure Function App with a System Assigned Managed Identity for RBAC and authorization. This identity could easily be granted permissions in Azure AD and perform other scheduled tasks requiring API permissions there.

The Managed Identity part of Azure is a great way of removing our dependence on explicit credentials here and there. Use it for authenticating all the supported resources, and you will be relying a bit less on stored secrets.