マニュアル
PR

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

sanane
記事内に商品プロモーションを含む場合があります

はじめに|このフローで作るもの

この記事では、Teamsで日付と時間を入力すると、空いている会議室だけが選択肢に出てきて、選んで「予約」すると Outlook 予定表にイベントが作られる──そんな会議室予約ボットを Power Automate で1本のフローとして作ります。

会議室予約を実用レベルで自動化したい方向けに、各アクションの設定値と式まで、初心者がそのまま手を動かして再現できるように解説します。

完成すると、次のように Teams 内だけで予約が完結します。

Teamsに届いた会議室検索カード。日付・開始時刻・終了時刻を入力して検索を押す
検索結果カード。空いている会議室B・会議室Cだけが選択肢に表示され、件名と参加者を入れて予約できる

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

あわせて読みたい
Power Automateとは?できること・使い方・始め方を実例28本で解説
Power Automateとは?できること・使い方・始め方を実例28本で解説

事前準備:会議室リソースとSharePoint「会議室マスタ」

このフローにはまず下準備が必要です。 会議室の一覧を SharePoint リスト「会議室マスタ」で持つことです。

まずは、会議室名とメールの対応表を SharePoint リストで作ります。

これをリストで持っておくと、フロー側は「リストを読む → 各室の空きを調べる」という作り方ができ、会議室をフローに直接書き込まずに済みます(会議室が増減してもリストを直すだけ)。

新規からリストを選ぶ

サイトで 「新規」→「リスト」を選びます。

SharePointの「新規」メニューからリストを選ぶ
「空白のリスト」を選択

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

SharePointのリスト作成で「空白のリスト」テンプレートを選ぶ画面
リスト名を「会議室マスタ」に

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

SharePointリストの名前に「会議室マスタ」を入力するフォーム
「会議室メール」列を追加

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

SharePointリストで「テキスト」を選んで会議室メール列を追加する画面
3室分を登録

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

会議室A・B・Cと各メールを登録し終えたSharePointリスト「会議室マスタ」

表示名と内部名は別物です。リスト作成時に付けた「会議室マスタ」は表示名で、URL で使われる内部名は別になることがあります(今回の環境では内部名が List でした)。「会議室名」列の内部名も Title のままです。さらに、日本語の列名(会議室メール)は内部名が自動エンコードされ OData__x4f1a__… のような名前になります。フローで列を選ぶときは動的コンテンツから表示名で選べばOKです。

完成フローの全体像

作るのは 「会議室予約(空き検索+予約)」という手動トリガーのフローです。アクションの並びは次のとおりです。

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

式の中に出てくる アダプティブ_カードを投稿して応答を待機する などはアクションの内部名です。アクションの表示名を変えると内部名も変わり、日本語名はスペースが _ に変換されます(例:会議の時間を検索 (V2)会議の時間を検索_(V2))。式が動かないときは、動的コンテンツの一覧から選び直すのが安全です。

手順0:手動トリガーを作る

新規フロー →「インスタント クラウド フロー」→ トリガー「手動でフローをトリガーします」を選びます。日付・時刻はこの後のカードで受け取るので、トリガーの入力は追加しません

手順1:検索カードを投稿して待機する

Teams コネクタの「アダプティブ カードを投稿して応答を待機する」を追加します。これが最初に届く検索カードです。

  • 投稿者:フロー ボット
  • 投稿先:フロー ボットとチャットをする
  • Recipient(受信者):実行ユーザーのメールアドレス

誰が実行しても使えるようにするコツ:Recipient に実行ユーザーのメールを入れる方法は2通りあります。①トリガーの triggerOutputs()?['headers']?['x-ms-user-email'] を使う。②先頭に Office 365 ユーザーコネクタの「マイ プロフィールの取得 (V2)」アクションを置き、その出力の 「メール」を 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":"検索"}]}
検索カードを投稿して応答を待機するアクションの設定(投稿者・投稿先・Message・Recipient)

カードの入力値は、後の手順で body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'] のように取り出します。

手順2:会議室マスタを読み込む(複数の項目の取得)

