Power Automateで複数人の空き時間を検索する方法

はじめに
この記事では、Power Automate で「会議の時間を検索 (V2)」(Find meeting times V2)を使い、複数メンバー全員が空いている会議候補をまとめて自動で出す方法を、画像付きで解説します。
「power automate 空き時間 検索」で調べると、コネクタの入力欄が独特で「出席者をどう渡すのか」「継続時間の単位は?」「なぜ候補の時間帯がズレるのか」とつまずきがちです。本記事では、Teams のメッセージから起動 → アダプティブカードで条件を入力 → 候補を Teams カードで返すという実用的な最小構成を作りながら、公式ドキュメントで分かりにくい仕様を1つずつ噛み砕きます。
- Teams のメッセージからワンクリックでフローを起動できる
- 出席者・開始日・会議の長さ・営業時間をカードで入力するだけ
- 全員が空いている会議候補の一覧が Teams に返ってくる
Power Automateの基本・料金・他の実例一覧はこちら。

完成フローの全体像
今回作るのは、Teams のメッセージから起動するインスタント クラウド フローです。全体は次の流れになります。
- 選択されたメッセージに対して (V2)(Microsoft Teams)― トリガー
- アダプティブ カードを投稿して応答を待機する(Teams)― 条件入力フォーム
- 会議の時間を検索 (V2)(Office 365 Outlook)― 空き時間を検索
- 選択(Select)― 候補を1行テキストに整形
- 作成(Compose)― 結果カードの JSON を組み立て
- チャットやチャネルにカードを投稿する(Teams)― 結果を返す

ステップ1:Teamsのメッセージから起動するトリガーを作る
まずはフローの入口(トリガー)を作ります。トリガーは Microsoft Teams の 「選択されたメッセージに対して (V2)」です。Power Automate でインスタント クラウド フローを新規作成し、トリガーの一覧からこれを選んで開始します。これを選ぶと、Teams のメッセージのメニューからフローを呼び出せるようになります。

トリガーを置いたら、ここから下のステップ(入力カード → 空き時間検索 → 結果カード)を順に追加していきます。
ステップ2:入力フォームをアダプティブカードで作る
続いて、検索条件を 「アダプティブ カードを投稿して応答を待機する」アクションで集めます。フロー ボットがチャットに入力カードを投稿し、利用者が「検索」を押すまで待機します。
- 投稿者:
フロー ボット - 投稿先:
フロー ボットとチャットをする(実行ユーザー個人宛)

カードの入力項目(Input)は次の5つです。各 Input の id はあとで式から参照するので、名前を控えておきましょう。
- 出席者(
attendees)― メールをカンマ区切りで入力(Input.Text) - 開始日(
startDate)― 検索したい日(Input.Date) - 会議の長さ(分)(
duration)― 30 / 60 / 90 / 120 から選択(Input.ChoiceSet・既定60) - 営業開始時刻(
bizStart)― 既定 9:00(Input.Time) - 営業終了時刻(
bizEnd)― 既定 17:00(Input.Time)

ステップ3:「会議の時間を検索 (V2)」を設定する(つまずき4点)
ここがこの記事の核心です。コネクタの入力欄に、カードの回答を式で整形して渡します。詳細パラメーターは 最大候補数=20 / 出席者の最小割合=100 / アクティビティ ドメイン=Unrestricted にしています。

