1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Power Automateで再帰的処理は表現できるのか?多階層グループの展開にチャレンジ(前編)

1
Last updated at Posted at 2026-06-13

Microsoft Entra IDのセキュリティグループは、メンバーに別のグループを追加することで多階層のグループを作ることができます。こんな感じです。

親グループ1
 ├ アデルさん
 └ 子グループ2
      ├ メーガンさん
      └ 孫グループ3
          ├ アレックスさん
          └ ひ孫グループ4(メール付きセキュリティグループ)
              └グラディーさん

グループ1~4までのすべてのメンバーを取得する方法は、こちらの記事で紹介しました。
Graph APIという方法を使いますが、とっても簡単です。

じゃあなぜまた書いてる?

そんなに簡単にメンバー取得できるなら、どうしてまたこの記事を書いているのか?
この簡単な方法を見つける前に、かなり強引に実現をさせていたからです。

簡単な方法が用意されていなかった場合(用意されていないと思っていたわけですが)、人は何とかして実現しようとします。


グループのメンバー一覧を取得すると、ユーザーとセキュリティグループが混ざって出てきます。
ユーザーはそのままCSVに書き出せばよいわけですが、グループは再度メンバー一覧を取得する必要があります。

取得したメンバー一覧に、まだセキュリティグループが残っていたら……?

今回のテーマは Power Automate クラウドフローで再帰的処理が書けるのか? です。

image.png

どうやって実現するか?

上の図を見てもらってわかるとおり、結果をもう一度戻してふるいにかけるような処理が必要です。クラウドフローはふつう上から下へ処理が進みます。結果を得たらその一部をもういちどというのはちょっと難しいです。

子フローを使う

子フローというのは、フローの中から別のフローを呼び出す仕組みです。その時に入力となる値を与え、結果として値を受け取ることができます。

サブルーチンというか、関数というか。そういうのを実現できる仕組みがあるので、これを使えば何とかなるんじゃないかと。

キューを使う

「Queue(キュー)」は、主にプログラミングやデータ構造で使われる概念で、先に入れたものが先に取り出される仕組み(FIFO: First-In, First-Out)です。

イメージするならば、順番に並ばせて、先頭から順番に処理する方法です。
今回の場合は、ふるいにかけた結果出てきたグループIDを列の一番後ろに並ばせて、先頭から順番に処理し、列がなくなったら終了です。

Apply to eachでできるの?

Apply to eachは入力に配列を与えることで、空っぽになるまで繰り返すという処理です。
でも、今回はその入力となる配列の一番最後に新しい値を追加していきます。ループが回っている間に次々入力を変えていくというのは、たぶんできないんじゃないかな?

だから Do Untilループを使う

Do Untilは、ループの停止条件をコントロールできます。今回の場合はキューである配列が空っぽ(列がなくなる)ことが停止条件になります。

キューってクラウドフローで作れるのか?

Python を触った方がある方ならイメージできるとおもいます。getで先頭から取り出す。appendで列の最後尾に突っ込む。

クラウドフローでappendのほうは「配列変数に追加」アクションがあります。列の最後尾が保証されるかどうかはわかりませんが、今回の場合はそれほど問題ではないので、これを使います。
image.png

getのほうは、配列の先頭は[0]添え字またはfirst関数で値を使ったあとに、その値を配列の箱を見えないように配列を取得して変数に書き戻すという方法を使えば実現できそうです。これにうってつけのSkip関数というのがありました。

実際には「配列変数に追加」ではなく「配列変数に設定」を使いました。
理由はPower Automateでは極端に遅くなるループ処理を取り除くためです
union関数を応用しています。この記事の(後編)をおたのしみに。

ソリューションを作る

子フローを使うには、ソリューションという箱の中でないといけません。
環境のなかに新しいソリューションを作成します。

image.png

子フローから実装する

ソリューションができたら、その中でクラウドフローを新規作成します。「すぐに」を選べばOKです。
image.png

子フロー側であることが分かりやすい名前にしておきます。
image.png

HTTP要求V2を送信する(グループ)に以下のようにURLを指定してテスト実行してみます。

ID部分はあなたのグループIDに差し替えてください。
GET https://graph.microsoft.com/v1.0/groups/{group-id}/members

image.png

結果はこのように取得できました。グループとメンバーが混在しています。"@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']
右辺,User
#microsoft.graph.user

image.png

並列化してコピーします。右辺だけ変えてアクションの名前をわかりやすいように変更しました。

右辺,group
#microsoft.graph.group

image.png

テスト実行してそれぞれユーザーだけ、グループだけがにフィルタリングできていることを確認します。
image.png

userフィルタの結果はCSVとして書き出す

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

groupフィルタの結果は親フローに戻す

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

+Add an output をクリックすると
image.png

型が選択できるのでここではTextを選びます。
image.png

左辺には適当な名前をつけ、右辺にはgroupと名前をつけたアレイのフィルタを渡します。

body('group')

image.png

テスト実行するとこんな感じになります。
渡す値はグループなので、相当な数のグループが子グループになっていない限りはこれでも大丈夫でしょう。もし数が膨大ならばグループIDだけの配列に加工して渡しても大丈夫だと思います。
image.png

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

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

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

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

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

忘れてはいけない実行ユーザー設定

一通り子フローが出来上がったら、最後に「実行のみユーザー」設定を変更しておく必要があります。(なぜか英語表記になってますが。)

image.png

各アクションに使われている接続参照が、実行したユーザーではなく特定の決まったユーザーになるように切り替えておきます。これをしないと親フローから呼び出せません。子フローは常に決まっただれかの権限で動作するのですね。
image.png

ここで一息

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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?