SharePoint コネクタの「複数の項目の取得」で、会議室マスタの3室を読み込みます。

  • サイトのアドレス:リストを作成したサイト
  • リスト名:会議室マスタ
  • その他は既定でOK(必要なら詳細パラメーターでフィルター可)
複数の項目の取得(サイト=All Company、リスト=会議室マスタ)

手順3:配列変数 availableRooms を初期化する

空いている会議室を貯めていく入れ物を用意します。「変数を初期化する」を追加します。

  • 名前availableRooms
  • 種類:アレイ(配列)
  • :空のまま
変数を初期化する(名前 availableRooms、種類 アレイ)

手順4:各会議室の空きを判定する(それぞれに適用する)

それぞれに適用する」で会議室を1室ずつ回し、その室がリクエストの時間帯に空いているかを調べます。ループの入力には手順2の戻り値を指定します。

outputs('複数の項目の取得')?['body/value']
それぞれに適用する(入力=複数の項目の取得の body/value)

会議室が多いと処理に時間がかかります。このループは1室ごとに「会議の時間を検索 (V2)」を1回ずつ呼び出すため、会議室の数が増えるほど実行時間が伸びます(例:20室あれば20回の検索が走ります)。会議室が多い場合は、SharePoint「複数の項目の取得」のフィルター(フロアや拠点など)で対象を絞る、または「それぞれに適用する」の設定で同時実行(並列)の次数を上げると、待ち時間を短縮できます。

手順4-1:会議の時間を検索 (V2)

ループの中に Office 365 Outlook の「会議の時間を検索 (V2)」を入れ、その会議室を必須出席者に指定して空きを判定します。

会議の時間を検索(V2)の設定(必須出席者・継続時間・開始終了・最大候補数20・最小割合100・Unrestricted)

必須出席者:その会議室のメール。動的コンテンツから「会議室メール」を選べば内部名を気にせず入ります。式で書くなら次のとおりです(日本語列名は内部名がエンコードされます)。

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')

終了時刻(上の式の startTimeendTime に変えるだけ):

convertTimeZone(concat(body('アダプティブ_カードを投稿して応答を待機する')?['data']?['searchDate'],'T',body('アダプティブ_カードを投稿して応答を待機する')?['data']?['endTime'],':00'),'Tokyo Standard Time','UTC','yyyy-MM-ddTHH:mm:ss')

詳細パラメーター(「すべて表示」で出します):

  • 最大候補数20
  • 出席者の最小割合100
  • 開催者が出席しなくてもよいか?:はい
  • アクティビティ ドメインUnrestricted(★最重要)

アクティビティ ドメインを Work にすると、メールボックスの勤務時間が優先され、こちらが渡した開始/終了が無視されて判定がおかしくなります。必ず Unrestricted にしてください。また、コネクタは開始/終了を UTC として扱うので、カードの JST 時刻は convertTimeZone で UTC 化してから渡します。

手順4-2:条件(空きがあるか)

条件」で、候補が1件以上あれば=その枠が空いている、と判定します。左辺に次の式を入れます。

length(body('会議の時間を検索_(V2)')?['meetingTimeSuggestions'])

演算子は 「より大きい」、右辺は 0。動的コンテンツで「会議時間の提案」を選び length( … ) で包むのが簡単です。True の分岐にだけ次の「配列変数に追加」を置きます(False は空のまま)。

条件(length(候補) より大きい 0)

手順4-3:配列変数に追加(True 分岐内)

空いていた会議室を、選択肢(ChoiceSet)用のJSONとして1件ずつ availableRooms に追加します。「配列変数に追加」の値に次の式を入れます。

concat('{"title":"',items('それぞれに適用する')?['Title'],'","value":"',items('それぞれに適用する')?['Title'],'|',items('それぞれに適用する')?['OData__x4f1a__x8b70__x5ba4__x30e1__x30'],'"}')
配列変数に追加(availableRooms に concat で1件追加)

★ミソ:value会議室名|メール を入れておくと、後で1つの選択値から「表示名」と「予約先メール」の両方を取り出せます。これが会議室予約フローの定番テクです。

