SlackへのAWS料金通知を(ほぼ)Step Functionsの機能だけで作ってみた

SlackへのAWS料金通知を(ほぼ)Step Functionsの機能だけで作ってみた

Clock Icon2024.12.11

大阪オフィスの小倉です。

プライベートのAWSアカウントをAWS Organizationsで管理するようになったのですが、
それに伴って従来使っていたSlack料金通知が活用しづらくなってしまいました。
長年お世話になってきたのは以下のブログです。

https://dev.classmethod.jp/articles/notify-slack-aws-billing/

ということで、以下の要件に従い、Step Functionsで作り直してみました。

  • 基本の仕様は上記ブログを踏襲
  • マルチアカウント環境で、アカウント別と合計の料金を通知したい
  • Lambdaを使わずに実装したい

できたもの

https://github.com/number09/aws-org-cost-notify

step-functions-cost-notify-01.png
step-functions-cost-notify-02.png

実装はマネジメントコンソールで行った後、以下の機能でCloudFormationテンプレートにExportしています。
https://dev.classmethod.jp/articles/sfn-workflow-export-to-sam-or-cfn-template/

以降、Step Functionsの実装部分を紹介していきます。

日付の編集

EventBridge Schedulerから送られる日付を利用して、集計日付のFrom-Toを作ります。

  • 基本は当月の月初日〜本日
  • ただし、本日が月初日の場合、前月の月初日〜前月末日

編集処理は、先日対応が発表されたJSONataを使いました。
https://aws.amazon.com/about-aws/whats-new/2024/11/aws-step-functions-variables-jsonata-transformations/

入力のtimeの値を使って、

{
  "version": "0",
  "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "detail-type": "Scheduled Event",
  "source": "aws.scheduler",
  "account": "xxxxxxxxxxxxx",
  "time": "2024-12-10T00:00:00Z",
  "region": "ap-northeast-1",
  (省略)
}

From-Toを作ります。

{
  "start_date": "2024-12-01",
  "end_date": "2024-12-10"
}

https://github.com/number09/aws-org-cost-notify/blob/f89bc8d23c466437b74e9c4bad9a0568efc974aa/template.yaml#L37-L74

JSONataはプログラミング言語としての機能を備えているので、変数、条件分岐、ループなどが使えます。
うるう年の判定とかあるので、だいぶ力技です。。。

サービス料金をアカウント毎に集計

組織の管理アカウント上でGetCostAndUsage APIを実行すると、
配下のアカウントの料金情報もまとめて取得できますが、ResultsByTime.Groupsという配列の中に全アカウント分入ってしまうので、 こちらもJSONataで編集・集計します。

APIの出力は以下イメージですが、

{
	"DimensionValueAttributes": [
		{
			"Attributes": {
				"description": "account-1"
			},
			"Value": "111111111111"
		},
		{
			"Attributes": {
				"description": "account-2"
			},
			"Value": "222222222222"
		},
        (略)
	],
	"GroupDefinitions": [
		{
			"Key": "LINKED_ACCOUNT",
			"Type": "DIMENSION"
		},
		{
			"Key": "SERVICE",
			"Type": "DIMENSION"
		}
	],
	"ResultsByTime": [
		{
			"Estimated": true,
			"Groups": [
				{
					"Keys": [
						"111111111111",
						"AWS Cost Explorer"
					],
					"Metrics": {
						"AmortizedCost": {
							"Amount": "0.58",
							"Unit": "USD"
						}
					}
				},
				{
					"Keys": [
						"111111111111",
						"AWS Lambda"
					],
					"Metrics": {
						"AmortizedCost": {
							"Amount": "0.0000123252",
							"Unit": "USD"
						}
					}
				},
    (略)
}

このように、アカウント毎にまとめてみました。

{
  "accounts": [
    {
      "accountId": "111111111111",
      "description": "account-1",
      "totalCost": "2.13 USD",
      "services": {
        "AWS Cost Explorer": "0.18 USD",
        "AWS Secrets Manager": "0.11 USD",
        "EC2 - Other": "0.09 USD",
        "Amazon Route 53": "0.5 USD",
        "Amazon Simple Storage Service": "1.04 USD",
        "Tax": "0.2 USD"
      }
    },
    {
      "accountId": "222222222222",
      "description": "account-2",
      "totalCost": "0.32 USD",
      "services": {
        "AWS Cost Explorer": "0.18 USD",
        "AWS Secrets Manager": "0.11 USD",
        "Tax": "0.03 USD"
      }
    },
    {
      "accountId": "333333333333",
      "description": "account-3",
      "totalCost": "0.41 USD",
      "services": {
        "AWS Cost Explorer": "0.37 USD",
        "Tax": "0.04 USD"
      }
    }
  ],
  "total": "2.86 USD"
}

https://github.com/number09/aws-org-cost-notify/blob/f89bc8d23c466437b74e9c4bad9a0568efc974aa/template.yaml#L98-L125

あとは、アカウント毎にMapステートで処理してあげます。

まとめ

JSONataがサポートされたことで、従来Lambdaで行っていた編集処理をStep Functions内で完結させることができました。
JSONataは今回利用した書き方だけでなく、クエリによる配列やオブジェクトの操作、ソートや集計、各種関数を利用することが出来るので、
ステート入出力の編集処理は恐らくJSONataでほぼ対応できるのではないでしょうか。

Web上にPlaygroundも多数あり、ブラウザからすぐ試すことも出来ますので、
少しでも気になる方は是非JSONataのドキュメントを確認してみてください。

参考資料

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.