0
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?

PowerAutomate子フロー:他者Outlook予定を一発で取得するサブフロー

0
Posted at

きっかけ

Outlook予定は「Outlook:イベントのカレンダービューの取得」で取得できますが、自身やOutlookされてる一部のメンバしかカレンダーID指定できません。
また、以下のように気を付けなければならない共通要素も多いです。

  • 時間帯の扱い(日本時間⇔UTC)
  • 取得件数がデフォルト10件(10件以上の予定を取りこぼす)
  • キャンセル予定はデフォルトで含まれる
  • 自身の非公開予定は必要に応じて隠匿化対応しないと公にさらされる

これら共通要素を加味して、メアド検索で必要最低限の予定情報(時間帯+件名のみ)を取得できるサブフローを作成したので記事を書きます。

PowerAutomate:子フローの作成 をベースに(作成したフロー)を子フロー化したので内容を紹介します。

このフローをつくると…

子フローでユーザメアド、予定取得期間などを指定すると対象者氏名、メアドと指定期間の予定一覧(件名+時間帯)をJSON応答します。

<対象メンバ・日付の予定>

<親フローの実装例>

<処理結果(JSON応答値)>

{
  "氏名": "〇〇 ××",
  "メアド": "〇×@****.com",
  "予定": [
    {
      "時間帯": "2026/04/26 23:00:00-2026/04/27 11:00:00",
      "件名": "前日日またぎ"
    },
    {
      "時間帯": "2026/04/27終日",
      "件名": "終日予定"
    },
    {
      "時間帯": "2026/04/27 08:30:00-2026/04/27 09:00:00",
      "件名": "9時終わり"
    },
    {
      "時間帯": "2026/04/27 13:00:00-2026/04/27 13:30:00",
      "件名": "非公開"
    },
    {
      "時間帯": "2026/04/27 15:15:00-2026/04/27 15:45:00",
      "件名": "途中予定"
    },
    {
      "時間帯": "2026/04/27 18:00:00-2026/04/28 03:00:00",
      "件名": "後日日またぎ"
    },
    {
      "時間帯": "2026/04/27 19:00:00-2026/04/27 19:30:00",
      "件名": "19時開始"
    }
  ]
}

子フロー全体像

構成は以下の通りです。

①トリガ設定

親フローからは7個の引数を渡します。

名前 種類 パラメータ名 必須/非必須 備考
メアド テキスト text 必須
開始日 日付 date 未指定の場合は当日日付
終了日 日付 date_1 未指定の場合は当日(処理場は翌日)日付
取得件数 number 未指定の場合は20件
日付簡略化 はい/いいえ boolean 未指定の場合は通常表示
キャンセル含む はい/いいえ boolean_1 未指定の場合はキャンセル予定除く
未承諾除く はい/いいえ boolean_2 未指定の場合は未承諾予定含む

②変数初期化

『変数を初期化する』で以下7個の変数を設定します。

名前 種類 初期値
開始日時 文字列 @{convertToUtc(convertFromUtc(utcNow(),'Tokyo Standard Time','yyyy-MM-dd'), 'Tokyo Standard Time', 'yyyy-MM-ddTHH:mm:ss')}
終了日時 文字列 @{convertToUtc(convertFromUtc(addDays(variables('開始日時'),1,'yyyy-MM-ddTHH:mm:ss.000000Z'),'Tokyo Standard Time','yyyy-MM-dd'), 'Tokyo Standard Time', 'yyyy-MM-ddTHH:mm:ss')}
取得件数 整数 20
日付簡略化フラグ ブール値 @{if(and(not(equals(triggerBody()?['boolean'],null)),triggerBody()?['boolean']),true,false)}
キャンセル含むフラグ ブール値 @{if(and(not(equals(triggerBody()?['boolean_1'],null)),triggerBody()?['boolean_1']),true,false)}
未承諾除くフラグ ブール値 @{if(and(not(equals(triggerBody()?['boolean_2'],null)),triggerBody()?['boolean_2']),true,false)}
予定配列 アレイ

③スコープ1:入力チェック


③-1) 開始日入力あり?

『条件』で開始日の空値判定をします。

  • 左辺: (式)triggerBody()?['date']
  • 条件:次の値に等しくない
  • 右辺: (式)null

③-2) 開始日時変数の設定

『変数の設定』で開始日時を指定値に設定します。

  • 値:@{convertToUtc(triggerBody()['date'], 'Tokyo Standard Time', 'yyyy-MM-ddTHH:mm:ss')}

