Microsoft Entra IDのセキュリティグループは、メンバーに別のグループを追加することで多階層のグループを作ることができます。こんな感じです。
親グループ1
├ アデルさん
└ 子グループ2
├ メーガンさん
└ 孫グループ3
├ アレックスさん
└ ひ孫グループ4(メール付きセキュリティグループ)
└グラディーさん
グループ1~4までのすべてのメンバーを取得する方法は、こちらの記事で紹介しました。
Graph APIという方法を使いますが、とっても簡単です。
じゃあなぜまた書いてる?
そんなに簡単にメンバー取得できるなら、どうしてまたこの記事を書いているのか?
この簡単な方法を見つける前に、かなり強引に実現をさせていたからです。
簡単な方法が用意されていなかった場合(用意されていないと思っていたわけですが)、人は何とかして実現しようとします。
グループのメンバー一覧を取得すると、ユーザーとセキュリティグループが混ざって出てきます。
ユーザーはそのままCSVに書き出せばよいわけですが、グループは再度メンバー一覧を取得する必要があります。
取得したメンバー一覧に、まだセキュリティグループが残っていたら……?
今回のテーマは Power Automate クラウドフローで再帰的処理が書けるのか? です。
どうやって実現するか?
上の図を見てもらってわかるとおり、結果をもう一度戻してふるいにかけるような処理が必要です。クラウドフローはふつう上から下へ処理が進みます。結果を得たらその一部をもういちどというのはちょっと難しいです。
子フローを使う
子フローというのは、フローの中から別のフローを呼び出す仕組みです。その時に入力となる値を与え、結果として値を受け取ることができます。
サブルーチンというか、関数というか。そういうのを実現できる仕組みがあるので、これを使えば何とかなるんじゃないかと。
キューを使う
「Queue(キュー)」は、主にプログラミングやデータ構造で使われる概念で、先に入れたものが先に取り出される仕組み(FIFO: First-In, First-Out)です。
イメージするならば、順番に並ばせて、先頭から順番に処理する方法です。
今回の場合は、ふるいにかけた結果出てきたグループIDを列の一番後ろに並ばせて、先頭から順番に処理し、列がなくなったら終了です。
Apply to eachでできるの?
Apply to eachは入力に配列を与えることで、空っぽになるまで繰り返すという処理です。
でも、今回はその入力となる配列の一番最後に新しい値を追加していきます。ループが回っている間に次々入力を変えていくというのは、たぶんできないんじゃないかな?
だから Do Untilループを使う
Do Untilは、ループの停止条件をコントロールできます。今回の場合はキューである配列が空っぽ(列がなくなる)ことが停止条件になります。
キューってクラウドフローで作れるのか?
Python を触った方がある方ならイメージできるとおもいます。getで先頭から取り出す。appendで列の最後尾に突っ込む。
クラウドフローでappendのほうは「配列変数に追加」アクションがあります。列の最後尾が保証されるかどうかはわかりませんが、今回の場合はそれほど問題ではないので、これを使います。

getのほうは、配列の先頭は[0]添え字またはfirst関数で値を使ったあとに、その値を配列の箱を見えないように配列を取得して変数に書き戻すという方法を使えば実現できそうです。これにうってつけのSkip関数というのがありました。
実際には「配列変数に追加」ではなく「配列変数に設定」を使いました。
理由はPower Automateでは極端に遅くなるループ処理を取り除くためです
union関数を応用しています。この記事の(後編)をおたのしみに。
ソリューションを作る
子フローを使うには、ソリューションという箱の中でないといけません。
環境のなかに新しいソリューションを作成します。
子フローから実装する
ソリューションができたら、その中でクラウドフローを新規作成します。「すぐに」を選べばOKです。