手順5:予約カードのJSONを組み立てる(作成)

ループを抜けたら、空き室の選択肢+件名+参加者+「予約」ボタンを持つカードを「作成」で組み立てます。choicesjoin(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":"予約"}]}')
作成アクションで予約カードのJSONを組み立てる

手順6:予約カードを投稿して待機する

アダプティブ カードを投稿して応答を待機する」をもう1つ追加します。投稿者=フロー ボット投稿先=フロー ボットとチャットをするRecipient は手順1と同じ triggerOutputs()?['headers']?['x-ms-user-email'] です。Message には手順5の出力を指定します。

outputs('作成')
予約カードを投稿して待機する(Message=作成の出力、Recipient=実行ユーザーのメール)

このカードは selectedRoom会議室名|メール)・subjectattendees を返します。これらを次の予定表登録で使います。

手順7:Outlook予定表にイベントを作成する(イベントの作成 V4)

Office 365 Outlook の「イベントの作成 (V4)」で、選んだ会議室を予約します。予定表 IDCalendar(実行ユーザーの既定の予定表)、タイム ゾーン(UTC) Coordinated Universal Time を選びます。

イベントの作成(V4)の設定(件名・開始終了・UTC・必須出席者・場所)

件名

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| より前)を入れます。splitfirst です。

first(split(body('アダプティブ_カードを投稿して応答を待機する_1')?['data']?['selectedRoom'],'|'))

会議室メールを出席者に入れることで、Room メールボックスが自動承諾して予約が確定します。これが「予約された」状態の正体です。

手順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}]}')
作成アクションで予約完了カードのJSONを組み立てる

手順9:予約完了カードを投稿する

最後に Teams の「チャットやチャネルにカードを投稿する」(応答待ちなし)で、✅予約完了カードを送ります。RecipienttriggerOutputs()?['headers']?['x-ms-user-email']アダプティブ カードには手順8の出力を指定します。

outputs('作成_1')
チャットにカードを投稿する(アダプティブカード=作成1の出力)

動作確認

フローを実行すると、フロー ボットから検索カードが届きます。日付と時間を入れて「検索」を押すと、その時間に空いている会議室だけが選択肢に並びます。会議室・件名・参加者を選んで「予約」を押せば、Outlook 予定表にイベントが作られ、✅予約完了カードが返ってきます。

検索結果カードで空いている会議室B・会議室Cが選択肢に表示されている

まとめ・つまずきどころ

  • 会議室をハードコードしない:SharePoint マスタを回すので、会議室が増えてもリストに足すだけ
  • ChoiceSet の value に 会議室名|メール:1つの選択値から表示名と予約先メールを両取り
  • JST↔UTC変換:Find Meeting Times も Create Event も内部は UTC。カードの JST 時刻を convertTimeZone で UTC 化
  • アクティビティ ドメイン=Unrestricted 必須(Work の罠)
  • 日本語列名の内部名はエンコードされるOData__x…)。動的コンテンツから選べば気にしなくてよい
  • 継続時間は「分」の整数"HH:mm"substringint で時・分に分け、枠の長さ(分)を出す

今回は空き室ゼロのときの分岐は未実装です(空きがある前提)。必要なら、条件のあとに「空きなしカードを出す」分岐を足すと親切です。また、今回は Power Automate から手動実行しています。Teams から起動したい場合は、選択メッセージトリガーや Workflows アプリを使う方法があります。

あわせて読みたい

Power Automateの全体像・基本・料金・実例一覧はこちらにまとめています。

あわせて読みたい
Power Automateとは?できること・使い方・始め方を実例28本で解説
Power Automateとは?できること・使い方・始め方を実例28本で解説

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

あわせて読みたい
Power Automateで複数人の空き時間を検索する方法
Power Automateで複数人の空き時間を検索する方法
あわせて読みたい
Power AutomateでTeamsに自動通知する方法|Forms・SharePoint・メール起点で解説
Power AutomateでTeamsに自動通知する方法|Forms・SharePoint・メール起点で解説
記事URLをコピーしました