Azure AD(Microsoft Entra ID) の新しいインバウンドプロビジョニング API のリリース


# Azure AD(Microsoft Entra ID) API 駆動型インバウンド プロビジョニング

個人的に ID エンジニアなら大切だとわかるけど、ID エンジニア以外にいまいちピンと来てもらえない ID 系話題 No1 はプロビジョニングです。

4/1 や 10/1 の度にやってくる大量の人事発令にげんなりし、前後の土日に死んだ表情で休日出勤をする、という経験は ID エンジニアなら皆あるはず。


当然、Azure AD([改め Microsoft Entra ID](https://news.microsoft.com/ja-jp/2023/07/12/230712-azure-ad-is-becoming-microsoft-entra-id/)) でもプロビジョニングは ID 基盤を下支えする重要なタスクではあるのですが、もうちょっと機能が増強されて欲しいな、と思っている領域でもあります。

特に、前述の 4/1 や 10/1 に受ける事になる Inbound プロビジョニングはオンプレミス AD や Workday などの限られた製品からの処理であればいいのですが、そうではない場合、Graph API による個別開発などを頑張る必要がありました。

今月、この [Inbound プロビジョニング](https://learn.microsoft.com/ja-jp/azure/active-directory/app-provisioning/inbound-provisioning-api-concepts) を強化する機能がリリースされたため、ご紹介したいと思います。

# 機能概要
この機能は [Graph API](https://learn.microsoft.com/ja-jp/graph/api/synchronization-synchronizationjob-post-bulkupload?view=graph-rest-beta&tabs=http#example-1-bulk-upload-using-scim-core-user-and-enterprise-user-schema) として Inbound プロビジョニングを受け付けるように実装されています。その際に様々なデータソースに対応できるように、[SCIM のスキーマ](https://datatracker.ietf.org/doc/html/rfc7643) でプロビジョニングの要求を受け付けられるようになっています。
　受け付けた要求に対して、属性の変換処理を Azure AD 側でやる事もできますし、エージェントを利用する事で、Azure AD のエンドポイントで受けたユーザープロビジョニング要求をオンプレミス AD に反映させる事も可能です(その場合は、Azure AD には AAD Connect か Cloud Sync で同期する事になります)

これにより、特に独自の人事データベースや複数の ID 製品を併用しているお客様で Azure AD への Inboud プロビジョニングの自由度が上がる事が期待されます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/133609bd-217e-b24f-2372-e658eab61a41.png)

なお、現時点の実装としては、[SCIM の CRUD に対応しているわけではなく](https://learn.microsoft.com/ja-jp/azure/active-directory/app-provisioning/inbound-provisioning-api-faqs#is-the-inbound-provisioning-bulkupload-api-a-standard-scim-endpoint)、あくまで Bulk Operation としての受け付けになっていますのでその点は注意が必要です。

それでは構築手順を紹介していきます。


# 構築手順

## 0.参考ドキュメント

[API 駆動型インバウンド プロビジョニング アプリの構成](https://learn.microsoft.com/ja-jp/azure/active-directory/app-provisioning/inbound-provisioning-api-configure-app)
[インバウンド プロビジョニング API へのアクセスを付与する](https://learn.microsoft.com/ja-jp/azure/active-directory/app-provisioning/inbound-provisioning-api-grant-access)
[cURL を使用した API 駆動インバウンド プロビジョニングのクイックスタート ](https://learn.microsoft.com/ja-jp/azure/active-directory/app-provisioning/inbound-provisioning-api-curl-tutorial)
[Perform bulkUpload(Graph API リファレンス)](https://learn.microsoft.com/ja-jp/graph/api/synchronization-synchronizationjob-post-bulkupload?view=graph-rest-beta&tabs=http)


## 1. API 駆動型プロビジョニングアプリの作成
まずは、Azure AD 側でプロビジョニングの処理(要求を受けて Azure AD にユーザー改廃を行うアプリケーションを作成します。公式ドキュメントなんかでは、API 駆動型プロビジョニングアプリと呼ばれているようです。

1. [Microsoft Entra Portal](https://entra.microsoft.com/) にアクセスします。

1. [Azure Active Directory] -> [アプリケーション] -> [エンタープライズ アプリケーション] を参照します。[新しいアプリケーション] をクリックして、新しいプロビジョニング アプリケーションを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/04fdfb0e-4ea9-da43-e26c-1f07002e24dd.png)

1. 検索フィールドに「API-driven」と入力し、セットアップするアプリケーションを選択します。今回はオンプレミス AD を考慮せず、Azure AD へのプロビジョニングを行います.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/75e01bcc-a93d-b10e-cfa8-93f90dce3ec5.png)


1. [名前] フィールドで、任意の名前を変更し、[作成] をクリックします。アプリケーションは複数作成する事が可能です.例えば、取り込み時のソースに合わせて変換規則を変える場合などです。その場合は取り込元などがわかりやすい名前にしておくと良いでしょう.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/33241475-5aeb-606f-63b4-c5b46db15e3d.png)


1. アプリケーションの作成が正常に完了したら、[Privisoning] に移動し、[Get started] をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/0ac04945-5f75-d6c8-b682-ec37ab29717e.png)


1. [プロビジョニング モード] を [手動] から [自動] に切り替えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/34d03c2d-f8a2-9544-9919-e6d774cc2289.png)

1. 保存が完了すると、さらに 2 つの拡張パネル ([マッピング] 用と [設定] 用) が表示されます。 次の手順に進む前に、有効な通知メールアドレスを指定し、構成をもう一度保存する必要があります。※通知メールアドレスは必須の模様
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/dd6ee34b-3288-8f3d-7cc2-f12c9956618d.png)

1. マッピングでは SCIM のスキーマから Azure AD へのプロパティ取り込みとして基本のセットが設定されています.この規則は ID データソースに合わせてカスタマイズする事も可能です.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/879d08b5-bffd-a81c-3799-ab9d4001782a.png)

1. マッピングの完了後、プロビジョニング アプリケーションの [プロビジョニング] ->[概要] ページを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/e7798cf7-0353-1848-e6ec-89a345a46349.png)

1.以下の Provisoning API Endpoint を控えておきます.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/34f4e3c3-3533-8ad3-7f53-c07cde231cc1.png)



## 2. API 実行用アプリケーションを登録してアクセスを付与する
続いて、前の手順で[作成した API 駆動型プロビジョニングアプリ](#1-api-駆動型プロビジョニングアプリの作成) に対して API を実行用のアプリケーションを作成し、API 実行許可を与えます。

1. グローバル管理者またはアプリケーション管理者のログイン資格情報を使用して、Microsoft Entra ポータル (https://entra.microsoft.com) にログインします。
1. [Azure Active Directory] ->[アプリケーション] ->[アプリの登録] に移動します。
1. オプション [新規登録] をクリックします。アプリ名を指定し、既定のオプションを選択して、[登録] をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/ba1bce39-70a5-992c-4255-60631665e8c1.png)

1. [概要] から [アプリケーション (クライアント) ID] と [ディレクトリ (テナント) ID] の値をコピーし、後で API クライアントで使用できるように保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/73f103e3-6c81-b7c6-0004-3b68cf107b4b.png)

1. アプリのコンテキスト メニューで、[Certificates & secretes] を選択します。
1. 新しいクライアント シークレットを作成します。 シークレットと有効期限の説明を入力します。
1. 生成されたクライアント シークレットの値をコピーし、後で API クライアントで使用するために保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/47117380-76ae-23b9-ce80-8ceb356c6120.png)

