こんな人向けの記事です
- Microsoft365のアカウント・ライセンス付与を自動化したいシステム担当者の方
- 特定のサービスプランを有効化・無効化させたい方
- Graph APIのPower Automateでの利用方法を知りたい方
概要
Microsoft 365にて、ユーザーへのライセンス付与はMicrosoft365管理センターや、Entra IDから手動操作で行うことができます。
また、ライセンス付与はPowerShellを使って自動化することもできるのでこちらを使っていらっしゃる管理者の方も多いかもしれません。
今回の記事では、ライセンスやサービスプランの制御をPower Automateのクラウドフローから行ってみます。Power Automateを使う利点はTeamsやFormsとの連携が容易になることです。
例えば、Formsでライセンスの申請フォームを作り、ユーザーの上司が承認したら自動でライセンスを付与し、本人と上司にライセンス付与結果をメールやチャットで送信する、ということもローコードで簡単に行うことができます。まさに市民開発です。
Forms以外にもCopilot Studioで作成したエージェントと組み合わせても良いですし、ライセンス付与用のメールアドレス宛に、ユーザーから届いたメール内容をAI Builderで解析して必要なライセンスを提案して自動付与を……。やりすぎか?
ともかく、いろいろ組み合わせることができる処理の基本部分を作る方法を紹介します。
必要な権限
ライセンスの付与を行うには、Entra IDで「アプリ登録」を行ってサービスプリンシパルを作成する必要があります。今回のフローを作成しようという方は企業のシステム担当者の方だと思いますので、しかるべき社内の手続きに従って登録してください。
勉強目的でチャレンジしたい場合には、Microsoft 365 開発者プログラムでテスト用のテナントを作ることをおすすめします。
「HTTP with Microsoft Entra ID (事前承認)」でユーザーのライセンス状況を確認する
ライセンスの付与は当然ですが権限を持ったユーザーしか行えません。(誰でも付与できたらいけませんよね?)
Power Automateのアクションを実行する権限は接続参照として設定されますが、今回はライセンス付与権限を持ったユーザーでフローを作製し、 「HTTP with Microsoft Entra ID (事前承認)」アクションを使います。
手始めに、指定したユーザーが今なんのライセンスを持っているのかをこのアクションを使って確認してみましょう。
https://graph.microsoft.com/v1.0/users/@{outputs('UPN')}/licenseDetails
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('AdeleV%400phny.onmicrosoft.com')/licenseDetails",
"value": [
{
"id": "tbIa-QWNN0KRqxk50X0FzKlUa2DYeJhCrYvfbvRIHIA",
"skuId": "606b54a9-78d8-4298-ad8b-df6ef4481c80",
"skuPartNumber": "CCIBOTS_PRIVPREV_VIRAL",
"servicePlans": [
{
"servicePlanId": "5d798708-6473-48ad-9776-3acc301c40af",
"servicePlanName": "FLOW_CCI_BOTS",
"provisioningStatus": "Success",
"appliesTo": "User"
},
{
"servicePlanId": "ce312d15-8fdf-44c0-9974-a25a177125ee",
"servicePlanName": "CCIBOTS_PRIVPREV_VIRAL",
"provisioningStatus": "Success",
"appliesTo": "User"
},
{
"servicePlanId": "cf7034ed-348f-42eb-8bbd-dddeea43ee81",
"servicePlanName": "DYN365_CDS_CCI_BOTS",
"provisioningStatus": "Success",
"appliesTo": "User"
}
]
}
]
}
とても長いので省略していますが、指定したユーザーが持っているライセンスと、その下の階層にライセンスのなかで有効となっているサービスプランが並んでいるのがわかります。
今回テストケースで取得したアデルさんのライセンスは"CCIBOTS_PRIVPREV_VIRAL"という名前のものだけで、そのskuIdは"606b54a9-78d8-4298-ad8b-df6ef4481c80"であることがわかります。
ライセンスに紐づく状態でサービスプランにも名前とIdがついていることがわかります。
サービスプリンシパル作成と権限付与
事前承認の要求ではテナントのライセンス在庫確認と付与はできないようなので、Entra IDからアプリ登録の操作を行い、権限をつkたサービスプリンシパルを作成します。
ライセンス付与に必要な権限は以下の2つです。
- User.ReadWrite.All
- Directory.ReadWrite.All
Directory.Read.All
→ テナント全体のディレクトリ情報を読み取る権限(ライセンス一覧やユーザー情報の取得のみ)。
User.ReadWrite.All
→ ユーザーオブジェクトのプロパティを更新する権限(ライセンス付与、パスワードリセット、属性変更など)。
Directory.ReadWrite.All
→ テナント全体のディレクトリ情報を更新する権限(より広範囲な操作が可能)。
同様にUser.ReadWrite.Allにもアプリケーションの権限を与えた後、
「管理者の同意を与えます」をクリックしておきます。

