😀
Azure Functions の Easy Auth で Azure REST API をユーザー権限で実行する環境を
Azure Functions の認証機能を使ってユーザー権限で何かしらの自動化処理を行う場合、Microsoft Graph API なら簡単に利用できます。しかしながら、Azure REST API を使おうとすると scope
に設定したい https://management.azure.com/user_impersonation
はどこに設定するのかわからなかったので、いろいろ試しながら .NET で検証環境を作ってみました。
ローカルの環境情報
bash
$ func --version
4.0.4629
$ dotnet --version
6.0.300
$ az version
{
"azure-cli": "2.43.0",
"azure-cli-core": "2.43.0",
"azure-cli-telemetry": "1.0.8",
"extensions": {}
}
Azure Functions を作成
bash
prefix=mnrfuncaad
region=japaneast
az group create \
--name ${prefix}-rg \
--location $region
az storage account create \
--name ${prefix}stor \
--resource-group ${prefix}-rg \
--sku Standard_LRS
az functionapp create \
--name ${prefix} \
--resource-group ${prefix}-rg \
--consumption-plan-location $region \
--runtime dotnet \
--functions-version 4 \
--storage-account ${prefix}stor \
--disable-app-insights \
--https-only \
--os-type Linux \
--assign-identity
サービスプリンシパルを作成
bash
appid=$(az ad app create \
--display-name ${prefix} \
--web-redirect-uris https://${prefix}.azurewebsites.net/.auth/login/aad/callback \
--enable-id-token-issuance true \
--sign-in-audience AzureADMyOrg \
--required-resource-accesse '[
{
"resourceAccess": [
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
},
{
"id": "37f7f235-527c-4136-accd-4a02d197296e",
"type": "Scope"
},
{
"id": "14dad69e-099b-42c9-810b-d002981feec1",
"type": "Scope"
},
{
"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182",
"type": "Scope"
}
],
"resourceAppId": "00000003-0000-0000-c000-000000000000"
},
{
"resourceAccess": [
{
"id": "41094075-9dad-400e-a0bd-54e686782033",
"type": "Scope"
}
],
"resourceAppId": "797f4846-ba00-4fd7-ba43-dac1f8f63013"
}
]' \
--query appId \
--output tsv)
apppw=$(az ad app credential reset \
--id $appid \
--append \
--display-name ${prefix} \
--years 100 \
--query password \
--output tsv)
Azure Fnctions の認証設定
bash
az webapp auth config-version upgrade \
--name ${prefix} \
--resource-group ${prefix}-rg
az webapp auth microsoft update \
--name ${prefix} \
--resource-group ${prefix}-rg \
--client-id $appid \
--client-secret $apppw \
--issuer https://sts.windows.net/$(az account show --query tenantId --output tsv)/v2.0 \
--yes
az resource update \
--set properties.globalValidation='{
"redirectToProvider": "azureactivedirectory",
"requireAuthentication": true,
"unauthenticatedClientAction": "RedirectToLoginPage"
}' \
--ids $(az webapp auth show \
--resource-group ${prefix}-rg \
--name ${prefix} \
--query id \
--output tsv)
az resource update \
--set properties.identityProviders.azureActiveDirectory.login='{
"disableWWWAuthenticate": false,
"loginParameters": [
"response_type=code id_token",
"scope=email openid profile https://management.azure.com/user_impersonation"
]
}' \
--ids $(az webapp auth show \
--resource-group ${prefix}-rg \
--name ${prefix} \
--query id \
--output tsv)
ローカルで Functions を作成
bash
func init ${prefix} --dotnet
cd ${prefix}
func new --name test --template "HTTP trigger" --authlevel "anonymous"
func start
test.cs を編集
test.cs
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Security.Claims;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
using Microsoft.Identity.Web;
namespace mnrfuncaad
{
public static class test
{
[FunctionName("test")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log)
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + req.Headers["X-MS-TOKEN-AAD-ACCESS-TOKEN"]);
var response = await httpClient.GetAsync("https://management.azure.com/tenants?api-version=2020-01-01");
var responseMessage = response + "\n";
if (response.StatusCode == HttpStatusCode.OK)
{
JObject tenants = JObject.Parse(await response.Content.ReadAsStringAsync());
foreach (var value in tenants.GetValue("value"))
{
responseMessage = responseMessage + "tenant: " + value["tenantId"] + "\n";
}
}
return new OkObjectResult(responseMessage);
}
}
}
必要なパッケージを追加
bash
dotnet add package Microsoft.Identity.Web
func start
Azure Functions にデプロイして動作確認
bash
func azure functionapp publish ${prefix}
open https://${prefix}.azurewebsites.net/api/test
Discussion