③-3) 終了日入力あり?

『条件』で終了日の空値判定をします。

  • 左辺: (式)triggerBody()?['date_1']
  • 条件:次の値に等しくない
  • 右辺: (式)null

③-4) 終了日時変数の設定

『変数の設定』で終了日時を指定値に設定します。

  • 値:@{convertToUtc(convertFromUtc(addDays(triggerBody()['date_1'],1,'yyyy-MM-ddTHH:mm:ss.000000Z'),'Tokyo Standard Time','yyyy-MM-dd'), 'Tokyo Standard Time', 'yyyy-MM-ddTHH:mm:ss')}

③-5) 開始日入力あり(終了日入力なし)?

『条件』で開始日の空値判定をします。

  • 左辺: (式)triggerBody()?['date']
  • 条件:次の値に等しくない
  • 右辺: (式)null

③-6) 終了日時変数の設定(=開始日時)

『変数の設定』で終了日を開始日翌日に設定します。

  • 値:@{convertToUtc(convertFromUtc(addDays(triggerBody()['date'],1,'yyyy-MM-ddTHH:mm:ss.000000Z'),'Tokyo Standard Time','yyyy-MM-dd'), 'Tokyo Standard Time', 'yyyy-MM-ddTHH:mm:ss')}

③-7) 開始日<終了日?

『条件』で開始日と終了日が逆転してないか判定します。

  • 左辺: (式)ticks(variables('開始日時'))
  • 条件:次の値未満
  • 右辺: (式)ticks(variables('終了日時'))

③-8) エラー応答(開始-終了NG)

『PowerApp または Flow に応答する』で応答値を設定します。

  • 出力の種類:テキスト
  • 名称:応答
  • 値:開始日が終了日より大きいです

③-9) 終了(開始-終了NG)

『終了』で処理を停止します。

  • 状態:失敗

③-10) 取得件数入力あり?

『条件』で取得件数の空値判定をします。

  • 左辺: (式)triggerBody()?['number']
  • 条件:次の値に等しくない
  • 右辺: (式)mull

③-11) 取得件数変数の設定

『変数の設定』で取得件数を指定値に設定します。

  • 値:@{int(triggerBody()?['number'])}

③-12) 取得件数が指定範囲内?

『条件』で取得件数が指定範囲値内か判定をします。

<And条件1>

  • 左辺: (式)variables('取得件数')
  • 条件:次の値より大きい
  • 右辺: (値)0

<And条件2>

  • 左辺: (式)variables('取得件数')
  • 条件:次の値未満
  • 右辺: (値)1000

③-13) エラー応答(取得件数不正)

『PowerApp または Flow に応答する』で応答値を設定します。

  • 出力の種類:テキスト
  • 名称:応答
  • 値:取得件数の指定(=@{variables('取得件数')})が不正です

③-14) 終了(取得件数不正)

『終了』で処理を停止します。

  • 状態:失敗

④スコープ2:予定検索


④-1) ユーザ検索

(Office365ユーザー)『ユーザーの検索 (V2)』で検索語句に入力メアドを設定します。

  • 値:@{triggerBody()['text']}

トリガーで「メアド」指定としてますが、ユーザーの検索で一意の検索結果が得られれば、氏名など別の値でも処理できます。一意の結果にならないケースの考慮が煩雑なため、メアド指定としています。


④-2) 検索結果1件?

『条件』でユーザ検索結果の件数判定をします。

  • 左辺: (式)length(outputs('ユーザ検索')?['body/value'])
  • 条件:次の値に等しい
  • 右辺: (値)1

④-3) エラー応答(ユーザ検索NG)

『PowerApp または Flow に応答する』で応答値を設定します。

  • 出力の種類:テキスト
  • 名称:応答
  • 値:入力メアドでユーザが特定できませんでした

④-4) 終了(ユーザ検索NG)

『終了』で処理を停止します。

  • 状態:失敗

④-5) ユーザID作成

『作成』で検索結果からユーザIDを作成します。

  • 入力:@{outputs('ユーザ検索')?['body/value']?[0]?['id']}

④-6) HTTP要求:カレンダーID検索

Office365 Outlook『HTTP要求を送信します』で以下パラメータを設定し、指定ユーザのカレンダー情報を取得します。