シークレットの作成
「証明書とシークレット」の項目を開き、「+新しいクライアントシークレット」をクリックして適当な名前と期限を設定してシークレットの「値」をコピーしてメモにの押しておきます。必要なのはシークレットIDではない点に注意が必要です。必要なのは「値」です。

「概要」から「ディレクトリ(テナント)ID」と「アプリケーション(クライアント)ID」の値も同じくコピーしてメモに残します。この3点セットでサービスプリンシパルを使うことができます。

HTTP要求
Graph APIをつかってライセンスを付与するには、assignLicenseを使います。
こちらに書かれているエンドポイントに対して付与するユーザーをURIに。付与するライセンスをBodyに以下のように書いて与えます。
https://graph.microsoft.com/v1.0/users/@{outputs('UPN')}/assignLicense
{
"addLicenses": [https://graph.microsoft.com/v1.0/users/<UPN>/licenseDetails
{
"skuId": "@{outputs('skuId')}",
"disabledPlans": []
}
],
"removeLicenses": []
}
上記の可変部分は「作成」を使って変数のように与えています。全体感はこんな感じです。シークレットなどの情報には鍵をかけてます。

こうしておくと、実行結果から隠されるので、クラウドフローの編集権を持っている人にしか見えないので少し安心です。

あらかじめ、アデルさんから全てのライセンスを外しておいた状態で、フローを実行してみます。
指定したskuIdのライセンスが付与されていることが確認できます。また、ライセンスに含まれる3つのサービスプランは全て有効担っています。

ちなみに、今回与えたMicrosoft Copilot Studio バイラル試験版は、Copilot Studioの試用版を誰かが試すと付与されるものです。
一部のサービスプランのみ有効にするには?
さてここで、ユーザーに対してライセンスを有効化すると、すべてのサービスプランが有効の状態で付与されることがわかりました。
実は、さきほど POST users//assignLicense を送信した際の本文に「"disabledPlans": []」という項目がありました。こちらに不要なサービスプランのIDを配列形式で与えてやることができます。こんな感じ。
{
"addLicenses": [https://graph.microsoft.com/v1.0/users/<UPN>/licenseDetails
{
"skuId": "@{outputs('skuId')}",
"disabledPlans": [
'5d798708-6473-48ad-9776-3acc301c40af',
'cf7034ed-348f-42eb-8bbd-dddeea43ee81'
]
}
],
"removeLicenses": []
}
そこで、配列を作って、それをHTTP要求の本文のなかのdisabledPlansのコロンの後ろに配置してやりました。

サービスプランの、IDを指定していなかったサービスプランだけが付与された状態になりました。

取り除くプランではなく付与したいプランを指定したい場合は?
実際、サービスプランは増えたり減ったりします。そのなかで、指定したサービスプランだけをピンポイントに指定したい場合にはどうすればよいでしょう?
集合の図を書くとこんな感じに表現できそうです。
ライセンスが持つ全てのサービスプラン - 有効にしたいサービスプラン = 取り除くサービスプラン
という関係になります。
付与したいサービスプランの配列を指定する。
今回は下のサービスプランだけを有効化したいとしましょう。
{
"servicePlanId": "cf7034ed-348f-42eb-8bbd-dddeea43ee81",
"servicePlanName": "DYN365_CDS_CCI_BOTS",
"provisioningStatus": "Success",
"appliesTo": "User"
}
全てのサービスプランを配列にする
各ライセンスが持つ現在のサービスプランの一覧を取得するにはこちらのエンドポイントを使います。
GET https://graph.microsoft.com/v1.0/subscribedSkus
認証設定を追加済みのHTTP要求アクションに以下のように設定します。

