Azure Functions から Microsoft Graph API を利用する
Azure Functions から Microsoft Graph API を呼び出すといったことを何度か試したのですが、IDやパーミッション周りで毎回嵌るので、記事として残しておきます。
今回作成するアプリケーション
Azure Functions の HTTPトリガーで動作する関数を作成します。利用言語はC#です。
関数では Microsoft Graph API を利用して、ユーザ一覧を取得し、レスポンスとして返却します。
Microsoft Graph API の利用に認証が必要ですが、Microsoft ID プラットフォームにアプリを登録してシークレットを払い出して認証を行います。
プロジェクトの作成
下記を参考にプロジェクトを作成します。
今回はVisual Studio Codeで作りました。(Visual Studioが使えるならば、Visual Studioの方が準備の手間が少なくて済みます)
プロジェクトの作成でデフォルトを選んでいくと、HTTPトリガーによるプロジェクトが作成されます。
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;
namespace Company.Function
{
public static class HttpTrigger1
{
[FunctionName("HttpTrigger1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
}
Run and Debug から実行すると、ローカルで関数を実行することができます。
Graph API の呼び出し
Graph SDK を使うことで、Graph API を簡単に呼び出せます。
NuGetでMicrosoft.Graph
とAzure.Identity
をインストールします。
Azure.Identity
は、認証のために利用します。
Credential
を作成し、作成したCredential
でGraphServiceClient
を生成します。
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential);
あとはGraphServiceClient
からリソース名を指定して取得します。
var result = await graphClient.Users.GetAsync();
今回はユーザ一覧を取得していますが、Graph APIでは様々な操作が行えます。
Graph API を呼び出すようにした関数のコード全体は下記の通りです。
using System;
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 Microsoft.Graph;
using System.Text;
using Azure.Identity;
namespace Company.Function
{
public static class HttpTrigger1
{
[FunctionName("HttpTrigger1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var tenantId = Environment.GetEnvironmentVariable("TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET");
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential);
var result = await graphClient.Users.GetAsync();
StringBuilder stringBuilder = new StringBuilder();
foreach(var user in result.Value) {
stringBuilder.AppendLine($"{user.Mail}: {user.DisplayName}");
}
return new OkObjectResult(stringBuilder.ToString());
}
}
}
Graph API の認証について
今回は、Microsoft ID プラットフォームにアプリを登録し、クライアントシークレットを利用することで認証するようにしています。
クライアントシークレット発行までの流れは下記の通りです。
(1) アプリの登録
「Azure Active Directory」→「アプリの登録」でアプリを登録します。
今回はAzure Functionsから利用するので、アカウントの種類は「この組織ディレクトリのみに含まれるアカウント (XXXX のみ - シングル テナント)」を選びます。
リダイレクトさせるような利用はしないのいで、リダイレクトURIは省略します。
(2) APIのアクセス許可
登録したアプリに対して、APIのアクセス許可を設定します。
「API のアクセス許可」から、「アクセス許可の追加」で「Microsoft Graph」を選びます。
「アプリケーションに必要なアクセス許可の種類」は「アプリケーションの許可」を選び、利用するAPIに必要な権限を追加します。
今回はユーザ一覧を参照したいので、User.Read.All
を指定しました。
管理者の同意が必要になるので、管理者の同意を与えます。(自分が管理者でない場合は、管理者に依頼します)
Graph APIで、アクセス許可として何が必要になるかは、下記が参考になります。
(3) クライアントシークレットの発行
「証明書とシークレット」から、クライアントシークレットを作成します。
作成したシークレットの値は、1度しか表示されない(移動して戻ると確認できない)ので、ここでメモします。
認証時に必要になるのは、このシークレットの値と、クライアントID、テナントIDです。
クライアントIDとテナントIDは概要から確認できます。
認証情報の管理について
先ほど下記の3つが認証に必要と書きましたが、
- テナントID
- クライアントID
- クライアントシークレット
これらをソースコードにそのまま記載するのは望ましくないので、環境変数として取得するようにしています。
var tenantId = Environment.GetEnvironmentVariable("TENANT_ID");
var clientId = Environment.GetEnvironmentVariable("CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET");
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
ローカルで実行する場合には、local.settings.json
に環境変数を設定しておきます。
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"TENANT_ID": "aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb",
"CLIENT_ID": "12345678-9012-3456-7890-123456789012",
"CLIENT_SECRET": "fffff~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdegfh"
}
}
Azure Functions上で実行する場合には、「関数アプリ」→「構成」→「アプリケーション設定」で設定します。
Discussion