TL;DR
You can run Azure Functions with Managed Identity for assigning Azure and API permissions.
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 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 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 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!
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:
You call the module with the bare necessary variables because of some smart defaults.
Deployment steps:
After this your Function Apps with supporting infrastructure have been created!
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.
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.
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.
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.
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.