Power Automateで会議室予約を自動化|空き会議室を検索してTeamsから予約する方法

- はじめに|このフローで作るもの
- 事前準備:会議室リソースとSharePoint「会議室マスタ」
- 完成フローの全体像
- 手順0:手動トリガーを作る
- 手順1:検索カードを投稿して待機する
- 手順2:会議室マスタを読み込む(複数の項目の取得)
- 手順3:配列変数 availableRooms を初期化する
- 手順4:各会議室の空きを判定する(それぞれに適用する)
- 手順5:予約カードのJSONを組み立てる(作成)
- 手順6:予約カードを投稿して待機する
- 手順7:Outlook予定表にイベントを作成する(イベントの作成 V4)
- 手順8:予約完了カードのJSONを組み立てる(作成1)
- 手順9:予約完了カードを投稿する
- 動作確認
- まとめ・つまずきどころ
- あわせて読みたい
はじめに|このフローで作るもの
この記事では、Teamsで日付と時間を入力すると、空いている会議室だけが選択肢に出てきて、選んで「予約」すると Outlook 予定表にイベントが作られる──そんな会議室予約ボットを Power Automate で1本のフローとして作ります。
会議室予約を実用レベルで自動化したい方向けに、各アクションの設定値と式まで、初心者がそのまま手を動かして再現できるように解説します。
完成すると、次のように Teams 内だけで予約が完結します。


Power Automateの基本・料金・他の実例一覧はこちら。

事前準備:会議室リソースとSharePoint「会議室マスタ」
このフローにはまず下準備が必要です。 会議室の一覧を SharePoint リスト「会議室マスタ」で持つことです。
まずは、会議室名とメールの対応表を SharePoint リストで作ります。
これをリストで持っておくと、フロー側は「リストを読む → 各室の空きを調べる」という作り方ができ、会議室をフローに直接書き込まずに済みます(会議室が増減してもリストを直すだけ)。
サイトで 「新規」→「リスト」を選びます。

テンプレートから 「リスト(空白のリスト)」を選びます。

リスト名に 「会議室マスタ」と入力して作成します。

既定の「タイトル」列を「会議室名」にリネームして使います(内部名は Title のまま)。続けて 「列の追加」→「テキスト(1行)」で 「会議室メール」列を追加します。

会議室3室の会議室名とメールをアイテムとして登録します。これで会議室マスタの完成です。

完成フローの全体像
作るのは 「会議室予約(空き検索+予約)」という手動トリガーのフローです。アクションの並びは次のとおりです。
- フローを手動でトリガーする(入力なし)
- アダプティブ カードを投稿して応答を待機する(検索カード)
- 複数の項目の取得(SharePoint「会議室マスタ」)
- 変数を初期化する(配列
availableRooms) - それぞれに適用する(各会議室をループ)→ 会議の時間を検索(V2) → 条件 → 空きなら配列に追加
- 作成(予約カードのJSONを組み立て)
- アダプティブ カードを投稿して応答を待機する(予約カード)
- イベントの作成 (V4)(Outlook 予定表に登録)
- 作成(予約完了カードのJSONを組み立て)
- チャットやチャネルにカードを投稿する(✅予約完了カード)

手順0:手動トリガーを作る
新規フロー →「インスタント クラウド フロー」→ トリガー「手動でフローをトリガーします」を選びます。日付・時刻はこの後のカードで受け取るので、トリガーの入力は追加しません。
手順1:検索カードを投稿して待機する
Teams コネクタの「アダプティブ カードを投稿して応答を待機する」を追加します。これが最初に届く検索カードです。
- 投稿者:フロー ボット
- 投稿先:フロー ボットとチャットをする
- Recipient(受信者):実行ユーザーのメールアドレス
Message には、日付(searchDate)・開始(startTime)・終了(endTime)を入力するカードのJSONを貼り付けます。
{"$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":"日付","wrap":true},{"type":"Input.Date","id":"searchDate"},{"type":"TextBlock","text":"開始時刻","wrap":true},{"type":"Input.Time","id":"startTime","value":"09:00"},{"type":"TextBlock","text":"終了時刻","wrap":true},{"type":"Input.Time","id":"endTime","value":"18:00"}],"actions":[{"type":"Action.Submit","title":"検索"}]}
カードの入力値は、後の手順で body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'] のように取り出します。
手順2:会議室マスタを読み込む(複数の項目の取得)
SharePoint コネクタの「複数の項目の取得」で、会議室マスタの3室を読み込みます。
- サイトのアドレス:リストを作成したサイト
- リスト名:会議室マスタ
- その他は既定でOK(必要なら詳細パラメーターでフィルター可)

