Azure利用コストをLogic AppsでSlackに通知しよう #azure #logicapps #slack

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
はじめに
クラウドサービスは気軽に使える反面、お手軽すぎてついついコストがかさんでしまうことがあり、日々のコスト追跡は重要です。本稿では Microsoft Azure の利用コストを、ノーコード・ローコード開発サービスである Azure Logic Apps (ロジックアプリ)を使用して Slack チャットに通知する方法をみてみます。
を参考にさせていただきました。本稿の内容は内容は2024年6月現在の仕様であるため、現在のAzureの仕様や、参考文献とは異なる部分があることにご注意ください。また、すべてステップバイステップで記載すると膨大な分量になってしまうため、概説となることもご了承ください。詳しい手順や不明点、本稿と実際の手順との差異などはMicrosoft Azure公式ドキュメントを確認してください。
ロジックアプリの作成と権限割り当て
マーケットプレイスから「ロジック アプリ」を作成しましょう。多くの入力項目が表示されますが、まず「プラン」を「消費」にすると必要最低限の入力項目のみに表示が切り替わるので、これで作成します。
作成したロジックアプリの「設定」から「ID」を開き、「システム割り当て済み」タブの「状態」スイッチを「オン」にします。次に「Azureロールの割り当て」ボタンを押し、ロジックアプリに「コスト管理の閲覧者」ロールを割り当てます。これで、ロジックアプリからCost Management API (コスト管理API)にアクセスすることができます。
ロジックアプリデザイナー
ここから実際のロジックを組んでいきます。その前に、どんなアプリを作るのかまとめておきましょう。
- 日本時間の朝10時に実行
- コスト管理APIに問い合わせ
- 特定のリソースグループを対象
- サービスごとにコストを集計
- 実行月のトータルコストを取得
- API問い合わせの結果を整形
- 整形結果をSlackへポスト
大雑把にこのようなアプリを作ります。これを例えばPythonで書くとするとなかなか手間がかかりそうなので、ノーコード・ローコードで開発できるAzure Logic Appsを使います。
日本時間の朝10時に実行
まずはロジックアプリの実行トリガーを設定しましょう。「トリガーの追加」から「schedule」を検索し、その中から「Recurrence」(繰り返し)を選びます。実行タイミングは「Interval」を「1」、「Frequency」を「日」、「Time Zone」を「UTC+09:00」、「At These Hours」を「10」とします。これで「日本時間の朝10時に実行」トリガーとなります。また、上の「Recurrence」部分はクリックすると編集できるので、「朝10時実行」などと付け足しておくと後から見たときもわかりやすくなります。

コスト管理APIに問い合わせ
作成した「Recurrence - 朝10時実行」の下にある丸十字記号をクリックして「アクションの追加」を選び、先程のように「http」を検索し、その中から「HTTP」を選びます。Query - Usage - REST API (Azure Cost Management)を参考に、APIに問い合わせる内容を埋めていきましょう。

まずURIは次の内容とします。【サブスクリプションID】はお使いのものを入れてください。
https://management.azure.com/subscriptions/【サブスクリプションID】/providers/Microsoft.CostManagement/query?api-version=2023-11-01
Methodは POST とします。Headersはキーに Content-Type 、値に application/json を入力します。
Bodyには問い合わせるものの詳細を入力することになります。
- 特定のリソースグループを対象
- サービスごとにコストを集計
- 実行月のトータルコストを取得
という条件にすることを考えていたので、これを満たすものを作りましょう。例を参考にしつつ作成したものが次のJSONです。
{
"type": "Usage",
"timeframe": "MonthToDate",
"dataset": {
"granularity": "None",
"aggregation": {
"totalCost": {
"name": "PreTaxCost",
"function": "Sum"
}
},
"grouping": [
{
"type": "Dimension",
"name": "ServiceName"
}
],
"filter": {
"dimensions": {
"name": "ResourceGroupName",
"operator": "In",
"values": [
"【特定のリソースグループ】"
]
}
}
}
}
- 特定のリソースグループを対象
"filter"部で指定しています。
- サービスごとにコストを集計
"grouping"部で指定しています。
- 実行月のトータルコストを取得
"timeframe"部と"aggregation"部で指定しています。
次に認証設定を行います。下にある「詳細パラメーター」の「すべてを表示」を押します。表示されたAuthenticationで、「認証の種類」を「マネージドID」、「マネージドID」を「システム割り当てマネージドID」とします。

