Power Automate を使って会議室の次の会議を取得し通知する

:::note
この記事は、[Microsoft Power Automate Advent Calendar 2024](https://qiita.com/advent-calendar/2024/powerautomate) の12月20日担当分の記事です。
:::

こんにちは。株式会社エーティーエルシステムズ 中村です。

以前、[Power Automate を使って Outlook に登録した会議が終わるちょっと前に通知する](https://qiita.com/atl-nakamura/items/07fbcd8e8c9b6c137b55) という記事を投稿しました。  
この記事では、Outlook の予定をもとに、ちょっと前に Outlook のメール仕分けルールを用いてデスクトップ通知を行うフローを作成する手順について解説しました。

今回は、さらにこのフローを改良し、予定に紐づいている会議室が次の時間帯も空いているかチェックし、延長可能かを合わせて通知する機能を追加してみます。

## 自己紹介

このアカウントでは初めてアドベントカレンダーに参加するので、すこし自己紹介を…。

中村と申します。2023年4月に入社してから、Azure Synapse Analytics を用いたデータ加工プログラムの開発に携わっております。

さまざまな自動化が好きで、Microsoft Power Automate はもちろん、ちょっとしたプログラムを書いて面倒な作業の自動化をしたり、定期的に情報収集や分析を行うプログラムをプライベートで開発したりなどしております。

もし弊社のことが気になった方は、私だけでなくさまざまな先輩社員とお話することが可能です。以下の弊社マイナビページよりお申し込みください。

https://job.mynavi.jp/26/pc/search/corp212664/obog.html

※上記リンクはマイナビ2026のページですので、必要に応じ最新のマイナビページをご参照ください。

## 改修の内容

前回の記事で、まもなく終了する予定があるときにメールを送信し、デスクトップ通知を出せるようになりました。  
今回はメールを送信する前に、予定に紐づいている会議室の情報を取得し、後ろの予定を取得して延長可能かどうかをメールに記載します。

したがって、以下の流れで通知するようになります。太字が今回改修する箇所になります。

1. 5分ごとにトリガー
2. Outlook から5分以内に終了する予定を取得
3. **会議室のメールアドレス群を取得**
4. **会議室のメールアドレスから、会議室ユーザーをそれぞれ取得**
5. **それぞれの会議室ユーザーのカレンダーを取得し、終了後1時間以内の予定を取得**
6. 自分自身にメールを送信
7. Outlook の仕分けルールで、受信したときにデスクトップ通知を表示

この改修により、以下のような通知を受け取れるようになります。延長できるかどうかが一目で分かりますね。

![改修後のメール通知画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/ff783459-4227-d523-c969-b16693b2871d.png) ![改修後のメール通知画面2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/18a8cdb8-216b-62d4-a4c4-9b9ff6c54538.png)

## 改修する

[前回の記事](https://qiita.com/atl-nakamura/items/07fbcd8e8c9b6c137b55) で、以下のようにフローを作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/7832f4a2-d978-6b35-3eab-f1af711ca7c0.png)

今回の改修では、「イベントの取得 (V4)」の後から変更を加えていきます。

### 1. 変数を定義する

今回のフローでは、つぎの変数を用います。

- `resourceAttendees` [Array]: イベントに紐づくリソースのメールアドレスを格納するための配列
- `resourceTexts` [Array]: イベントに紐づくリソースのテキストを格納するための配列
- `endDateTime` [String]: イベントの終了時刻
- `isExtendedable` [Boolean]: 延長可能であるか (デフォルト値: `true`)

次の手順で、変数を初期化するアクションを追加します。

まず、「イベントの取得 (V4)」の次にある「+」をクリックし、「アクションの追加」をクリックします。  
「アクションの追加」画面で、検索欄に「変数」と入力。「変数 (In-app)」というものが出てくるので、「さらに表示」をクリックします。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/3b876111-01b8-261d-afbe-3acf4cfe0457.png)

一番下にある、「変数を初期化する」をクリックします。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/8fbaa248-2fef-dd0d-da54-8173deb19bc4.png)

変数についての情報を入力する画面に切り替わります。  
先ほどの変数一覧を参考に、以下のように入力します。

- **Name**: 変数名を入力。例えば `resourceAttendees`
- **Type**: 変数の種類を指定。例えば `Array`
- **Value**: 変数の初期値を指定。isExtendedable のみ `true` と入力

ここまでの手順を4回繰り返し、4つの変数を初期化してください。

### 2. resourceAttendees 変数にリソースのメールアドレス配列を格納する

さきほど作成した resourceAttendees 変数に、リソース（会議室）のメールアドレス一覧を設定します。  
For each 内で `item()?['resourceAttendees']` を用いることで取得ができるのですが、この属性には、セミコロン `;` 区切りでメールアドレスが含まれており、かつ最後にもセミコロンが入るため、考慮して抽出する必要があります。  
（例: `resource1@example.com;resource2@example.com;`）

さきほどと同じようなやり方で、For each の真下に、今度は「変数の設定」アクションを追加し、つぎのように設定します。

- **Name**: `resourceAttendees` を選ぶ
- **Value**: 入力欄をクリックして、*fx* をクリック。以下のコードを入力する

```
split(if(
    endsWith(item()?['resourceAttendees'], ';'),
    substring(item()?['resourceAttendees'], 0, sub(length(item()?['resourceAttendees']), 1)),
    item()?['resourceAttendees']
), ';')
```

つぎに、最後にセミコロンが入る都合で空の要素が含まれてしまうため、これをフィルタします。  
以下のようにランタイムで「組み込み」を選び、「Data Operation」から「アレイのフィルター処理」をクリックして追加します。

![20241212_155241_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/a30ffd8a-3471-6d7e-f009-4a2049909ea4.png)

「アレイのフィルター処理」の設定画面では、以下のように設定します。

- **From**: 入力欄をクリックして、稲妻マークをクリック。変数の「resourceAttendees」をクリック
- **Filter Query**
    - 左: 入力欄をクリックして、*fx* をクリック。`empty(item())` と入力
    - 中央: `is equal to` のまま変更しない
    - 右: `false` と入力

![20241212_160158_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/2fa79108-dce0-85b1-df90-cec9814b0684.png)

### 3. resourceTexts に空配列、endDateTime にイベント終了日時を格納する

後続の処理で利用するため、「変数の設定」を用いて二つの変数に値を設定します。

- **Name**: `resourceTexts` を選ぶ
    - **Value**: `[]` と入力
- **Name**: `endDateTime` を選ぶ
    - **Value**: 稲妻マークから、「イベントの取得 (V4)」の「終了時刻」をクリック

### 4. リソースメールアドレスからリソースユーザーを取得する

リソースのメールアドレスは取得できていますが、そのリソースの名前を知りたいため、リソースユーザーを取得します。  
リソースユーザーの取得には、「Office 365 ユーザー」の「ユーザーの検索 (V2)」を利用します。

![20241212_164546_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/a768a67d-00fa-1aad-8d55-460b5550648b.png)

「ユーザーの検索 (V2)」の設定画面では、次のように設定します。

1. 詳細パラメーターの箇所で、「すべて表示」をクリック
2. 「上」（top パラメーター、つまり取得数）で `1` を入力
3. 「検索文字列が必須」は「はい」のまま
4. 「検索語句」は稲妻マークから先ほど作成した「アレイのフィルター処理」の「Body 項目」をクリック

![20241212_164524_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/a3b83eb7-269d-5765-ef2b-8742e176176e.png)

このとき、自動的に作成される For each に名前をつけておいてください。  
私は `Get users next event` と名前を設定しました。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/d88a1a3f-093f-4817-339b-203ceba9c872.png)