1. [API Permissions] から、オプション [権限の追加] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/f8399619-7e09-e523-ddac-acef437f9a52.png)


1. [API 権限の要求] で、[Microsoft Graph] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/6363b79b-d00e-4c49-d129-747965bdcb9a.png)

1. [アプリケーションのアクセス許可] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/7919b096-911b-efbc-97d2-5396c4924841.png)

1. [AuditLog.Read.All] と [SynchronizationData-User.Upload] を検索して選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/cd56057a-2412-ce1c-d7aa-0956cf2a1afb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/5ab47172-08e0-62fd-9113-72da364d0d33.png)


1. 次の画面で [管理者の同意の付与] をクリックして、権限の割り当てを完了します。 確認ダイアログで [はい] をクリックします。 アプリには次の権限セットがあるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/350a8b8c-f0e5-b18b-3e3a-154521724754.png)

## 3. Provisoning を試す

### 3-1 トークンの取得
アクセストークンを取得します。色々な取得方法がありますが、PowerShell のスクリプトのサンプルを置いておきます。
※極力簡単にトークンを取得する事を目的としてるため、Windwos 10 の標準コマンドレットでの取得を行っています。実環境で継続的に運用するのであれば、[MSAL](https://github.com/AzureAD/MSAL.PS) モジュールなどを利用して、証明書を使ったトークンを利用する事などを検討した方が良いでしょう。

```PowerShell:Get-Token.ps1
# param

$appID = <Your Client ID>
$sec = <Your Secrets>
$tenantID = <Your TenantID>
[System.Uri]$tokenurl = "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" 

#
# get access token
#

$headers = @{
  "Content-Type" = "application/x-www-form-urlencoded"}

$params = @{
  "client_id"=$appID; 
  "scope"="https://graph.microsoft.com/.default"; 
  "client_secret"=$sec;
  "grant_type"="client_credentials"}


$res = Invoke-RestMethod -Method POST -body $params -Uri  $tokenurl
$res.access_token
```
結果は以下のように長い文字列のアクセストークンが取得できるのでこの内容を控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/4d304ceb-4518-8c39-6033-43c85e7f76fe.png)