パラメータ名
URI https://graph.microsoft.com/v1.0/users/@{outputs('ユーザID作成')}/calendars
メソッド GET
本文
コンテンツの種類 application/json

④-7) カレンダーID取得OK?

『条件』でカレンダーIDの取得判定をします。

  • 左辺: (式)contains(body('HTTP要求:カレンダーID検索'),'error')
  • 条件:次の値に等しい
  • 右辺: (式)false

前段処理が失敗した場合も処理されるよう三点メニュー→「実行条件の構成」で「に失敗しました」「がタイムアウトしました」にもチェックを入れます。

当社は会議室の一部がカレンダーID取得できませんでした。
カレンダーIDが取得できた場合はGraph APIの「CalendarView」で詳細情報を取得(フロー:④-8~15)し、取得できなかった場合は代替情報としてGraph APIの「getSchedule」で空き情報を取得(フロー:④-16~20)してます。


④-8) 予定表の抽出

データ操作『アレイのフィルター処理』で以下パラメータを設定し、指定時間帯、指定会議室(複数)の空き状況を取得します。

  • 差出人: @body('HTTP要求:カレンダーID検索')?['value']
  • 条件: (式)@or(equals(item()?['name'], '予定表'),equals(item()?['name'], 'Calendar'))

詳細設定モードで編集します


④-9) 予定表抽出OK?

『条件』で予定表抽出結果が1件か判定します。

  • 左辺: (式)length(body('予定表の抽出'))
  • 条件:次の値に等しい
  • 右辺: (式)1

④-10) エラー応答(予定表抽出NG)

『PowerApp または Flow に応答する』で応答値を設定します。

  • 出力の種類:テキスト
  • 名称:応答
  • 値:予定表が抽出できませんでした

④-11) 終了(予定表抽出NG)

『終了』で処理を停止します。

  • 状態:失敗
  • メッセージ:予定表が抽出できませんでした

④-12) カレンダーID作成

『作成』でカレンダー取得結果からカレンダーIDを作成します。

  • 入力:@{body('予定表の抽出')?[0]?['id']}

④-13) HTTP要求:予定検索

Office365 Outlook『HTTP要求を送信します』で以下パラメータを設定し、指定カレンダーID、指定時間帯、指定条件の予定を取得します。

パラメータ名
URI https://graph.microsoft.com/v1.0/users/@{outputs('ユーザID作成')}/calendars/@{outputs('カレンダーID作成')}/calendarView?$top=@{variables('取得件数')}&startDateTime=@{variables('開始日時')}&endDateTime=@{variables('終了日時')}&@{if(variables('キャンセル含むフラグ'), '', '$filter=isCancelled eq false')}&$orderby=start/dateTime
メソッド GET
本文
コンテンツの種類 application/json
CustomerHeader1 Prefer: outlook.timezone="Tokyo Standard Time"

④-14) 予定選択

『選択』で、「④-13) HTTP要求:予定検索」の情報のうち必要情報のみを整形して選択します。

  • 開始:(式)@{body('HTTP要求:予定検索')?['value']}
  • マップ:以下参照
パラメータ名
subject @item()?['subject']
startDateTime convertTimeZone(item()?['start/dateTime'],item()?['start/timeZone'],'Tokyo Standard Time','yyyy-MM-dd HH:mm:ss')
endDateTime convertTimeZone(item()?['end/dateTime'],item()?['end/timeZone'],'Tokyo Standard Time','yyyy-MM-dd HH:mm:ss')
isAllDay @item()?['isAllDay']
sensitivity @item()?['sensitivity']
response @item()?['responseStatus/response']

④-15) 予定配列の設定(予定)

『変数の設定』で予定配列に「④-14) 予定選択」を設定します。

  • 式:@{body('予定選択')}

④-16) マイプロフィール取得

(Office365ユーザー)『マイ プロフィールの取得 (V2)』を実行します。


④-17) HTTP要求:スケジュール取得

Office365 Outlook『HTTP要求を送信します』で以下パラメータを設定し、空きスケジュール情報を取得します。

パラメータ名
URI https://graph.microsoft.com/v1.0/users/@{outputs('マイプロフィール取得')?['body/id']}/calendar/getSchedule
メソッド POST
本文 { "schedules": ["@{outputs('ユーザ検索')?['body/value']?[0]?['Mail']}"], "startTime": { "dateTime": "@{variables('開始日時')}", "timeZone": "UTC" }, "endTime": { "dateTime": "@{variables('終了日時')}", "timeZone": "UTC" }, "availabilityViewInterval": 15}
コンテンツの種類 application/json

