ささみ学習帳 - sasami's study book

ささみ学習帳

Microsoft365 や Power Platform について学んだこと・アイデアのメモ

Microsoft365関連の未使用ライセンス数が指定値を下回ったらTeamsチャネルに通知するPowerAutomateクラウドフロー(V2)💎

この記事は「Microsoft Power Automateのカレンダー | Advent Calendar 2023 - Qiita」 に参加しています。シリーズ2-3日目の記事です。

 

 

このフローで実現できること

Microsoft365関連ライセンスの未使用数が指定した条件を下回ったらTeamsのチャネルで通知します。ライセンスの発注を忘れないように作ってみました。

 

実行イメージ

こんな形Teamsのチャネルに通知します。

 

 

準備するもの

PowerAutomate Premium ライセンス

PremiumコネクタのHTTPを使用するためPowerAutomate Premiumライセンスが必要になります。

※実運用には使用できませんが、PowerApps 開発者向けプランで無料で試してみることはできます。

 

Entra IDにアプリ登録

1.Microsoft Entra 管理センター にアクセス(管理者アカウントでサインイン)

https://entra.microsoft.com/#view/Microsoft_AAD_IAM/

2.ID→アプリケーション→アプリの登録→新規登録

3.アプリの新規登録画面

  • 名前
    • 適切な名前を設定
  • サポートされているアカウントの種類
  • リダイレクトURI
    • 省略

「登録」ボタンをクリックします

 

4.概要に表示される [アプリケーション (クライアント) ID] と [ディレクトリ (テナント) ID] の値をコピーしておきます

 

5.[API のアクセス許可] – [+アクセス許可の追加] をクリック

 

6.[Microsoft Graph] – [アプリケーションの許可] をクリック

 

7.[Organization.Read.All]  を選択して [アクセス許可の追加] をクリック

 

8.[<テナント名> に管理者の同意を与えます] をクリック

確認が表示されるので [はい] をクリック

9.[概要]をクリック

 

10.概要に表示される [アプリケーション (クライアント) ID] と [ディレクトリ (テナント) ID] の値をコピーしておきます

 

11.証明書とシークレット→クライアントシークレット→新しいクライアントシークレット

 

12.説明を入力し有効期限を選択して「追加」

 

13.値をコピーしておく(他の画面に遷移するとコピーできなくなるので忘れずに)

以上で準備は完了です。

 

フロー全体図

 

フロー解説

1.[Schedule] Reccurence (繰り返し) トリガー

今回のフローは月~金曜日12時(日本時間)に自動起動する想定としています。

  • Interval
    • 1
  • Frequency
  • TimeZone
    • (UTC+900) Osaka, Sapporo, Tokyo
  • Start Time
    • 任意
  • On These Days
    • 月曜日, Tuesday, Wednesday, Thursday, 金曜日
  • At These Minutes
    • 12

 

2.[Variable] 変数を初期化する-ライセンス数残警告閾値

通知する条件を指定しますこの変数で指定した数を下回るライセンスが通知対象となります。

  • Name
    • ライセンス数警告閾値
  • Type
    • Integer
  • Value
    • 10


3.[Varible] 変数を初期化する-targetSKUs

通知する対象ライセンスの指定と表示名の設定です。ここに設定がないライセンスだけが通知されます。また、今回使用するAPIの返す値では"Microoft Power Automate free" → "FLOW_FREE"といったIDで返されます。そのままでは直感的でないので馴染みのある名称との対応をとるためのJSONを定義しています。

製品名との対応はこちらのページにありますので、必要なライセンスの分を追加変更してください。

learn.microsoft.com

 

  • Name
    • TargetSKUs
  • Type
    • Object
  • Value
    • {
        "FLOW_FREE": "Microsoft Power Automate free",
        "DEVELOPERPACK_E5": "Microsoft 365 E5 Developer",
        "POWERAPPS_DEV": "Power Apps for Developer",
        "Win10_VDA_E3": "Windows10 Enterprise E3",
        "EXCHANGESTANDARD": "Exchange Online (プラン1)",
        "EXCHANGEARCHIVE_ADDON": "Exchange Online Archiving for Exchange Online",
        "SPE_E3": "Microsoft 365 E3",
        "POWER_BI_STANDARD": "Power BI"
      }

 

4.[Control] スコープ-ライセンス情報を取得

4-1.[HTTP] HTTP - subscribedSku

このフローの本体はここです。HTTPコネクタのHTTPアクションでsubscribedSkus を一覧表示するGraph APIを実行しています。必要なアクセス許可(Organization.Read.All)の関係で、プレミアムコネクタのHTTPアクションが必要になっています。

GET https://graph.microsoft.com/v1.0/subscribedSkus

learn.microsoft.com

 

  • URI
  • Method
    • GET
  • Authentication
    • 認証の種類
    • Tenant
      • 準備するもの-AzureADにアプリ登録 でコピーしておいたテナント(ディレクトリ)ID
    • Audience
    • Client ID
      • 準備するもの-AzureADにアプリ登録 でコピーしておいたクライアント(アプリケーション)ID
    • 資格情報の種類
      • シークレット
    • Secret
      • 準備するもの-AzureADにアプリ登録 でコピーしておいたシークレット

 

4-2.[Data Operation] JSON の解析-subscribedSkus

