PowerShellからGraph API を「委任されたアクセス許可」で利用する

#はじめに
Graph APIを使う方法を調査すると「アプリケーションのアクセス許可」で実装する例は多くみつかるのですが、「委任されたアクセス許可」の例はほとんどでてきません。

Graph API を「委任されたアクセス許可」で利用する方法を調べたので備忘録を兼ねて投稿します。

#そもそも「委任されたアクセス許可」と「アプリケーションのアクセス許可」の違いは何か？

一言で違いを説明すると以下の通りです。

- 「委任されたアクセス許可」 ・・・ 指定したユーザーの権限でAPIを使用する
- 「アプリケーションのアクセス許可」 ・・・ AzureADアプリに設定した権限でAPIを使用する

このあたりをとても分かりやすい資料でまとめてくださっている方がいました。私はこちらを拝見して理解しました。ｱﾘｶﾞﾄｳｺﾞｻﾞｲﾏｽ  (*ᴗˬᴗ)⁾⁾

https://jpo365ug.connpass.com/event/142896/presentation/

#「アプリケーションのアクセス許可」のデメリット

アクセス許可の設定を「アプリケーションのアクセス許可」に限定してしまうことには以下のようなデメリットがあります。（要件によってはメリットもありますがこの投稿では割愛します）

- APIからのレスポンスを実行ユーザーが製品側で付与されている権限でトリミングすることができない
- 上記と同じ理由で、実行側に許可する操作を細かく調整することができない。理由：トリミングができないのでリクエストの条件に一致するデータをすべて返す設定にせざるを得ない
-  「保護されたAPI」にあたるAPIを使う場合はAzureADアプリ単位でMSに利用申請を出す必要がある


#どうやって「委任のアクセス許可」を使うか

以下の3つのステップで実現します

1. AzureADアプリを登録する
1. 作業端末に「MSAL.PS」モジュールをインストールする
1. PowerShellでAPIを呼び出す過程でアクセストークンを使用する

## 手順1: AzureADアプリを登録する

AzureADアプリの登録を行います。
ここでは例として以下の2つのアクセス許可を「委任されたアクセス許可」で付与します。
・User.Read.All
・Group.Read.All

AzureADポータル開いて「アプリの登録」画面で「新規登録」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/038bbe4e-b250-e484-9bc3-938cfcdc0f67.png)

「名前」欄でAzureADアプリの名前を入力します。任意の文字列を大丈夫です。わかりやすい名前にしましょう。
「サポートされているアカウントの種類」で誰がAPIが使用できる範囲を指定します。テナント外から利用させる要件がなければ、一番上にある「この組織ディレクトリのみ～」を指定してください。
「リダイレクトURL」は入力は不要です。
ここまで入力したら画面最下部の「登録」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/1d0734dc-ac13-506e-6886-15f771e531eb.png)

「概要」画面を開きます。以下の2つのIDをコピーしてメモにひかえます。

- アプリケーション（クライアント）ID
- ディレクトリ（テナント）ID
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/e9c1b6cc-7ba6-7522-47b2-b2ca7274902f.png)

「認証」画面を開きます。「プラットフォームを追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/cb333eed-f3a7-ddcc-922b-a44bdede1cdd.png)

「モバイル アプリケーションとデスクトップ アプリケーション」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/fb4cb3c7-471c-6612-5323-3af551add55f.png)

「リダイレクトURL」にある「msal～」のチェックボックスをオンにします。
画面最下部の「構成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/590cac94-f695-0d13-e8fa-d7d4618ae8fc.png)

「クリックボードにコピー」をクリックして、リダイレクトURLをメモにひかえます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/59847bfe-b305-6490-ab11-30b118109494.png)

「APIのアクセス許可」画面を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/2b2fdd20-36e0-1252-3629-1125f84f6293.png)

既定で付与されていた「User.Read」は使わないので削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/f85071db-59c5-068a-a24a-4152bb902dc2.png)

まずは、User.Read.Allのアクセス許可を付与します。

「アクセス許可の追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/fe1d5491-c888-de5e-db91-3284143ec17e.png)

「Microsoft Graph」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/bf770fc2-7801-588f-dded-8a009a08368f.png)

「委任されたアクセス許可」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/64d09f5b-5df1-8bfd-5bc3-d3a6d9cf6f6e.png)

付与したいアクセス許可（ここでは User.Read.All）を検索ボックスに入力します。画面下部に表示されたアクセス許可のうち、目的のもの（ここでは User）をクリックして開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/5f712e0c-16fd-1baf-7c85-b769d83d71ec.png)

付与したいアクセス許可のチェックボックスをオンにします。画面最下部の「アクセス許可の追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/30f789e1-ae82-1d0b-d66b-d48bad3b383b.png)

これで、User.Read.Allのアクセス許可を付与できました。
次に、Group.Read.Allのアクセス許可を付与します。

「アクセス許可の追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/ca0fd75d-7e6d-e6ae-665a-b1c59b403073.png)

「Microsoft Graph」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/bf770fc2-7801-588f-dded-8a009a08368f.png)

「委任されたアクセス許可」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/64d09f5b-5df1-8bfd-5bc3-d3a6d9cf6f6e.png)

