はじめに
需要があるのかないのか、怪しいエントリーです。
Function App 内にある HTTP トリガー関数の実行時に、別の関数を HTTP トリガーで実行するために必要なコードを取得してみます。
# ちょっと何言っているかわからないかもしれませんが。。。
本題
今回は、1 つの Function App に作成した、ある HTTP トリガー関数の実行中に、同じ Function App 内にある別の HTTP トリガー関数の実行を想定します。
必要な手順と参考にしたドキュメントは以下の通りです。
前準備
マネージド ID を有効にして、発行されたサービス プリンシパルに、予め対象の Function App に対する "Web サイト共同作成者" ロールを割り当てておきます。
docs.microsoft.com
これは実行時に、Azure の REST API を実行するために必要となります。
- Azure ポータルで Function App を開いて、"プラットフォーム機能" タブ内にある "ID" をクリックします。
- 表示された "識別" 画面内の "システム割り当て済み" タブ内の "状態" を "オン" にし、保存します
- "プラットフォーム機能" タブに戻り、"アクセス制御 (AIM)" をクリックします
- 表示された "Access Control" 画面内で、"+追加" -> "ロールの割り当ての追加" をクリックし、"役割" に "Web サイト共同作成者" 、"アクセスの割り当て先" に "Function App"、を選択し、今開いている Function App そのものを選択したうえで保存します。
"ロールの割り当て" タブ内で、先ほどのロールが "このリソース" に割り当てられていることを確認してください。
以上で前準備は終了です。
実行時
実行時に必要な処理は下記のとおりです。
- マネージド ID を使用して、Azure の REST API (ARM) 実行に必要なアクセス トークンを取得します。
docs.microsoft.com - 下記の Azure REST API を、1. で取得したアクセス トークンを使用して実行し、Function App の Admin Token を取得します。
docs.microsoft.com - Function Host の REST API を、2. で取得した Function App の Admin Token を使用して実行し、関数の実行に必要なキーを取得します。
github.com
下記コードは、TypeScript でのサンプル実装です。
Windows 版の Function App のみで動作を確認しました。
import { AzureFunction, Context, HttpRequest } from "@azure/functions" import fetch, { Response } from 'node-fetch'; const API_VERSION: String = '2017-09-01'; const RESOURCE_URL: String = 'https://management.azure.com/'; const FUNCTION_NAME_CALLEE: String = '<呼び出し先の関数名>'; const getMasterKey = async function(): Promise<String> { try { // step1: get access token using managed ID. // see https://docs.microsoft.com/ja-jp/azure/app-service/overview-managed-identity#using-the-rest-protocol let response:Response = await fetch(`${process.env.MSI_ENDPOINT}?api-version=${API_VERSION}&resource=${RESOURCE_URL}`, { headers: { secret: process.env.MSI_SECRET } }); let json:any = await response.json(); // set parameters from environment variables const [, subscriptionId] = process.env.WEBSITE_OWNER_NAME.match(/^([^+]*)+.*$/); const resourceGroup:String = process.env.WEBSITE_RESOURCE_GROUP; const functionApp:String = process.env.WEBSITE_SITE_NAME; // step2: get admin token for the function app using the access token // see https://docs.microsoft.com/ja-jp/rest/api/appservice/webapps/getfunctionsadmintoken response = await fetch(`https://management.azure.com/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Web/sites/${functionApp}/functions/admin/token?api-version=2016-08-01`, { headers: { Authorization: `Bearer ${json.access_token}` } }); json = await response.json(); // step3: get master key using the admin token // see https://github.com/Azure/azure-functions-host/wiki/Key-management-API#host-key-resource-adminhostkeyskeyname // # but wrong url /admin/host/keys/ in the document. response = await fetch(`https://${process.env.WEBSITE_HOSTNAME}/admin/functions/${FUNCTION_NAME_CALLEE}/keys/default`, { headers: { Authorization: `Bearer ${json}` } }); json = await response.json(); return json.value; } catch { return ''; } }; const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> { context.log('HTTP trigger function processed a request.'); const code:String = await getMasterKey(); const response:Response = await fetch(`http${masterKey?'s':''}://${process.env.WEBSITE_HOSTNAME}/api/${FUNCTION_NAME_CALLEE}?code=${code}`); context.res = { status: 200, /* Defaults to 200 */ body: await response.text() }; }; export default httpTrigger;
留意点
Azure の REST API (ARM) の URL に必要な、サブスクリプション ID やリソース グループ名、Function App 自体の名前は、今回は環境変数から取得していますが、Linux 版でこれが使えるのか、Windows 版でもこれが使い続けられるかは、正直わかりません。
(上記コードから抜粋)
// set parameters from environment variables const [, subscriptionId]:Array<String> = process.env.WEBSITE_OWNER_NAME.match(/^([^+]*)+.*$/); const resourceGroup:String = process.env.WEBSITE_RESOURCE_GROUP; const functionApp:String = process.env.WEBSITE_SITE_NAME;
まとめ
Function App で、HTTP トリガー関数をネストする時、各関数のキーをハードコード、もしくは、アプリケーション設定などに保存しておく必要がありましたが、上記の方法で実行ごとに動的に取得することが可能になります。
# HTTP リクエストが 3 回増える&そもそもそういう設計するのか、というのはありますが。。。