BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Microsoft Graph API で ユーザー 情報を操作する ( C#, .NET SDK )

Microsoft Graph API を使って Azure Active Directory (略して Azure AD とか AAD) からユーザーの情報を操作するのってかなり昔から使ってるんですがブログにしたことがなかったので改めて書いてみようと思いました。

今回は C# の SDK を使ったユーザーの操作をメモします。

ちなみに認証フローは、Web API の中から管理系の操作をしたいので Client Credentials フローを使います。Web API から Graph API をコールするときは On Behalf Of を使うことも多いと思います。

今回は Graph API と SDK の利用がメインなので認証フロー周りは細かく話しませんが、シナリオと認証フローの選択についてはこちらのドキュメントを見るとわかりやすいと思います。

docs.microsoft.com

事前準備: Azure AD で Microsoft Graph のアクセス許可を追加

Microsoft Graph API は、デフォルトではログインしているユーザーのプロファイルしか取得できません。そのため、例えばユーザーの一覧を取得したいとか自分以外のユーザーの情報を取得するにはアクセス許可を追加してあげる必要があります。

アクセス許可の設定方法は、前回のブログにまとめましたので不明な場合はご参考ください。

blog.beachside.dev

今回は以下3つの情報が必要になりますので準備しておく必要があります(これも含め上記のブログに書いてあります)。

  • テナント ID
  • クライアント ID
  • シークレット

以降では特にアクセス許可には触れず SDK を使った操作を中心に書くので、認可関連のエラーが出たときは必要に応じて設定すれば解決できます。具体的には、一番最後にドキュメントのリンクを付けています。やりたいこと(「ユーザーの一覧を取得する」とか「ユーザーを招待する」とか)のドキュメントを開くとどのアクセス許可が必要か書いてますので、それを設定していく流れです。

余談: SDK の変遷

2020年前半に Service library ( .NET だと Microsoft.Graph ) と Core library ( .NET だと Microsoft.Graph.Core) に分かれた構成になりました。

この2つの責務を軽く説明すると、

  • Service library: Graph API の metadata の更新(頻繁にある)に合わせて機能を更新していく。将来的には metadata からコードを自動生成できるようにしたいらしい(あくまで将来的なゴール)。
  • Core library: インフラ周り、 reliability や resilient とかの実装とか。コア機能なのでリリースサイクルはゆっくり。手で書くコードはこっちのみにしたい。

この構成への変更前は、Microsoft.Graph.Beta に preview 機能があって GA 版が Microsoft.Graph っていう微妙に扱いにくい構成だったのでいい感じに変わってくれた印象です。

コンソールアプリの作成

冒頭で Client Credentials フローを使うと書きましたが、イメージしやすいようにユースケースの例を書くと、ユーザーを管理している Web API があり、その API が Graph API を使ってユーザーの登録とかを行うイメージです。

Web API のサンプルコードを書くと DI とか余計なことを書きたくなって脱線するので、コンソールアプリを作って進めます。

まずは Visual Studio 2019 を開いてコンソールアプリを作るだけなので手順は省略します。

.NET のターゲットフレームワークは .NET Core 3.1 を使います。最新の .NET 5でも NuGet Package に依存するとこはとくにないので問題ないですが、状況的にまだ私は .NET 5 を利用する気になっていないからです。

NuGet パッケージのインストール

Microsoft.GraphMicrosoft.Graph.Auth の2つを使います。 Visual Studio で NuGet パッケージの管理を開き「 Microsoft.Graph」と検索すると表示されますのでインストールしましょう。注意点として、このブログを書いた時点だと Microsoft.Graph.Auth は pre-release 中なので プレリリースを含める にチェックを入れてあげる必要があります。

一応このブログで使ったバージョンは以下です。

  • Microsoft.Graph: v3.28.0
  • Microsoft.Graph.Auth: 1.0.0-preview6

あとはコード書いていくだけです♪

GraphServiceClient の初期化

Microsoft Graph API にアクセスするには、GraphServiceClient という client を使ってアクセスします。初期化方法はこんな感じ。コード内の TODO 3か所は前述の「事前準備: Azure AD で Microsoft Graph のアクセス許可を追加」で触れた Azure AD から取得する情報です。

Client credentials 以外での初期化方法はこちらの公式ドキュメントを参考にさくっと作れます。

ユーザーの情報を取得

ユーザーの一覧取得

ユーザーの一覧はこれで取得できます。ドキュメント通りです。

var users = await graphServiceClient.Users.Request().GetAsync();

foreach (var user in users)
{
    Console.WriteLine($"{user.Id} - DisplayName: {user.DisplayName} (Email: {user.Mail})");
}

Select を使って指定したプロパティだけを取得することもできます。余計なプロパティを取得することでレスポンスのサイズが無駄に大きくなります。

そのため基本的には Select を使って最小限のプロパティのみを取得することが必須と考えましょう。

var users = await graphServiceClient.Users.Request().Select("id,displayName").GetAsync();

Select の中を文字列ではなく Func にすることで Type safety に指定することもできます。

var users = await graphServiceClient.Users.Request().Select(u => new { u.Id, u.DisplayName }).GetAsync();

ObjectId を指定したユーザーの取得

Azure Portal では Azure AD のユーザーの一意の ID は Object ID と表記されています。ASP.NET Core とかで認証してログインした場合はこの ID も取得できます。この ID をキーにユーザーのプロファイルを取得する際は以下のようになります。

// TODO: user の ObjectIdをセット
var user1Id = "";

var user1 = await graphServiceClient.Users[user1Id].Request().GetAsync();

ユーザーが所属しているグループを取得

MemberOf を使ってユーザーがどのメンバーに属しているかを取得できます。

// TODO: user の ObjectIdをセット
var user1Id = "";

var memberOf = await graphServiceClient.Users[user1Id].MemberOf.Request().GetAsync();

取得した結果は以下図の感じになります。ユーザーの一覧を取得したときも同様ですが CurrantPage の中にほしい情報が入ってきます。これは Microsoft Graph の特徴のひとつです。CurrantPage は DirctoryObject 型となっていますが雑に説明すると IList<T> の Generics で色んな型のオブジェクトが入ってきます。

デバッグして CurrantPage の中をみるとわかりますが、ユーザーが Group だけじゃなくなんらかの Role に属してると両方の情報が取得できます。

foreach (var obj in memberOf)
{
    switch (obj)
    {
        case DirectoryRole role:
            Console.WriteLine($"Role {role.Id}: {role.DisplayName}");
            break;
        case Group group:
            Console.WriteLine($"Group: {group.Id}:{group.DisplayName} ({group.Description})");
            break;
    }
}

例えば Group だけを取得したい場合、OfType を使って以下のように取得することができます。

foreach (var group in memberOf.OfType<Group>())
{
    Console.WriteLine($"Group: {group.Id}:{group.DisplayName} ({group.Description})");
}

その他の操作

SDK を利用する上での癖というか特徴はこれくらいです。あとの操作はドキュメントを見ながらやれば前述のやり方を考慮するだけでサクッとできます。

user リソースの種類 - Microsoft Graph v1.0 | Microsoft Learn

ユーザーを招待する

Azure AD にユーザーを追加する場合、私の場合はユーザーを新規に作成することはほぼなく、招待する方が多いです。そりゃそうですよね、新たにアカウント作ってパスワードも管理するなら、既存のどっかのアカウントを招待することでアカウントを増やすことなくすむので。

ということでユーザーを招待してみましょう。

以下のコードでは TODO が2つ、招待する相手のアカウントの Email とリダイレクトする URL をセットする必要があります。また、招待メールを送信するには SendInvitationMessagetrue にしてあげる必要があります。

// TODO: 招待を送るユーザーのメールをセット
 var targetUserEmail = "";
// TODO: 同意後、リダイレクトする URL をセット
 var inviteRedirectUrl = "";

var invitation = new Invitation()
{
    InvitedUserEmailAddress = targetUserEmail ,
    InviteRedirectUrl = inviteRedirectUrl ,
    InvitedUserMessageInfo = new InvitedUserMessageInfo() {MessageLanguage = "ja-jp"},
    // default: false。true をセットしないと招待メールは送信されない
    SendInvitationMessage = true
};

// 招待を実行
var response = await graphServiceClient.Invitations.Request().AddAsync(invitation);
// レスポンスから、採番されたユーザーの ID が確認可能
Console.WriteLine($"OID: {response.InvitedUser.Id}; InvitedEmail:{response.InvitedUserEmailAddress}");

レスポンスからユーザーの ID がわかるので、このユーザーを後続処理で Group に追加するときなどの操作に使えます。

招待メールは、Azure AD から招待を受けたことがある人なら見覚えのありそうなのが届きます。招待の承諾をすると、inviteRedirectUrl に設定した URL にリダイレクトします。

あとは動作で気になる点としては...

  • 招待を同じ Email に複数回送信しても、新なユーザーは作られず同じ ID に再送するだけとなります。
  • 招待を承諾した後に再度招待メールを送信しても同様です。
  • Invtation を作る際、InvitedUserMessageInfo の中の MessageLanguage のdefault は en-us とドキュメントにありますが、動作を確認したところこの値を設定しなくても日本語でメールが送られてきました。Azure AD の Notification language を English に変えてもかわらんかったで、デフォルト値はどっか別のところにありそうですね(もしくは変更の反映に時間がかかるとか?)。まぁセットしておけば間違いないでしょう。

終わりに

Microsoft Graph の操作も、C# の SDK の癖さえわかってしまえば簡単にできます♪

Group の操作については次のブログで整理しました。

blog.beachside.dev

参考

docs.microsoft.com

docs.microsoft.com