HTTP要求V2を送信する(グループ)に以下のようにURLを指定してテスト実行してみます。
GET https://graph.microsoft.com/v1.0/groups/{group-id}/members
結果はこのように取得できました。グループとメンバーが混在しています。"@odata.type"の値によってユーザーなのかグループなのかを見分けることができます。
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"value": [
{
"@odata.type": "#microsoft.graph.user",
"id": "f23dabca-2439-4b8f-a17a-a10cd4a22592",
"businessPhones": [
"+1 425 555 0109"
],
"displayName": "Adele Vance",
"givenName": "Adele",
"jobTitle": "Retail Manager",
"mail": "AdeleV@0phny.onmicrosoft.com",
"mobilePhone": null,
"officeLocation": "18/2111",
"preferredLanguage": "en-US",
"surname": "Vance",
"userPrincipalName": "AdeleV@0phny.onmicrosoft.com"
},
{
"@odata.type": "#microsoft.graph.group",
"id": "277b389f-0a0c-43d1-ba85-d6dbfbcc2cdc",
"deletedDateTime": null,
"classification": null,
"createdDateTime": "2026-06-13T02:08:27Z",
"creationOptions": [],
"description": "g2",
"displayName": "g2",
"expirationDateTime": null,
"groupTypes": [],
"infoCatalogs": [],
"isAssignableToRole": false,
"mail": null,
"mailEnabled": false,
"mailNickname": "00000000-0000-0000-0000-000000000000",
"membershipRule": null,
"membershipRuleProcessingState": null,
"onPremisesDomainName": null,
"onPremisesLastSyncDateTime": null,
"onPremisesNetBiosName": null,
"onPremisesSamAccountName": null,
"onPremisesSecurityIdentifier": null,
"onPremisesSyncEnabled": null,
"preferredDataLocation": null,
"preferredLanguage": null,
"proxyAddresses": [],
"renewedDateTime": "2026-06-13T02:08:27Z",
"resourceBehaviorOptions": [],
"resourceProvisioningOptions": [],
"securityEnabled": true,
"securityIdentifier": "S-1-12-1-662386847-1137773068-3688269242-3693923579",
"theme": null,
"uniqueName": null,
"visibility": null,
"onPremisesProvisioningErrors": [],
"serviceProvisioningErrors": []
}
]
}
取得できたのはJSONなので、よく見るとvalueの値は配列になっています。
この部分だけを「アレイのフィルター処理」に渡してユーザーとグループに分けます。まずはユーザー
body('HTTP_要求_V2_を送信する')?['value']
item()?['@odata.type']
#microsoft.graph.user
並列化してコピーします。右辺だけ変えてアクションの名前をわかりやすいように変更しました。
#microsoft.graph.group
テスト実行してそれぞれユーザーだけ、グループだけがにフィルタリングできていることを確認します。

userフィルタの結果はCSVとして書き出す
ユーザー側の結果は、CSV化してファイルに書き出しました。子フローは書き出し処理を担います。

groupフィルタの結果は親フローに戻す
User側は処理できたので、今度は処理できずに残ったgroup側のIDを親フローに返してやります。
それには「Respond to a Power App or flow」とうアクションを使います。

左辺には適当な名前をつけ、右辺にはgroupと名前をつけたアレイのフィルタを渡します。
body('group')
テスト実行するとこんな感じになります。
渡す値はグループなので、相当な数のグループが子グループになっていない限りはこれでも大丈夫でしょう。もし数が膨大ならばグループIDだけの配列に加工して渡しても大丈夫だと思います。

出力部分はできたので、今度は入力部分です。
トリガーにも同じようにText型の入力を追加してやります。

入力につけた名前を動的コンテンツとしてHTTP要求のグループID部分と差し替えます。

グループの名前を取得するために配置していたHTTP要求の部分も差し替えておきます。

これでテスト実行すると、先ほどとは違って実行時にグループIDが尋ねられます。メンバーにグループを持つグループIDを張り付けて実行してやりましょう。

User側はCSVファイルとして書き出され、group側は戻り値として表示されていればOKです。

忘れてはいけない実行ユーザー設定
一通り子フローが出来上がったら、最後に「実行のみユーザー」設定を変更しておく必要があります。(なぜか英語表記になってますが。)
各アクションに使われている接続参照が、実行したユーザーではなく特定の決まったユーザーになるように切り替えておきます。これをしないと親フローから呼び出せません。子フローは常に決まっただれかの権限で動作するのですね。

ここで一息
子フローの作成までできました。
次はこれを動かす親フローですが、今回書きたかったのは親フローのほうなんです。
続きは、またのちほど。次の記事(後編)にて。