手順3:配列変数 availableRooms を初期化する
空いている会議室を貯めていく入れ物を用意します。「変数を初期化する」を追加します。
- 名前:
availableRooms - 種類:アレイ(配列)
- 値:空のまま

手順4:各会議室の空きを判定する(それぞれに適用する)
「それぞれに適用する」で会議室を1室ずつ回し、その室がリクエストの時間帯に空いているかを調べます。ループの入力には手順2の戻り値を指定します。
outputs('複数の項目の取得')?['body/value']
手順4-1:会議の時間を検索 (V2)
ループの中に Office 365 Outlook の「会議の時間を検索 (V2)」を入れ、その会議室を必須出席者に指定して空きを判定します。

必須出席者:その会議室のメール。動的コンテンツから「会議室メール」を選べば内部名を気にせず入ります。式で書くなら次のとおりです(日本語列名は内部名がエンコードされます)。
items('それぞれに適用する')?['OData__x4f1a__x8b70__x5ba4__x30e1__x30']会議の継続時間(分):検索する時間枠の長さ(分)です。カードの "HH:mm" を「時×60+分」に直し、終了−開始で枠の長さを求めます。
sub(add(mul(int(substring(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],0,2)),60),int(substring(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],3,2))),add(mul(int(substring(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startTime'],0,2)),60),int(substring(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startTime'],3,2))))substring(endTime,0,2)=時、substring(endTime,3,2)=分。時×60+分で「0時からの通算分」にして、終了−開始=枠の長さ(分)を出します。例:18:00→1080分、09:00→540分、差=540分。その室を「開始〜終了の全時間」予約できるか=枠まるごと空いているかを判定するため、継続時間=枠の長さにします。継続時間は PT540M のような ISO8601 ではなく「分」の整数である点に注意。
開始時刻(カードの日付+開始を JST→UTC へ変換):
convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startTime'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')終了時刻(上の式の startTime を endTime に変えるだけ):
convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')詳細パラメーター(「すべて表示」で出します):
- 最大候補数:
20 - 出席者の最小割合:
100 - 開催者が出席しなくてもよいか?:はい
- アクティビティ ドメイン:
Unrestricted(★最重要)
手順4-2:条件(空きがあるか)
「条件」で、候補が1件以上あれば=その枠が空いている、と判定します。左辺に次の式を入れます。
length(body('会議の時間を検索_(V2)')?['meetingTimeSuggestions'])演算子は 「より大きい」、右辺は 0。動的コンテンツで「会議時間の提案」を選び length( … ) で包むのが簡単です。True の分岐にだけ次の「配列変数に追加」を置きます(False は空のまま)。