### 3-2 プロビジョニングの実行

今回は [postman](https://www.postman.com/) を利用して プロビジョニング API の実行を行います。
Web のリクエストを実行出来れば特になんでも良く、公式のドキュメントでは [cURL](https://learn.microsoft.com/ja-jp/azure/active-directory/app-provisioning/inbound-provisioning-api-curl-tutorial) を利用していますが、本記事では直観的にわかりやすいため、Postman を紹介しています。

1.  Postman を初めて利用する場合は Sign Up を行ってください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/2117b5cf-e0b5-054e-7543-cbb4b8af3ae4.png)


1. サインインアップとサインインが完了したら上部メニューから Worksapces をクリックして、[Create Workspace] を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/98b6bf87-3290-ffd1-34b4-fd1f309df94a.png)

1. デフォルトの内容で問題ないので、[Next] をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/85c06ee9-9d07-be08-5058-ba34900bc31b.png)

1. 目的がわかりやすい名前を付けておくと便利です。[Who can access your workspace?] は "Personal" を選択して[Create] をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/7654cdd4-8a1d-5130-c3af-f7f077042042.png)

1. Workspace が開いたら[+]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/436ed9d2-afc7-a4b5-81ef-0da668343419.png)

1. Postman の [Authorization] 設定を開き、 "Bearer Token" を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/b94eb65a-ff23-5bc6-2b2f-faf9c2236b76.png)

1. [アクセストークンの取得手順](#3-1-トークンの取得)で控えておいたトークンを指定します。(有効期限は 1 時間なので注意。改めてこのタイミングで取り直しても良いでしょう)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/891f34a2-9ab4-4dc2-954f-a0f98331e95a.png)

1. [Headers] 設定を開き、Content-Type の指定をします。Key : **Content-Type** / Value : **application/scim+json**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/5e2780a3-4bc7-d726-7387-0fd78b9e1c1a.png)

1. [Body] 設定を開き、要求の payload を指定します。種類は "row" に指定します。 指定後、URL 欄にエンドポイントの API を指定し、メソッドを "POST" にして[Send]をクリックします。けっかとして、"202"　が返ってくると成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/569454c8-0fea-60b0-ec30-f5e406dd6c26.png)