付与したいアクセス許可（ここでは Group.Read.All）を検索ボックスに入力します。画面下部に表示されたアクセス許可のうち、目的のもの（ここでは Group）をクリックして開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/8fad3e78-70b2-bf5b-c4a6-3a6225864b69.png)

付与したいアクセス許可のチェックボックスをオンにします。画面最下部の「アクセス許可の追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/6f1d42e4-6dee-05a3-6708-71905a2e040c.png)

「（テナント名）に管理者の同意を与えます」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/711f1567-e294-b546-9643-1c0e8b87193d.png)

「管理者の同意の確認を与えます」というメッセージが表示されたら「はい」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/d156f711-a134-c61b-9cb6-134155c7c419.png)

アクセス許可の「状態」欄に「(テナント名) に付与されました」と表示されたことを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/9af3913c-d8d6-4d7d-c8d5-100ec43d2dfb.png)

これでAzureADアプリの作成は以上です。

## 手順2: 作業端末に「MSAL.PS」モジュールをインストールする

作業端末でPowerShellを管理者権限で起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/17fee44d-ec0b-c819-a296-24c2cacad5d8.png)

以下のコマンドを実行します。

```powershell:作業端末に「MSAL.PS」モジュールをインストールする
Install-Module -Name MSAL.PS
```

これでモジュールのインストールは以上です。

## 手順3: PowerShellでAPIを呼び出す過程でアクセストークンを使用する

作成したAzureADアプリを使って、PowerShellからAPIを呼び出します。

以下はスクリプトの例を示します。以下の3点はメモにひかえた内容に置き換えてください。

- アプリケーション（クライアント）ID
- ディレクトリ（テナント）ID
- リダイレクトURL


またこの例では、付与したアクセス許可のうちUser.Read.Allを使用します。
Group.Read.Allの権限を使用した例については後述します。

```powershell:スクリプトの例：AzureAD上のすべてのユーザーを取得
#-------------------------------------------------
#入力情報
#-------------------------------------------------
#テナントのID
$tenantID = "（メモにひかえたID）"

#AzureADアプリ
$clientID = "（メモにひかえたID）"

#MSALのリダイレクトURL
$MSALRedirectUri = "（メモにひかえたリダイレクトURL）"

#-------------------------------------------------
#認証情報を取得
#------------------------------------------------- 
#トークンを取得
$token = Get-MsalToken `
-TenantId    $tenantID `
-ClientId    $clientID `
-RedirectUri $MSALRedirectUri `
-Interactive

$headerParams = @{
    'Authorization' = "bearer $($token.AccessToken)"
}

#-------------------------------------------------
#ユーザー情報を取得
#-------------------------------------------------
$url = "https://graph.microsoft.com/v1.0/users/"

$responseFromAPI = Invoke-RestMethod `
-uri     $url `
-Method  Get `
-Headers $headerParams 

$users = @()
$users += $responseFromAPI.value

$pages = $responseFromAPI.'@odata.nextLink'

while([string]::IsNullOrEmpty($pages) -eq $false)
{
    $addtional = Invoke-RestMethod `
    -uri     $pages `
    -Method  Get `
    -Headers $headerParams
     
    $users += $addtional.value

    $pages = $addtional."@odata.nextLink"
}
```
実行すると、認証情報の入力を求められます。
APIを呼び出すユーザーのIDとパスワードを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/a1c71db5-ed53-9be5-0b12-736925adc249.png)

処理が完了しました。
期待した通りレスポンスがかえってきたことを確認します。

```powershell
$users | select UserPrincipalName
```

期待する結果は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357828/d4632292-8a95-49f9-6ac9-74135c69b7e5.png)

#Group.Read.Allの権限を使用した例

Group.Read.Allの権限を使用した例をご紹介します。
User.Read.AllとGroup.Read.Allの権限があると、下記の記事に記載しているMicrosoft Teamsのいいねを一覧取得することができます。

下記の記事では「アプリケーションのアクセス許可」を記載しましたが、「委任されたアクセス許可」を使っても実現することができます。
もちろん、ソースコードのほうも手直しをする必要がありますが、この投稿に例として記載しましたソースコードをみていただければ直す箇所はわかってもらえるかと思います。読み比べてみてください。

繰り返しになりますが、「委任されたアクセス許可」を使用した方法であれば以下のメリットがあります。
- 取得できるデータの対象を実行ユーザーがアクセスできる範囲に絞ることができる。（つまり、実行ユーザーがメンバに含まれていないチームのデータへのアクセスはブロックできる）
- 「保護されたAPI」ではないため、MSに申請をする必要がない。

https://qiita.com/wataruf01/items/a4aa72c65721f1a5dd68

#最後に
この投稿で例として挙げたユーザー情報取得スクリプトは実行するたびに実行ユーザーのIDとパスワードの入力が求められます。
ですが、コマンドの使い方をしらべたところWindows認証、保存した資格情報、証明書による認証は使えそうにです。そのため、定期的に自動実行するツールでもこの方法は活用できそうです。（実現性の裏付けはしていないので、ご参考までにお願いします。）