Skip to content

Commit b54e5cc

Browse files
committed
Wrote about my adventures with calling an azure function from an azure automation runbook with powershell
Signed-off-by: David Söderlund <[email protected]>
1 parent c831160 commit b54e5cc

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
---
2+
title: Calling an Azure Function from an Azure Automation Runbook
3+
published: true
4+
excerpt_separator: <!--more-->
5+
---
6+
7+
In this post I will explain how authentication in Azure Functions compare to authentication in Azure resources in general. This allows us to invoke a function from an Azure Automation Runbook.
8+
9+
<!--more-->
10+
11+
Too long didn't read: Azure functions authentication requires that the accessToken audience matches tha application id of the IDP tied to the function app. So even Azure resources whose managed service identities are granted access should create access tokens issued by the tenant, and with the for the audience of the Azure function, not the Azure resource management (`aud: https://management.core.windows.net/`) like one might think.
12+
13+
## Background
14+
15+
I have a customer who I helped previously build simple tools for automation of their infrastructure due to new customers coming in to their multi tenant solution. As part of the infrastructure they may need one or more of:
16+
17+
- DNS records
18+
- tenant configuration in a database
19+
- new databases
20+
- keycloak organizations
21+
- Azure web app custom hostname with certificate binding
22+
23+
This has worked well and good but they recently wanted to add some functionality and used Azure functions for, intending to migrate my old powershell scripts from runbooks to there to build a more powerful and flexible dashboard. This dashboard app will for obvious reasons have a nicer time invoking Azure functions than Azure automation runbooks.
24+
25+
Naïvly I just thought I would use something like `Connect-AzAccount` in directly in powershell and set the tenant and subscription - or use `az account get-access-token` to get something I could use to make the request, but I was met by failure.
26+
27+
``` powershell
28+
$AzureFunctionUrl = "https://somefunction-somenounce.region.azurewebsites.net/api/SomeFunction"
29+
$accessToken = az account get-access-token --query accessToken | ConvertFrom-Json
30+
$headers = @{
31+
'Content-Type' = 'application/json'
32+
'Authorization' = "Bearer $accessToken"
33+
}
34+
$jsonSomeBody = @{"once"="toldme"} | ConverTo-Json
35+
Invoke-RestMethod -Method Post -Uri $AzureFunctionUrl -Body $jsonSomeBody -ContentType 'application/json' -Headers $headers
36+
```
37+
38+
> Invoke-RestMethod: You do not have permission to view this directory or page.
39+
40+
## The problem that you might also have
41+
42+
So we are dealing with an azure function, it is not publicly available, nor available with an apikey, so we need to authenticate to be able to have our call authorized. Yet the normal way one might think of getting such an auth token is invalid. We have to go deeper.
43+
44+
As a normal user with a frontend in a browser or mobile app, usually this isn't that hard to wrap your head around when building an application leveraging Azure functions or any serverless platform. Your functions/applications are their own thing separate from your Azure Resource Management and you will use the `/.auth/aad` or whic hever federation you set up. The user will navigate the flow and come out the other end with an access token.
45+
46+
You could do the same via a device flow to get an intermediate identity token that you can then send to Azure to get a working access token for the application.
47+
48+
You and me though, dear reader, are not any normal user. We are a powershell runtime running in Azure automation runbooks under a managed service account whose been granted access to the function. It is not feasible to use either device flow or a browser flow to authenticate. So what can we do?
49+
50+
## The insight
51+
52+
The first thing we need to realize is that we are not authenticating with the same endpoint as for Azure Resource Management which colloquially gets refered to as "Azure" in devops circles. It is per tenant and the resource we want matches the audience that is required.
53+
54+
Putting the pieces together and looking closely at the details of the Azure function I was asked to work on I figured that the authentication for it needs to look a certain way, very different from the tokens I usually work with for Azure Resource Management.
55+
56+
![idp-sometenant-someissuer](../assets/2025-05-15_18-44-36-idp-sometenant-someaudience.png)
57+
58+
Once we are equipped with that knowledge, we can find that Microsoft has tucked in environment variables into Azure automation runbooks that helps us resolve endpoints in the runtime that acts as proxies for authentication to the resources, using their application Id as a reference.
59+
60+
[This is documented here](https://learn.microsoft.com/en-us/Azure/app-service/overview-managed-identity?tabs=portal%2Cpowershell#connect-to-azure-services-in-app-code), but it took me quite a lot of time and google-fu to find it because as I didn't really understand what I was really searching for.
61+
62+
## The solution
63+
64+
Using the internally accessible REST endpoint for token retrieval, a working solution can be constructed as so.
65+
66+
``` powershell
67+
$AzureFunctionUrl = "https://somefunction-somenounce.region.azurewebsites.net/api/SomeFunction"
68+
$appid = 'cc334b87-fdbe-4996-aa3f-5bad192a18a6' # some random guid representing somefunction application id
69+
$accessToken = Invoke-RestMethod -Method Get `
70+
-Headers @{"X-IDENTITY-HEADER"="$env:IDENTITY_HEADER"} `
71+
-Uri "$( $env:IDENTITY_ENDPOINT )?resource=$($appid)&api-version=2019-08-01" `
72+
| Select-Object -ExpandProperty access_token
73+
$headers = @{
74+
'Content-Type' = 'application/json'
75+
'Authorization' = "Bearer $accessToken"
76+
}
77+
$jsonSomeBody = @{"once"="toldme"} | ConverTo-Json
78+
Invoke-RestMethod -Method Post -Uri $AzureFunctionUrl -Body $jsonSomeBody -ContentType 'application/json' -Headers $headers
79+
```
80+
81+
## Further reading and acknowledgements
82+
83+
I read [this fantastic article by Rakhesh Sasidharan](https://rakhesh.com/azure/authenticating-against-azure-functions-using-azure-ad/) which helped explain to me what was going on. While their elegant solution for device flow wasn't useful it was fun to play around with and helped me bridge the gap of my knowledge about oidc to how Azure does things.
84+
85+
[I also came across this article by Krizzia Relente](https://medium.com/@relente/how-to-use-managed-identity-to-authenticate-azure-functions-70b92cb710f2) which I didn't really parse as the solution to my problem because I read it before having a solid understanding of the problem actually was (the insight was that it is not about runbooks but about managed identities).
86+
95.5 KB
Loading

0 commit comments

Comments
 (0)