(A) 出席者は「; 区切りの文字列」。カンマ入力を変換する
「会議の時間を検索 (V2)」の必須出席者 / 任意出席者 / リソース出席者は、いずれもセミコロン(;)区切りの文字列を受け取ります。配列ではありません。一方、カードでは利用者にやさしいカンマ区切りで集めているので、必須出席者欄で次の式を使い、カンマをセミコロンへ変換します。
replace(replace(trim(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['attendees']),' ',''),',',';')trim で前後の空白を、内側の replace(...,' ','') ですべての空白を除去し、最後にカンマ(,)をセミコロン(;)へ置換します。これで taro@example.com, hanako@example.com が taro@example.com;hanako@example.com になります。
(B) 継続時間は「分(数値)」。文字列を int() で数値化
会議の継続時間は PT30M のような ISO8601 ではなく、30 / 60 / 90 といった「分」の整数で渡します。カードの ChoiceSet が返すのは文字列なので、int() で数値化します。
int(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['duration'])(C) 検索範囲は「開始日+営業時間」。タイムゾーンとActivityDomainの罠
検索する時間帯は 開始日+営業開始/終了時刻から組み立て、開始日当日の営業時間内だけを探します。コネクタへ渡す開始/終了はUTC基準なので、convertTimeZone で JST(Tokyo Standard Time)から UTC へ変換してから渡します。
開始時刻:
convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['bizStart'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')終了時刻(bizStart を bizEnd に変えるだけ):
convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['bizEnd'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')2026-07-01 と 09:00 を連結して 2026-07-01T09:00:00 を作り、これを JST として UTC へ変換します(9時間引かれて 2026-07-01T00:00:00)。
(D) 候補(meetingTimeSuggestions)の主なフィールド
検索結果は meetingTimeSuggestions という候補の配列で返ります。1件ずつに次のようなフィールドが入っています。
| フィールド | 意味 |
|---|---|
| meetingTimeSlot.start.dateTime / .start.timeZone | 候補の開始日時とタイムゾーン |
| meetingTimeSlot.end.dateTime / .end.timeZone | 候補の終了日時とタイムゾーン |
| confidence | 候補の信頼度(%) |
| organizerAvailability | 開催者の空き状況 |
| suggestionReason | その候補が提案された理由 |
| emptySuggestionsReason | 候補が0件のときに入る理由コード |
ステップ4:候補を整形してTeamsカードで返す
候補の配列を、人が読める1行テキストへ整形してから結果カードにします。
候補整形(選択 / Select)
選択(Select)アクションの「元」に meetingTimeSuggestions を指定し、テキストモードのマップに次の式を入れます。UTC で返る日時を JST 表記に直しています。

concat(convertTimeZone(item()?['meetingTimeSlot']?['start']?['dateTime'],'UTC','Tokyo Standard Time','MM/dd HH:mm'),' - ',convertTimeZone(item()?['meetingTimeSlot']?['end']?['dateTime'],'UTC','Tokyo Standard Time','HH:mm'))これで 07/01 09:00 - 09:30 のような行が候補の数だけ並びます。
結果カードJSON(作成 / Compose)
作成(Compose)で、結果表示用のアダプティブカード JSON を組み立てます。候補が0件のときは emptySuggestionsReason 相当の案内文に切り替えます。

concat('{"$schema":"http://adaptivecards.io/schemas/adaptive-card.json","type":"AdaptiveCard","version":"1.4","body":[{"type":"TextBlock","text":"✅ 空き時間 検索結果","weight":"Bolder","size":"Medium","wrap":true},{"type":"TextBlock","text":"',if(empty(body('空き時間検索')?['meetingTimeSuggestions']),'指定条件では空き時間が見つかりませんでした。営業時間や日付を変えて再検索してください。',join(body('候補整形'),'\n')),'","wrap":true}]}')結果をTeamsカードで投稿する
最後に 「チャットやチャネルにカードを投稿する」で、組み立てたカード(作成の出力)を実行ユーザー宛に返します。投稿者はフロー ボット、投稿先はフロー ボットとチャットをするです。

実際に起動して使ってみる
フローが完成したら、ステップ1で設定したトリガーから起動して動作を確認します。Teams の任意のメッセージで 「…(その他の操作)」→ フロー名を選ぶと起動します。続けて入力カードがチャットに届くので、条件を入力して「検索」を押すだけです。

よくある質問
- 候補が0件になってしまいます
全員が忙しい時間帯を指定していないか、営業開始/終了時刻が逆になっていないかを確認してください。アクティビティ ドメインが
Workだと勤務時間が優先されて期待した時間帯が外れることもあります。Unrestrictedに変え、営業時間や開始日を広げて再検索してみてください。
- 候補の時間が9時間ずれます
タイムゾーン変換の方向が逆になっている可能性があります。コネクタへ渡す開始/終了は JST → UTC、結果の表示は UTC → JST です。それでもずれる場合は、開始/終了の値の末尾に
Zを付けて明示的に UTC と認識させてください。
- 任意出席者や会議室も含めて探せますか?
はい。「会議の時間を検索 (V2)」には任意出席者・リソース出席者の欄もあり、いずれもセミコロン区切りの文字列で渡せます。会議室(リソース)の空きも含めた検索は、本フローの発展形として組み込めます。
まとめ
- 出席者は「; 区切りの文字列」。カンマ入力は
replaceで変換する - 継続時間は「分」の数値。
int()で数値化する - 開始/終了は営業時間+
convertTimeZoneで組み立て、ドメインはUnrestricted - 候補配列は選択(Select)でまとめて整形し、ループ増殖を避ける
コネクタ独特の入力仕様さえ押さえれば、「複数人の予定を突き合わせて空き時間を探す」面倒な作業はワンクリックで片付きます。まずは最小構成で動かし、慣れてきたら会議室(リソース)の空き検索や、候補からそのまま予約まで進める発展形に挑戦してみてください。
あわせて読みたい
Power Automateの全体像・基本・料金・実例一覧はこちらにまとめています。

検索した候補から、会議室予約まで自動化したい場合はこちら。

検索結果をTeamsへ通知したい場合はこちら。




