GAS(Google Apps Script)でAWS Cost Explorerから先月の料金を取得してみました。Slack Botでやったりする方法は多く情報がありますが、GASを使う方法はあまり情報がなかったのでノウハウを共有しようと思います。
ゴール
まず、今回作ったもののを紹介します。自動でAWSの月額料金を取得して、Google スプレッドーシートに書き込むということをやっています。スプレッドシートはこんなイメージです。
やってみた結果
難しい部分は2つあると思います。まずはAWSの認証です。これは先人のライブラリを使って解決できます。2つ目はAPIのパラメータ作成が難しいです。これも参考となる記事があったので進める上で指針になったのですが、それでも難しいなと感じました。
手順
1. Googleスプレッドシートを用意します
手順は割愛します。
2. Apps Scriptの画面を開く
手順は割愛します。
3. aws.gsファイルを作成し、コードを貼り付ける
ここではAWSの認証部分を作成していきます。ここで先人が作成した認証ライブラリを使用します。
aws.gsファイルを作成します。
aws.jsファイルの中身をコピーします。
[blogcard url=”https://github.com/smithy545/aws-apps-scripts”]aws.gsファイルに貼り付けます。
4. テスト関数を設定する
まずはテスト関数を作成し、AWSの認証と情報取得ができることを確認します。ここが上手く行かないと、AWS Cost Explorerの細かいパラメータ作成ができません。
main.gsファイルを作成し、下記(テスト関数)をコピー&ペーストします。
function test(){
const awsAccessKey = PropertiesService.getScriptProperties().getProperty("AWS_ACCESS_KEY");
const awsSecretKey = PropertiesService.getScriptProperties().getProperty("AWS_SECRET_KEY");
AWS.init(awsAccessKey, awsSecretKey)
const res = AWS.request(
'ce',
'us-east-1',
'AWSInsightsIndexService.GetCostAndUsage',
{},
'POST',
{"TimePeriod": {"Start": "2023-06-01", "End": "2023-07-01"}, "Granularity": "MONTHLY", "Metrics": ["BlendedCost"]},
{"Content-Type": "application/x-amz-json-1.1"},
)
const code = res.getResponseCode()
const text = res.getContentText();
Logger.log(code)
Logger.log(text)
}
5. スクリプトプロパティを設定します
GASのスクリプトプロパティを下記のように設定します。
プロパティ | 値 |
---|---|
AWS_ACCESS_KEY | AWSのIAMから取得したものを設定 |
AWS_SECRET_KEY | AWSのIAMから取得したものを設定 |
6. テスト実行
テスト関数を実行してAWSの認証とAWSからの情報取得ができているか確認します。実行したあと、下記のようなログが出力されれば正常に情報が取得されています。そして Amount
に金額は入っています。
ここまででAWSの認証とAWS Cost Explorerから情報を取得できました。
7. Cost Explorerのパラメータを作成する
今度はAWS Cost Explorerのパラメータを作成します。先ほどのテスト関数は先月分の全体の金額を取得するものでした。今回は特定のタグがついたリソースの金額を取得することを目標とします。具体的にはAWS.requestのパラメータを作成することになります。
このパラメータ作成はこちらの記事がとても参考になります。
[blogcard url=”https://akkinoc.dev/posts/2022/05/15/aws-api-from-google-apps-script/#awsrequest-%E3%81%AE%E5%BC%95%E6%95%B0″]この記事で紹介されているパラメータの確認の方法はAWS CLIのログから確認するという方法です。まずはAWS CLIで特定のタグが付いたリソースの金額を取得してみます。
TAG-VALUEには実際のタグの値を入れてください
aws ce get-cost-and-usage --time-period Start="2023-06-01",End="2023-07-01" --granularity MONTHLY --metrics "BlendedCost" "UnblendedCost" --group-by Type=TAG,Key=Customer --filter '{"Tags": { "Key": "Customer", "Values": ["TAG-VALUE"] }}' --debug
ログの一部
2023-07-11 14:44:31,108 - MainThread - botocore.endpoint - DEBUG - Making request for OperationModel(name=GetCostAndUsage) with params: {'url_path': '/', 'query_string': '', 'method': 'POST', 'headers': {'X-Amz-Target': 'query_string', 'Content-Type': 'application/x-amz-json-1.1', 'User-Agent': 'aws-cli/2.9.21 Python/3.9.11 Darwin/21.6.0 exe/x86_64 prompt/off command/ce.get-cost-and-usage'}, 'body': b'{"TimePeriod": {"Start": "2023-06-01", "End": "2023-07-01"}, "Granularity": "MONTHLY", "Metrics": ["BlendedCost", "UnblendedCost"], "GroupBy": [{"Type": "TAG", "Key": "Customer"}]}', 'url': 'https://ce.us-east-1.amazonaws.com/', 'context': {'client_region': 'aws-global', 'client_config': <botocore.config.Config object at 0x7fab5825a730>, 'has_streaming_input': False, 'auth_type': 'v4', 'signing': {'region': 'us-east-1', 'signing_name': 'ce'}}}
このログから必要なパラメータを作成していきます。
引数番号 | 実際の値 | 引数の値の特定方法 |
---|---|---|
サービス | ce | ログ中の url のサブドメインを参照 |
リージョン | us-east-1 | ログ中の url のサブドメインを参照 |
アクション | AWSInsightsIndexService.GetCostAndUsage | ログ中の headers の X-Amz-Target を参照 |
パラメータ | {} | ログ中の query_string を参照 (多分) |
メソッド | POST | ログ中の method を参照 |
ペイロード | {“TimePeriod”: {“Start”: 2023-06-01, “End”: 2023-07-01}, “Granularity”: “MONTHLY”, “Filter”: {“Tags”: {“Key”: “Customer”, “Values”: [“TAG-VALUE”]}}, “Metrics”: [“BlendedCost”, “UnblendedCost”], “GroupBy”: [{“Type”: “TAG”, “Key”: “Customer”}]} | ログ中の body を参照 TAG-VALUEには実際のタグの値を入れてください |
ヘッダ | {“Content-Type”: “application/x-amz-json-1.1”} | ログ中の headers の Content-Type を参照 |
パス | / | ログ中の url_path を参照 (多分) |
※この表は このサイト を参考に作成しております。
パラメータが確認できたので、あとはこれをGASで使えるように形式を整えます。整えたら、テスト関数の該当箇所と入れ替えます。
最終的なコード
function test(){
const awsAccessKey = PropertiesService.getScriptProperties().getProperty("AWS_ACCESS_KEY");
const awsSecretKey = PropertiesService.getScriptProperties().getProperty("AWS_SECRET_KEY");
AWS.init(awsAccessKey, awsSecretKey)
const res = AWS.request(
'ce',
'us-east-1',
'AWSInsightsIndexService.GetCostAndUsage',
{},
'POST',
{"TimePeriod": {"Start": costStart, "End": costEnd}, "Granularity": "MONTHLY", "Filter": {"Tags": {"Key": "Customer", "Values": ["TAG-VALUE"]}}, "Metrics": ["BlendedCost", "UnblendedCost"], "GroupBy": [{"Type": "TAG", "Key": "Customer"}]},
{"Content-Type": "application/x-amz-json-1.1"},
'/'
)
const code = res.getResponseCode()
const text = res.getContentText();
Logger.log(code)
Logger.log(text)
}
これで特定のタグが付いたリソースの先月分の金額が取得できました。
8. 取得したデータの取り出し
今度は取得したデータを取り出せるようにしていきます。参考にしたサイトではXMLで取得されると書かれていましたが、私の環境ではJSONで取得
されました。私はここで30分ハマったので気をつけてください。
// resにレスポンスデータが入っている
const code = res.getResponseCode();
const text = res.getContentText();
// JSON
const data = JSON.parse(text);
// BlendedCostの値を取得する
monthCost = Math.floor(data.ResultsByTime[0].Groups[0].Metrics.BlendedCost.Amount * 100) / 100;
Logger.log(monthCost)
return monthCost
これで金額を取り出すことができました。
9. スプレッドシートへの書き込み
あとはスプレッドシートへ書き込みするだけです。
これは他サイトにもたくさん情報がでているのでそちらを見てください
ハマったところ
一番ハマったのが、他の記事ではXMLで取得されていると紹介されていましたが、実際はJSONで取得されているところでした。
ここに行き着くまでにわりと時間がかかりました。
const data = JSON.parse(text);
最後に
コスト情報は経理なども確認したい内容だと思うので、GASでコストデータを取得するというのはわりとありな気がしています。
実際はAWSマネジメントコンソールの権限を経理に付与するか、このような形でGoogleスプレッドシートで過去の金額含めて確認できるようにするか、Slack Botでピンポイントの値が確認できるようにするか、といくか方法が考えられます。
人や状況に合わせて最適なものを選択していきましょう。
GASでAWS Cost Explorerから料金を取得する