④-18) キャンセル除去して抽出

データ操作『アレイのフィルター処理』で以下パラメータを設定し、キャンセル含むフラグがない場合はキャンセル情報を除去して抽出します。

  • 差出人: @outputs('HTTP要求:スケジュール取得')?['body']?['value']?[0]?['scheduleItems']
  • 条件: (式)@and(not(variables('キャンセル含むフラグ')),not(startsWith(item()?['subject'], 'キャンセル済み:')),not(startsWith(item()?['subject'], 'Canceled:')))

詳細設定モードで編集します


④-19) スケジュール選択

『選択』で、「④-18) キャンセル除去して抽出」の情報のうち必要情報のみを整形して選択します。

  • 開始:(式)@{body('キャンセル除去して抽出')}
  • マップ:以下参照
パラメータ名
subject @item()?['subject']
startDateTime @convertTimeZone(item()?['start/dateTime'],item()?['start/timeZone'],'Tokyo Standard Time','yyyy-MM-dd HH:mm:ss')
endDateTime @convertTimeZone(item()?['end/dateTime'],item()?['end/timeZone'],'Tokyo Standard Time','yyyy-MM-dd HH:mm:ss')
isAllDay @if(equals(addDays(item()?['start/dateTime'],1,'yyyy-MM-dd HH:mm:ss'),formatDateTime(item()?['end/dateTime'],'yyyy-MM-dd HH:mm:ss')),true,false)
sensitivity @if(item()?['isPrivate'],'private','normal')
response @null

④-20) 予定配列の設定(スケジュール)

『変数の設定』で予定配列に「④-19) スケジュール選択」を設定します。

  • 式:@{body('スケジュール選択')}

⑤スコープ3:スコープ3:データ整形


⑤-1) 日付フォーマット作成

『作成』で経過日数をふまえた日付変数を作成します。

  • 入力:@{if(variables('日付簡略化フラグ'),'MM/dd HH:mm','yyyy/MM/dd HH:mm:ss')}

⑤-2) 未承諾除去(フラグON時のみ)

データ操作『アレイよフィルター処理』で以下パラメータを設定し、未承諾除くフラグがONの場合は予定の応諾ステータスが特定のものを取り除きます。

  • 差出人: @variables('予定配列')
  • 左辺: (式)if(variables('未承諾除くフラグ'), createArray('notResponded', 'tentativelyAccepted'), createArray(''))
  • 条件:次の値を含まない
  • 右辺: (式)@item()?['response']

⑤-3) 検索結果の変換

データ操作『選択』で以下パラメータを設定し、絞り込みした予定情報から件名、時間帯を取得します。

  • 差出人: @{body('未承諾除去(フラグON時のみ)')}
  • マップ1: 時間帯=(以下値)
if(item()?['isAllDay'],
  formatDateTime(item()?['startDateTime'],'yyyy/MM/dd終日'),
  concat(
    formatDateTime(item()?['startDateTime'],outputs('日付フォーマット作成')),
    '-'
    ,if(and(variables('日付簡略化フラグ'),equals(
      first(split(item()?['startDateTime'],' ')),
      first(split(item()?['endDateTime'],' '))
    )),
    last(split(formatDateTime(item()?['endDateTime'],outputs('日付フォーマット作成')),' ')),
    formatDateTime(item()?['endDateTime'],outputs('日付フォーマット作成'))
)))
  • マップ2: 件名=if(equals(item()?['sensitivity'],'normal'),item()?['subject'],'非公開')

⑥応答設定

『PowerApp または Flow に応答する』で応答にJSONデータを設定します。
「氏名」は姓・名がある場合はSurname/GivenNameを設定し、ない場合はDisplayNameを設定します。

  • 出力の種類:テキスト
  • 名称:応答
  • 設定値:(以下参照)
{
  "氏名" : "@{if(empty(outputs('ユーザ検索')?['body/value']?[0]?['Surname']), outputs('ユーザ検索')?['body/value']?[0]?['DisplayName'], concat(outputs('ユーザ検索')?['body/value']?[0]?['Surname'], ' ', outputs('ユーザ検索')?['body/value']?[0]?['GivenName']))}",
  "メアド" : "@{outputs('ユーザ検索')?['body/value']?[0]?['Mail']}",
  "予定" : @{body('検索結果の変換')}
}
0
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
0
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?