サンプルデータはまずは API の[リファレンスページ](https://learn.microsoft.com/ja-jp/graph/api/synchronization-synchronizationjob-post-bulkupload?view=graph-rest-beta&tabs=http#example-1-bulk-upload-using-scim-core-user-and-enterprise-user-schema)から取得すると良いでしょう。

以下の例では以下の 2 ユーザーを作成しています。
bjensen@example.com
Kjensen@example.com

```JSON:users.json
{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
    "Operations": [
    {
        "method": "POST",
        "bulkId": "897401c2-2de4-4b87-a97f-c02de3bcfc61",
        "path": "/Users",
        "data": {
            "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User",
            "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],
            "id": "2819c223-7f76-453a-919d-413861904646",
            "externalId": "701984",
            "userName": "bjensen@example.com",
            "name": {
                "formatted": "Ms. Barbara J Jensen, III",
                "familyName": "Jensen",
                "givenName": "Barbara",
                "middleName": "Jane",
                "honorificPrefix": "Ms.",
                "honorificSuffix": "III"
            },
            "displayName": "Babs Jensen",
            "nickName": "Babs",
            "emails": [
            {
              "value": "bjensen@example.com",
              "type": "work",
              "primary": true
            }
            ],
            "addresses": [
            {
              "type": "work",
              "streetAddress": "234300 Universal City Plaza",
              "locality": "Hollywood",
              "region": "CA",
              "postalCode": "91608",
              "country": "USA",
              "formatted": "100 Universal City Plaza\nHollywood, CA 91608 USA",
              "primary": true
            }
            ],
            "phoneNumbers": [
            {
              "value": "555-555-5555",
              "type": "work"
            }
            ],
            "userType": "Employee",
            "title": "Tour Guide",
            "preferredLanguage": "en-US",
            "locale": "en-US",
            "timezone": "America/Los_Angeles",
            "active":true,
            "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
                "employeeNumber": "701984",
                "costCenter": "4130",
                "organization": "Universal Studios",
                "division": "Theme Park",
                "department": "Tour Operations",
                "manager": {
                  "value": "89607",
                  "$ref": "../Users/26118915-6090-4610-87e4-49d8ca9f808d",
                  "displayName": "John Smith"
                 }
            }
        }
    },
    {
        "method": "POST",
        "bulkId": "897401c2-2de4-4b87-a97f-c02de3bcfc61",
        "path": "/Users",
        "data": {
            "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User",
            "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],
            "id": "2819c223-7f76-453a-919d-413861904646",
            "externalId": "701985",
            "userName": "Kjensen@example.com",
            "name": {
                "formatted": "Ms. Kathy J Jensen, III",
                "familyName": "Jensen",
                "givenName": "Kathy",
                "middleName": "Jane",
                "honorificPrefix": "Ms.",
                "honorificSuffix": "III"
            },
            "displayName": "Kathy Jensen",
            "nickName": "Kathy",
            "emails": [
            {
              "value": "kjensen@example.com",
              "type": "work",
              "primary": true
            }
            ],
            "addresses": [
            {
              "type": "work",
              "streetAddress": "100 Oracle City Plaza",
              "locality": "Hollywood",
              "region": "CA",
              "postalCode": "91618",
              "country": "USA",
              "formatted": "100 Oracle City Plaza\nHollywood, CA 91618 USA",
              "primary": true
            }
            ],
            "phoneNumbers": [
            {
              "value": "555-555-5545",
              "type": "work"
            }
            ],
            "userType": "Employee",
            "title": "Tour Lead",
            "preferredLanguage": "en-US",
            "locale": "en-US",
            "timezone": "America/Los_Angeles",
            "active":true,
            "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User": {
            "employeeNumber": "701984",
            "costCenter": "4130",
            "organization": "Universal Studios",
            "division": "Theme Park",
            "department": "Tour Operations",
            "manager": {
              "value": "89607",
              "$ref": "../Users/26118915-6090-4610-87e4-49d8ca9f808d",
              "displayName": "John Smith"
             }
            }
        }
    }
],
    "failOnErrors": null
}

```

## 4. 結果の確認

1. [Microsoft Entra Portal](https://entra.microsoft.com/) にアクセスします。

1. [Azure Active Directory] -> [アプリケーション] -> [エンタープライズ アプリケーション]にアクセスし、[作成しておいた](#1-api-駆動型プロビジョニングアプリの作成)、API 駆動型プロビジョニングアプリを開き、[プロビジョニング] を選択します。
1. プロビジョニングログを開いて、成功していると、作成されたユーザーの情報を確認する事が出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/0e7c9b71-10f8-1fd8-3f17-6281fe44e430.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277597/abe6f4db-f667-2027-a8d3-651faef8e310.png)


以上でプロビジョニングの基本は完了です。 API でアップロードするデータの詳細や変換処理について別記事で解説したいと思います。