### 5. リソースごとに、1時間後までの予定を取得

リソースの予定を取得するのですが、Power Automate では該当するアクションが用意されていないため、[Microsoft Graph API の List calendarView エンドポイント](https://learn.microsoft.com/ja-jp/graph/api/user-list-calendarview?view=graph-rest-1.0&tabs=http) を利用します。  

`http` と検索し、Offifce 365 Outlook の「HTTP 要求を送信します」アクションを追加します。

![20241212_172331_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/4716f277-83f4-9814-c918-a9a781af27bc.png)

「HTTP 要求を送信します」アクションの設定画面では、以下のように設定します。

- **URL**: 入力欄をクリックして、*fx* をクリック。以下のコードを入力する
- **メソッド**: `GET` のまま
- **コンテンツの種類**: `application/json` のまま

```
concat(
  'https://graph.microsoft.com/v1.0/users/',
  trim(items('Get_users_next_event')),
  '/calendar/calendarView?startDateTime=',
  formatDateTime(addSeconds(variables('endDateTime'), 1), 'yyyy-MM-ddTHH:mm:ssZ'),
  '&endDateTime=',
  formatDateTime(addHours(variables('endDateTime'), 1), 'yyyy-MM-ddTHH:mm:ssZ'),
  '&orderby=start/dateTime&top=1'
)
```

これにより、リソースのカレンダーから、イベント終了時刻1秒後から1時間後までにある予定のうち、直近1件のみが取得できます。  
取得した結果は、「Data Operation」の「JSON の解析」で解析します。

![20241212_181038_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/bb7e61fb-8d05-17ea-e169-2f8f7036da51.png)

「JSON の解析」アクションの設定画面では、以下のように設定します。

- **Content**: 稲妻マークから、「HTTP 要求を送信します」の「本文」をクリック
- **Schema**: `{}` を入力

![20241212_181656_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/563a4dd1-edc0-e346-82b9-74025f0dd61a.png)

JSON Schema を指定することで、JSON のバリデーションをかけることもできるのですが、JSON Schema を作りこむ必要があり面倒なため `{}` で回避しています。

### 6. 取得した予定から、メッセージを構成

ここまでで、リソースに1時間以内の次予定があるかどうかが取得できました。
`resourceTexts` 配列変数に結果を設定し、通知用メッセージを構成します。

今回は、リソースごとに以下のように表示したいと思います。

- 次の予定があれば、`{リソース名}: {イベントタイトル} {イベント開始時刻}`
- 次の予定がなければ、`{リソース名}: このあと1時間以内に会議なし`

また、次の予定がある場合は `isExtendedable` を false にし、会議が延長できないことを記録しておきます。

Control から「条件」アクションを追加し、以下のように設定します。

- 左: 入力欄をクリックして、*fx* をクリック。`empty(body('JSON_の解析')?['value'])` と入力
- 中央: `is equal to` のまま変更しない
- 右: `false` と入力

![20241212_184006_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/5b022960-dbe6-fd82-a447-736b6e3f7934.png)

条件の分岐では、以下のように実装します。

- True の場合
  - 「配列変数に追加」アクションを用いて、`resourceTexts` に後述する「①イベントがあるときの resourceTexts の値」を設定
  - 「変数の設定」アクションを用いて、`isExtendedable` に `false` を設定
- False の場合
    - 「配列変数に追加」アクションを用いて、`resourceTexts` に後述する「②イベントがないときの resourceTexts の値」を設定

#### ①イベントがあるときの resourceTexts の値

```
concat(
  first(outputs('ユーザーの検索_(V2)')?['body/value'])['DisplayName'],
  ': ',
  first(body('JSON_の解析')?['value'])['subject'],
  ' ',
  formatDateTime(addHours(first(body('JSON_の解析')?['value'])['start']['dateTime'], 9), 'yyyy-MM-dd HH:mm:ss')
)
```

#### ②イベントがないときの resourceTexts の値

```
concat(
  first(outputs('ユーザーの検索_(V2)')?['body/value'])['DisplayName'],
  ': このあと1時間以内に会議なし'
)
```

### 7. 送信メールの内容を変更

最後に、送信するメールの内容に、リソースの状態を示す文言を追加します。

以下のように、2か所変更を加えます。

1. 件名にて、*fx* から `if(variables('isExtendedable'), '延長可', '延長不可!')` を追加し、延長可能かどうかを件名に追加
2. 本文にて、*fx* から `join(variables('resourceTexts'), '<br>')` を追加し、先ほど構成したメッセージを改行タグ区切りで追加

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/d4e94bb9-088a-16a6-fffc-bfd32c7abd62.png)

## 全体像

ここまでで、やっと改良がおわりました。  
全体像は以下のようになります。

![20241212_190614_chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659670/ac07c4ac-db71-c971-ce8d-f2d6275ca434.png)

## さいごに

前回作成したフローを改良し、会議終了ちょっと前にデスクトップ通知を表示させるだけでなく、会議が延長可能か（会議室の次予定が無いか）どうかを調べ、通知することができました。

ある程度凝ったことをやろうとすると、Power Automate ではやりにくいなあ…と思うことも多いです。
しかし、Microsoft 365 まわりの操作を行う分には認証情報などをあまり気にせず（情シスとかに面倒な申請などをする必要なく）、いろいろ自動化できるので、業務効率化にはもってこいですね。

この記事の内容が参考になったら「いいね」をお願いします:star: