Using Managed Identity to Authenticate Azure Functions and APIM
This is example code showing how to authenticate from Azure Functions to Azure API Management (APIM) and from Azure API Management to Azure Functions using a managed identity.
Content
File/folder | Description |
---|---|
src |
Azure Function C# Code |
terraform |
Terraform IAC Code |
.gitignore |
Define what to ignore at commit time. |
CHANGELOG.md |
List of changes to the sample. |
CONTRIBUTING.md |
Guidelines for contributing to the sample. |
README.md |
This README file. |
LICENSE.md |
The license for the sample. |
Features
This example covers the following use cases:
- Azure Function App to Azure API Management authentication using a Managed Identity.
- Azure API Management to Azure Function App authentication using a Managed Identity.
Important Notes
There are currently some limitations when using Managed Identity for these use cases that you should be aware of:
Access Tokens are cached for 24 hours and there is no way to override this. This means that any changes you make to your App Registration with regards to app roles, claims, etc will not be available in the token generated from the MSI enabled app for up to 24 hours. This can be quite problematic when developing, but should not have a significant impact on production workloads. More details on this limitation can be found here and here. HINT: When developing with Terraform, you can just delete your Managed Identity and let Terraform re-create it to refresh your token.
Managed Identities (any Service Principal) cannot be assigned to an App Registration App Role via an AzureAD Group. This is a major limitation for customers that want to use groups across the board, but it does not mean you can't use Managed Identities. It simply means you have assign the Managed Identity directly the the App Registration App Role. More details on this limitation can be found here. Specifically the sentence
Currently, if you add a service principal to a group, and then assign an app role to that group, Azure AD doesn't add the roles claim to tokens it issues.
.
Getting Started
Prerequisites
- The Azure Functions Core Tools CLI: Download
- HashiCorp Terraform CLI: Download
- Azure CLI: Download
- An Azure Subscription: Free Account
Installation
- Clone this repository to your local machine
Quickstart
We deploy the infrastructure using Terraform first. Follow these steps to login to Azure and apply the Terraform.
- Open your console of choice and navigate to the the
/terraform
folder. - Run
az login
to login to your Azure Account. - Run
az account set --subscription [my-subscription]
where [my-subscription] is the name of your subscription. - Run
terraform init
to pull down the providers. - Run
terraform apply
. Take a look at the plan it generates and then typeyes
to run the plan. - The function code is deployed as part of the Terraform using a local provisioner which runs the
func azure functionapp publish
command of the Azure Functions Core Tools CLI. - Take note of the outputs from Terraform as you'll need these later.
- Take a look at the resource groups and resources in the Azure portal.
Demos
Each use case has it's own demo as follows:
Azure Function to APIM Authentication
This following diagram provides an overview of the demo for this use case:
For the Function App to APIM authentication use case, the most important parts can be found in these files:
The Azure Function code follows these steps:
- Get the user assigned managed identity.
- Generate a JWT from the user assigned managed identity, passing in the App Registration scope in the case of the
group
example. - Make a call to the APIM end point, passing the JWT in the Authorization Bearer header.
There are two different examples of the APIM Policy:
The simple
function does not use an App Registration and generates a generic JWT for itself:
- It uses the
validate-azure-ad-token
policy type. - It specifies that only the client ID of the user assigned managed identity for the trusted public function app can access the operation. It does this by including it in the
client-application-ids
list.
The group
function uses an App Registration so it can be assigned an App Role and pass that claim to APIM:
- It uses the
validate-jwt
policy type. - It specifies that only the App Registration audience is valid. It does this by including it in the
audiences
list. - It specifies that only the App Role claim assigned to the Managed Identity is valid. It does this by including it in the
required-claims
list.
To run the demo, follow these steps:
- Simple Untrusted: Navigate to the untrusted function app, using the url in
public_untrusted_simple_demo_url
. You should see a 401 error like this:{ "statusCode": 401, "message": "Invalid Azure AD JWT" }
. - Simple Trusted: Navigate to the trusted function app, using the url in
public_trusted_simple_demo_url
. You should see a successful call like this:{"message":"Hello from the Private Function! The APIM Managed Identity has been assigned to the role: Private.Example","dateOfMessage":"2022-11-08T12:14:59.4735675+00:00"}
. - Group Untrusted: Navigate to the untrusted function app, using the url in
public_untrusted_group_demo_url
. You should see a 401 error like this:{ "statusCode": 401, "message": "Unauthorized. Access token is missing or invalid." }
. - Group Trusted: Navigate to the trusted function app, using the url in
public_trusted_group_demo_url
. You should see a successful call like this: `{"message":"Hello from the Private Function! The APIM Managed Identity has been assigned to the role: Private.Example","dateOfMessage":"2022-11-08T12:14:59.4735675+00:00"}
As you can see the untrusted function does not have it's managed identity specified in the APIM policy, so it is not authenticated. More details on the policy can be found here.
Azure API Management to Azure Function
This following diagram provides an overview of the demo for this use case:
For the APIM to Function App authentication use case, the most important parts can be found in these files:
- APIM Policy definition: apim.tf
- Azure Function Authentication Configuration: function_private.tf
- AzureAD App Registration: azuread_applications.tf
The APIM Policy includes the following steps:
- It uses the
authentication-managed-identity
policy to retrieve the JWT to authenticate. It specified the client ID of the App Registration in theresource
attribute and the client ID of the user assigned managed identity in theclient-id
attribute. - It takes the output of the previous step and adds the JWT to the Authorization Bearer header using the
set-header
policy.
The AzureAD App Registration has the following attributes:
- It is configured using the instructions defined here in the documentation.
- It is configured to require users or groups to be assigned using the
app_role_assignment_required
property. - It has the user assigned managed identity assigned to the default role.
- This combination of settings ensures that only the user assigned managed identity we specified has access to the Private Function.
The Private Function has the following attributes:
- It is configured to have AzureAD authentication enabled using the App Registration described in the previous section
- The enforces that only authenticated users / services can access the Function App.
To run the demo, follow these steps:
- Navigate to the trusted function app, using the url in
public_trusted_demo_url
.You should see a successful call like this:{"message":"Hello from the Private Function! The APIM Managed Identity has been assigned to the role: Private.Example","dateOfMessage":"2022-11-08T12:14:59.4735675+00:00"}
. - Open Azure Portal and navigate to your APIM instance.
- Open the
APIs
section and click on theUntrusted API
entry. - Select the
Test Operation
and open theTest
tab. - Hit
Send
and you will get a 500 error. - Hit
Trace
, then navigate to the Trace and scroll down to theauthentication-managed-identity
section. - You will see details of why a 500 error was produced and it is because the user assigned managed identity is not assigned to the App Registration.