結果は長大なのでリファレンスページの結果サンプルを参考にしましょう。

valueというキーの配列1つずつが1つのライセンスです。skuIdが見えます。また、servicePlansの値は配列になっているので、この配列部分を取得すればライセンスごとの全手のサービスプランが取得できそうです。
テナントのライセンス一覧には、その他多くのライセンスが配列状態でひしめき合っているので、この中から今回の対象となるskuIdだけに絞る必要があります。「アレイのフィルター」アクションを使います。
body('テナントのライセンス一覧')?['value']
item()?['skuId']
outputs('skuId')
これで、両辺が一致した、つまり指定のskuIdの情報だけが配列として取得できます。
[
{
"accountName": "0phny",
"accountId": "f91ab2b5-8d05-4237-91ab-1939d17d05cc",
"appliesTo": "User",
"capabilityStatus": "Enabled",
"consumedUnits": 2,
"id": "f91ab2b5-8d05-4237-91ab-1939d17d05cc_606b54a9-78d8-4298-ad8b-df6ef4481c80",
"skuId": "606b54a9-78d8-4298-ad8b-df6ef4481c80",
"skuPartNumber": "CCIBOTS_PRIVPREV_VIRAL",
"subscriptionIds": [
"0481b988-1410-4854-bd98-f26807a49975"
],
"prepaidUnits": {
"enabled": 10000,
"suspended": 0,
"warning": 0,
"lockedOut": 0
},
"servicePlans": [
{
"servicePlanId": "5d798708-6473-48ad-9776-3acc301c40af",
"servicePlanName": "FLOW_CCI_BOTS",
"servicePlanType": "ProcessSimple",
"provisioningStatus": "Success",
"appliesTo": "User"
},
{
"servicePlanId": "ce312d15-8fdf-44c0-9974-a25a177125ee",
"servicePlanName": "CCIBOTS_PRIVPREV_VIRAL",
"servicePlanType": "ccibotsprod",
"provisioningStatus": "Success",
"appliesTo": "User"
},
{
"servicePlanId": "cf7034ed-348f-42eb-8bbd-dddeea43ee81",
"servicePlanName": "DYN365_CDS_CCI_BOTS",
"servicePlanType": "CRM",
"provisioningStatus": "Success",
"appliesTo": "User"
}
]
}
]
こんどは、この中から"servicePlans"の中身だけ、しかも"servicePlanId"の値だけを取り出してIDのみの配列にします。これには「選択」アクションを使います。
選択に渡す前に先程のアレイのフィルターは名前をわかりやすいものに変えておきました。
「選択」の「開始」に与える際のポイントは、skuIdのフィルタで得られた結果は要素1つの配列であることです。
取得したいのは、その中の"servicePlanId"であるので、「開始」には1つ目の配列のなかの"servicePlans"を指定します。

first(body('skuIdでフィルタ'))?['servicePlans']
item()?['servicePlanId']
結果はこのとおり。サービスプランのIDだけが配列になりました。「選択」は実行前にわかりやすい名前に変えておきました。