これで問い合わせ部分は完成です。
API返り値の処理
コスト管理APIからは、JSON形式で結果が返ってきます。その中から必要な値を取得するには「Parse JSON」アクションを使います。

「Content」の入力欄にフォーカスを合わせると、稲妻アイコンが出てくるのでそれをクリックします。ここでは「HTTP」の「Body」を選択しましょう。つまり、先のコスト管理APIに対してHTTP問い合わせしたレスポンスボディを処理対象にするという意味になります。
「Schema」には、Contentをどのようにパースするかを書き込むのですが、これを一から書くのはなかなか大変です。少し下にスクロールすると、「サンプルのペイロードを使用してスキーマを生成する」というリンクがあります。これを使うと、実際の結果からそれらしくスキーマを作ってくれます。

では、ここに与える「サンプルのペイロード」を用意しましょう。既に私達はAPIのエンドポイントURIとポストする問い合わせ内容を持っていますので、curlを使って問い合わせしてみましょう。
まず、新しくコンソールターミナルを開き、Bearer Tokenを取得します。
az account get-access-token | jq .accessToken - "eyJ.....INw"
ダブルクォートでくくられたとても長い部分がBearer Tokenです。これをコピーしておきます。
そして次のコマンドを実行します。その際、「Bearer Token」と「サブスクリプションID」は置き換えてください。
cat | curl -X POST -d @- -H 'Authorization: Bearer 【Bearer Token】' -H 'Content-Type: application/json' 'https://management.azure.com/subscriptions/【サブスクリプションID】/providers/Microsoft.CostManagement/query?api-version=2023-11-01' | jq . -
標準入力を待っているので、問い合わせ内容のJSONを入れてみましょう。すると、
{
"id": "subscriptions/【サブスクリプションID】/providers/Microsoft.CostManagement/query/★★★★★",
"name": "★★★★★",
"type": "Microsoft.CostManagement/query",
"location": null,
"sku": null,
"eTag": null,
"properties": {
"nextLink": null,
"columns": [
{
"name": "PreTaxCost",
"type": "Number"
},
{
"name": "ServiceName",
"type": "String"
},
{
"name": "Currency",
"type": "String"
}
],
"rows": [
[
19.6714278148716,
"Azure App Service",
"JPY"
],
[
3790.44384522,
"Azure Cognitive Search",
"JPY"
],
[
0.0,
"Bandwidth",
"JPY"
],
[
608.2010685117,
"Cognitive Services",
"JPY"
],
[
3.26420576120105,
"Logic Apps",
"JPY"
],
[
19.3829152823112,
"Storage",
"JPY"
]
]
}
}
特定のリソースグループ内で実行しているサービスの利用料金が取得できました! これを貼り付けて「完了」ボタンを押すと、パース用のスキーマが自動作成されます。
変数の用意
パースして取得した値を格納する変数を2つ用意します。
まず1つ目はコスト一覧を格納する配列です。「Initialize variable」アクションを追加し、「Name」は cost_list 、「Type」は Array とします。

2つ目はコストの合計を格納する変数です。もう1つ「Initialize variable」アクションを追加し、「Name」は total_cost 、「Type」は Float 、「Value」は 0 とします。

繰り返し処理
コスト管理APIから返ってきた値をここから処理していきます。繰り返し処理には「For each」を使います。処理対象とするデータは、レスポンス「Body」の「rows」要素になります。