HTTPアクションの出力を処理しやすいようにJSONの解析アクションで解析します。

  • Content
    • @{body('HTTP-subscribedSkus')}
  • Schema
    • {
          "type": "object",
          "properties": {
              "@@odata.context": {
                  "type": "string"
              },
              "value": {
                  "type": "array",
                  "items": {
                      "type": "object",
                      "properties": {
                          "accountName": {
                              "type": "string"
                          },
                          "accountId": {
                              "type": "string"
                          },
                          "appliesTo": {
                              "type": "string"
                          },
                          "capabilityStatus": {
                              "type": "string"
                          },
                          "consumedUnits": {
                              "type": "integer"
                          },
                          "id": {
                              "type": "string"
                          },
                          "skuId": {
                              "type": "string"
                          },
                          "skuPartNumber": {
                              "type": "string"
                          },
                          "subscriptionIds": {
                              "type": "array",
                              "items": {
                                  "type": "string"
                              }
                          },
                          "prepaidUnits": {
                              "type": "object",
                              "properties": {
                                  "enabled": {
                                      "type": "integer"
                                  },
                                  "suspended": {
                                      "type": "integer"
                                  },
                                  "warning": {
                                      "type": "integer"
                                  },
                                  "lockedOut": {
                                      "type": "integer"
                                  }
                              }
                          },
                          "servicePlans": {
                              "type": "array",
                              "items": {
                                  "type": "object",
                                  "properties": {
                                      "servicePlanId": {
                                          "type": "string"
                                      },
                                      "servicePlanName": {
                                          "type": "string"
                                      },
                                      "provisioningStatus": {
                                          "type": "string"
                                      },
                                      "appliesTo": {
                                          "type": "string"
                                      }
                                  },
                                  "required": [
                                      "servicePlanId",
                                      "servicePlanName",
                                      "provisioningStatus",
                                      "appliesTo"
                                  ]
                              }
                          }
                      },
                      "required": [
                          "accountName",
                          "accountId",
                          "appliesTo",
                          "capabilityStatus",
                          "consumedUnits",
                          "id",
                          "skuId",
                          "skuPartNumber",
                          "subscriptionIds",
                          "prepaidUnits",
                          "servicePlans"
                      ]
                  }
              }
          }
      }

 

4-3[Data Operation] 選択 - ライセンス名取得

HTTPアクションの出力から必要な項目のみを選択します。

同時にTargetSkus変数からライセンス名称を取得します。

  • From
    • @{body('JSON_の解析-subscribedSkus')?['value']}
  • Map
    • 名称
      • @{variables('TargetSKUs')?[item()?['skuPartNumber']]}
      • TargetSKUs変数から表示用の名称を取得
    •  保有総数
      • @{int(item()?['prepaidUnits']?['enabled'])}
    • 使用数
      • @{int(item()?['consumedUnits'])}
    • 未使用数
      • @{add(
            int(item()?['prepaidUnits']?['enabled']),
            mul(
                int(item()?['consumedUnits']),
                -1
            )
        )}
    • skuPartNumber
      • @{item()?['skuPartNumber']}

 

4-4.[Data Operation] JSON の解析-subscribedSkus2

選択アクションの出力を処理しやすいようにJSONの解析アクションで解析します。

 

  • Content
    • @{body('選択-ライセンス名称取得')}
  • Schema
    • {
          "type": "array",
          "items": {
              "type": "object",
              "properties": {
                  "skuPartNumber": {
                      "type": "string"
                  },
                  "保有総数": {
                      "type": "string"
                  },
                  "使用数": {
                      "type": "string"
                  },
                  "未使用数": {
                      "type": "string"
                  },
                  "名称": {
                      "type": "string"
                  }
              },
              "required": [
                  "skuPartNumber",
                  "保有総数",
                  "使用数",
                  "未使用数",
                  "名称"
              ]
          }
      }

 

5.[Control]スコープ-対象を検出して通知

5-1.[Data Operation]アレイのフィルター処理 - targetSKUのみ

通知対象のライセンスのみにフィルタリングします。名称が空欄=対象ライセンスとして定義されていないもの。

  • From
    • @{body('JSON_の解析-subscribedSkus-2')}
  • Filter Query
    • @{item()['名称']}
    • is not equal to
    • (空欄)

 

5-1.[Data Operation]アレイのフィルター処理 - 残ライセンス通知対象

閾値未満のライセンスのみにフィルタリングします。

  • From
    • @{body('アレイのフィルター処理-targetSKUのみ')}
  • Filter Query
    • @{int(item()['未使用数'])}
    • is less or equal to
    • @{variables('ライセンス数残警告閾値')}

 

5-2[Control] 条件 - 通知対象となるライセンス数がある?

通知対象のライセンスがあるか?で分岐します。

  • Condition Expression
    • @{length(body('アレイのフィルター処理-残ライセンス通知対象'))}
    • is greater than
    • 0

 

5-3[Data Operation]HTMLテーブルの作成

表示用にフィルターした結果をHTMLテーブルに変換します。

  • From
    • @{body('アレイのフィルター処理-残ライセンス通知対象')}

 

5-4[Microsoft Teams] チャットまたはチャネルでメッセージを投稿する

作成したHTMLテーブルをチャネルに投稿します。

  • 投稿者
    • フローボット
  • 投稿先
    • Channel
  • Team
    • メッセージを投稿するチームを選択
  • Channel
    • メッセージを投稿するチャネルを選択
  • Message
    • ライセンス残アラート(@{variables('ライセンス数残警告閾値')}未満)
      Output('HTMLテーブルの作成')

 

参考にしたページ

JSON Dictionaryの考え方を参考にさせていただきました。

mofumofupower.hatenablog.com

 

wataruf.hatenablog.com

 

さいごに

今回のフローは2023年6月に投稿したこちらのフローの改良版です。

sasami-axis.hatenablog.com

このフローを作ったことをすっかり忘れていて、頭の中は真っ白な状態から新たにフローを作成しました。半年前の作り方よりもライセンスID→名称変換のあたりがスマートになりました。