手順4-3:配列変数に追加(True 分岐内)
空いていた会議室を、選択肢(ChoiceSet)用のJSONとして1件ずつ availableRooms に追加します。「配列変数に追加」の値に次の式を入れます。
concat('{"title":"',items('それぞれに適用する')?['Title'],'","value":"',items('それぞれに適用する')?['Title'],'|',items('それぞれに適用する')?['OData__x4f1a__x8b70__x5ba4__x30e1__x30'],'"}')
手順5:予約カードのJSONを組み立てる(作成)
ループを抜けたら、空き室の選択肢+件名+参加者+「予約」ボタンを持つカードを「作成」で組み立てます。choices に join(variables('availableRooms'),',') を差し込むのがポイントです。
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":"',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],' ',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startTime'],' 〜 ',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],'","wrap":true},{"type":"TextBlock","text":"会議室を選択してください:","wrap":true},{"type":"Input.ChoiceSet","id":"selectedRoom","style":"compact","placeholder":"会議室を選択...","choices":[',join(variables('availableRooms'),','),']},{"type":"TextBlock","text":"件名","wrap":true},{"type":"Input.Text","id":"subject","placeholder":"会議の件名を入力してください"},{"type":"TextBlock","text":"参加者(メールアドレス、セミコロン区切り)","wrap":true},{"type":"Input.Text","id":"attendees","placeholder":"user@example.com;user2@example.com"}],"actions":[{"type":"Action.Submit","title":"予約"}]}')
手順6:予約カードを投稿して待機する
「アダプティブ カードを投稿して応答を待機する」をもう1つ追加します。投稿者=フロー ボット、投稿先=フロー ボットとチャットをする、Recipient は手順1と同じ triggerOutputs()?['headers']?['x-ms-user-email'] です。Message には手順5の出力を指定します。
outputs('作成')
このカードは selectedRoom(会議室名|メール)・subject・attendees を返します。これらを次の予定表登録で使います。
手順7:Outlook予定表にイベントを作成する(イベントの作成 V4)
Office 365 Outlook の「イベントの作成 (V4)」で、選んだ会議室を予約します。予定表 ID は Calendar(実行ユーザーの既定の予定表)、タイム ゾーン は (UTC) Coordinated Universal Time を選びます。

件名:
body('アダプティブ_カードを投稿して応答を待機する_1')?['data']?['subject']開始時刻(カードの日付+開始を UTC へ):
convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startTime'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')終了時刻(同じく endTime で):
convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')詳細パラメーター(「すべて表示」で出します)。必須出席者には、会議室メールと参加者を ; で連結します。selectedRoom は 会議室名|メール なので、split(...,'|') の last がメールです。
concat(last(split(body('アダプティブ_カードを投稿して応答を待機する_1')?['data']?['selectedRoom'],'|')),';',body('アダプティブ_カードを投稿して応答を待機する_1')?['data']?['attendees'])場所には会議室名(selectedRoom の | より前)を入れます。split の first です。
first(split(body('アダプティブ_カードを投稿して応答を待機する_1')?['data']?['selectedRoom'],'|'))手順8:予約完了カードのJSONを組み立てる(作成1)
「作成」をもう1つ追加し、✅予約完了カードのJSONを組み立てます。
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":"',first(split(body('アダプティブ_カードを投稿して応答を待機する_1')?['data']?['selectedRoom'],'|')),' を予約しました(',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],' ',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['startTime'],' 〜 ',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],')","wrap":true}]}')
手順9:予約完了カードを投稿する
最後に Teams の「チャットやチャネルにカードを投稿する」(応答待ちなし)で、✅予約完了カードを送ります。Recipient は triggerOutputs()?['headers']?['x-ms-user-email']、アダプティブ カードには手順8の出力を指定します。
outputs('作成_1')
動作確認
フローを実行すると、フロー ボットから検索カードが届きます。日付と時間を入れて「検索」を押すと、その時間に空いている会議室だけが選択肢に並びます。会議室・件名・参加者を選んで「予約」を押せば、Outlook 予定表にイベントが作られ、✅予約完了カードが返ってきます。

まとめ・つまずきどころ
- 会議室をハードコードしない:SharePoint マスタを回すので、会議室が増えてもリストに足すだけ
- ChoiceSet の value に
会議室名|メール:1つの選択値から表示名と予約先メールを両取り - JST↔UTC変換:Find Meeting Times も Create Event も内部は UTC。カードの JST 時刻を
convertTimeZoneで UTC 化 - アクティビティ ドメイン=
Unrestricted必須(Work の罠) - 日本語列名の内部名はエンコードされる(
OData__x…)。動的コンテンツから選べば気にしなくてよい - 継続時間は「分」の整数:
"HH:mm"をsubstring+intで時・分に分け、枠の長さ(分)を出す
あわせて読みたい
Power Automateの全体像・基本・料金・実例一覧はこちらにまとめています。

この記事の土台になっている「会議の時間を検索 (V2)」コネクタの基礎と、Teams メッセージから起動するパターンは、次の記事で詳しく解説しています。あわせてどうぞ。