配列の引き算を実行するにはアレイのフィルター処理をつかう
サービスプランごとの全IDから指定したいIDを引くには配列の引き算をします。これには「アレイのフィルター処理」アクションを使います。
引かれる側を「差出人」にして、その1要素ごとに引く側の配列に存在するかどうかを確認していくイメージです。配列に対する存在のチェックにはcontains関数を使います。
body('テナントの全サービスプラン配列'
contains(outputs('有効化したいサービスプランID配列'),item())
「次の値に等しい」を選択して
false
HTTP要求によるライセンスの追加の本文に取り除く対象として指定するだけ。初期には手で与えていた部分を差し替えましょう。
実行してみると、ちゃんと配列の引き算ができました。ここで取れたのは取り除くサービスプランIDの配列です。

ライセンスの追加には上記のID配列が取り除かれる対象ですので、結果的に有効化したいサービスプランIDの対象だけが有効化されました。(というよりは、消されたあとに有効として残った状態になりました。)

現在ユーザーが持つライセンスに指定サービスプランだけを加えるには?
ここまでで、付与したいサービスプランを指定できるようになったので、おおよそ問題ないのですが。
テナントによっては、ユーザーによってすでに付与されているサービスプランがまちまちだったりします。
その上で、指定したサービスプランを付与したいというニーズもあるでしょう。これを実現するにはどうすれば良いでしょうか?
こんがらがってきたので、図にして考えてみました。
ここまで見てきたとおり、ライセンスを追加する際には取り除くサービスプランID配列を指定します。
ユーザーがすでに持っているサービスプランを維持したいならば、その逆(対偶)のID配列を用意すれば良いことはここまでで確認できました。
持っているか持っていないか判定するのも良いですが、上記のような対偶を求める前に、ユーザーが持っているサービスプランID配列に与えたいIDを加えてしまってはどうでしょうか?
現在ユーザーが持つサービスプラン配列を取得
ユーザーがすでに持っている指定したライセンスのなかの"servicePlanId"を取りに行きます。これも手順は先程のテナント全体の場合と全く同じです。skuIdで絞ります。そのなかで、provisioningStatusがSuccessのものだけをに絞ります。選択を使ってservicePlanIdだけの配列にします。
最後にunion関数を使って、与えたいサービスプランとすでにユーザーが持っているサービスプランのID配列を重複なく統合します。
■skuIdでユーザのライセンスをフィルタ「アレイのフィルター処理」
body('ユーザーのライセンスを取得')?['value']
item()?['skuId'
outputs('skuId')
■ユーザの有効なライセンスをフィルタ「アレイのフィルター処理」
first(body('skuIdでユーザのライセンスをフィルタ'))?['servicePlans']
item()?['provisioningStatus']
Success
■ユーザのサービスプランID配列「アレイのフィルター処理」
body('ユーザの有効なライセンスをフィルタ')
item()?['servicePlanId']
■ユーザのサービスプランIDに有効化するIDを加える「作成」
union(body('ユーザのサービスプランID配列'),outputs('有効化したいサービスプランID配列'))
配列に要素を加えるには、union関数が使えます。以前にブログに書いたことがあるので参考にしてください。
テストする
現在、アデルさんは以下のような状態なので、これに加えて、 「CCI BotsのFlow」というサービスプランを有効にしたいとしましょう。
「CCI BotsのFlow」のIDを有効化したいサービスプランID配列に加えておきます。
最終的にHTTP要求で与えるのは、「取り除くサービスプラン配列」ですから、「ユーザのサービスプランID配列に有効化するIDを加える」の対偶を取得するために差出人の部分を入れ替えるのを忘れずに。
実行してみると、すでに有効化だったプランに、有効化させたいプランが加わりました!

まとめ
ずいぶん長くなりましたが、ライセンスとサービスプランの付与制御について丁寧に検証してみました。
まとめるとこういう感じになります。
- ライセンスを付与するには、サービスプリンシパルが必要
- ライセンスを有効にするとサービスプランは全て有効に。無効化したいプランIDを指定する
- 有効化したいIDを指定するには、テナント全体から対偶を取る
- ユーザーがすでに持っているサービスプランに加えたければ、union関数で加えてから対偶を取れば良い
検証できていない部分
今回はサービスプリンシパルの権限を「アプリ」種別で取得して使いました。
この場合、クラウドフローのなかのアプリIDとシークレットが流出すると、誰でもライセンスの有効化・無効化ができてしまいます。
セキュリティ的によろしくないので、本来は「委任」種別で権限を付与して、ロールを持つユーザーが実行した場合にだけ動作するようにしたいところです。
ここのところをどうやって実現したら良いのか、試行錯誤したのですがうまく行かず。ぜひアドバイスがほしいです。
委任のサービスプリンシパルをクラウドフローから利用するには、やっぱりカスタムコネクターしか方法はないのだろうか。
— △ランゲルハンス島のDD△ (@DaddyDaddy) December 6, 2025
こんな人が書きました。
職場でPower Platformの管理者さんをしています。ブログではPower AutomateのTipsのようなものを書いています。QiitaではPower BIとクラウドフローのちょこ技を記事にしています。Twitterをフォローしていただいたり、「いいね」などしていただけるととっても喜びます。
大阪開催Power Platform系のイベントやオンラインの集まりには時々参加していますので、こちらのアイコンを見かけたら声をかけていただけると嬉しいです。


