"rows": [
[
19.6714278148716,
"Azure App Service",
"JPY"
],
[
3790.44384522,
"Azure Cognitive Search",
"JPY"
],
[
0.0,
"Bandwidth",
"JPY"
],
[
608.2010685117,
"Cognitive Services",
"JPY"
],
[
3.26420576120105,
"Logic Apps",
"JPY"
],
[
19.3829152823112,
"Storage",
"JPY"
]
]
「For each」により、「rows」配列の要素が1つずつ処理されます。
個々のコスト取得
「Compose」により、「For each」処理で受け取った要素から値を取り出します。ここでは items('For_each')?[0] とします。

これはつまり、
[
19.6714278148716,
"Azure App Service",
"JPY"
],
の0番目の要素である 19.6714278148716 が取り出されます。「Azure App Service」のコストが日本円で約 20 円ということになります。
合計コストに加算
「Increment variable」で計算を行います。ここでは先の「変数の用意」で準備した Float 型の変数 total_cost に直前の「個々のコスト取得」で取得した値を加算する、という処理を行います。

つまり、現時点では total_cost に加算された値は 19.6714278148716 となります。
サービス名取得
再び「Compose」により、「For each」処理で受け取った要素から値を取り出します。今度は items('For_each')?[1] です。

すなわち、
[
19.6714278148716,
"Azure App Service",
"JPY"
],
の1番目の要素である Azure App Service が取り出されます。
コスト一覧arrayに追加
「Append to array variable」で配列に要素を追加します。ここでは先の「変数の用意」で準備した array 型の変数 cost_list に、直前の「サービス名取得」で取得した値と、そのコストを結びつけた文字列を追加します。

この式は ロジックアプリでAzureのコストを毎日Slackに通知する にて紹介されていた内容をそのまま流用させていただきました。
ここまでの処理を繰り返し、コストの合計 total_cost と、サービスとコストの一覧 cost_list を生成します。
ここからは、コストの合計と、サービスとコストの一覧をSlackに通知するためのメッセージの整形処理になります。
「コスト一覧」のソート
コストの一覧 cost_list をそのまま表示してもよいですが、せっかくなのでサービス名の順でソートします。「Compose」を使い、sort(variables('cost_list')) とします。

コスト一覧を作成
コストの一覧 cost_list は配列のため、「Join」を利用して要素を連結し文字列に加工します。

ここでは要素を「JPY」と「改行」で連結しています。
「コスト一覧を作成」の文字列を取得
「Compose」によって、コストの一覧の要素を連結した文字列を取得しています。

「合計コスト」の文字列を取得
「Compose」によって、コストの合計を文字列として取得しています。

メッセージの投稿
「メッセージの投稿 (V2)」でSlackにメッセージを投稿しましょう。Slackとの連結方法についてはここでは割愛します。

convertFromUtc(utcNow(), ...) で現在時刻を取得し、先の「「コスト一覧を作成」の文字列を取得」と「「合計コスト」の文字列を取得」でそれぞれ取得した内容をメッセージ内に埋め込んでいます。
実際の通知
実際にSlackに通知された結果は次のようになります。

2024-07 の RG: 【特定のリソースグループ】 のコスト Azure App Service : 約 19 JPY Azure Cognitive Search : 約 3999 JPY Bandwidth : 約 0 JPY Cognitive Services : 約 608 JPY Logic Apps : 約 3 JPY Storage : 約 19 JPY 合計: 4650.154427765051 JPY
まとめ
本稿では
を参考に、Azureの利用コストをAzure Logic Appsを使用してSlackに通知するようにしてみました。これにより毎日のコストがプッシュ通知されてくるため、毎日のコスト変動の監視が容易になります。
ただ、今回の設定は毎朝10時にその月のコストを通知する、というものであるため、月末最終日の朝10時〜24時の間の利用コストは通知されないことに注意が必要です。その間の利用コストは翌日、つまり月初の通知に含められず、その間に急激なコスト上昇があってもわからないため、他の条件のロジックアプリや、コスト管理によるコスト予測との組み合わせも重要となるでしょう。本稿が参考となれば幸いです。
