LoginSignup
32
33

More than 3 years have passed since last update.

【Go】AWSのサービス毎の利用料金を取得する

Posted at

はじめに

これまで、「AWSの利用料金をChatworkに通知してくれるバッチをGoで作った」や「Goでslackに通知するバッチを作った【新Webhook】」ではAWSの合計利用料金を通知してきましたが、サービス毎の明細が欲しかったので、サービス毎の利用料金もいっしょに通知してみることにしました!

通知したいこと

  • 昨日の合計利用料金
  • 昨日のサービス毎の利用料金

利用料金の取得

Lambda関数の作成

定期的にSlackやChatworkに通知することを想定して、Lambda関数を作成していきます。

IAMポリシー

API「GetCostAndUsage」を実行できる権限を付与します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ce:GetCostAndUsage",
            "Resource": "*"
        }
    ]
}

このポリシーをアタッチしたIAMロールを作成し、Lambdaに紐づけてください。

ソースコード

本コードはサービス毎の利用料金の取得方法にフォーカスしています。合計料金の取得方法は、こちらを参考にしてください。

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/aws/aws-lambda-go/lambda"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/costexplorer"
    "github.com/aws/aws-sdk-go/service/costexplorer/costexploreriface"
)

// コストの取得
func GetCost(svc costexploreriface.CostExplorerAPI) (result *costexplorer.GetCostAndUsageOutput) {

    // Granularity
    granularity := aws.String("DAILY")

    // Metrics
    metric := "UnblendedCost"
    metrics := []*string{&metric}

    // TimePeriod
    // 現在時刻の取得
    jst, _ := time.LoadLocation("Asia/Tokyo")
    now := time.Now().UTC().In(jst)
    dayBefore := now.AddDate(0, 0, -1)

    nowDate := now.Format("2006-01-02")
    dateBefore := dayBefore.Format("2006-01-02")

    // 昨日から今日まで
    timePeriod := costexplorer.DateInterval{
        Start: aws.String(dateBefore),
        End:   aws.String(nowDate),
    }

    // GroupBy
    group := costexplorer.GroupDefinition{
        Key:  aws.String("SERVICE"),
        Type: aws.String("DIMENSION"),
    }
    groups := []*costexplorer.GroupDefinition{&group}

    // Inputの作成
    input := costexplorer.GetCostAndUsageInput{}
    input.Granularity = granularity
    input.Metrics = metrics
    input.TimePeriod = &timePeriod
    input.GroupBy = groups

    // 処理実行
    result, err := svc.GetCostAndUsage(&input)
    if err != nil {
        log.Println(err.Error())
    }

    return result
}

/**************************
    処理実行
**************************/
func run() error {
    log.Println("コスト取得バッチ 開始")
    log.Println("セッション作成")
    svc := costexplorer.New(session.Must(session.NewSession()))

    log.Println("コスト取得 実行")
    cost := GetCost(svc)
    log.Println("コスト取得 完了")

    fmt.Println(cost)

    log.Println("コスト取得バッチ 完了")
    return nil
}

/**************************
    メイン
**************************/
func main() {
    lambda.Start(run)
}

実行結果

Lambda関数の実行ログから一部抜粋しています。

2020/05/06 03:59:54 コスト取得バッチ 開始
2020/05/06 03:59:54 セッション作成
2020/05/06 03:59:54 コスト取得 実行
2020/05/06 03:59:55 コスト取得 完了
{
  GroupDefinitions: [{
      Key: "SERVICE",
      Type: "DIMENSION"
    }],
  ResultsByTime: [{
      Estimated: true,
      Groups: [
        {
          Keys: ["AWS Cost Explorer"],
          Metrics: {
            UnblendedCost: {
              Amount: "0.05",
              Unit: "USD"
            }
          }
        },
        {
          Keys: ["AWS Lambda"],
          Metrics: {
            UnblendedCost: {
              Amount: "0.0000019028",
              Unit: "USD"
            }
          }
        },
      ],
      TimePeriod: {
        End: "2020-05-06",
        Start: "2020-05-05"
      },
      Total: {

      }
    }]
}
2020/05/06 03:59:55 コスト取得バッチ 完了

解説

合計利用料金を取得する際に利用したAPI「GetCostAndUsage」のInput要素は以下の3つでした。

  • Granularity
  • Metrics
  • TimePeriod

今回、サービス毎の料金を取得するため、以下の要素を追加します。

  • GroupBy

GroupByは指定した条件に沿ってグループ化することができます。条件を指定するために、KeyTypeを設定します。
Keyは何を基準に判断するのか、Typeはどう分けるのかということを意味します。今回はサービス毎の利用料金を知りたいので下記のようにしました。

Key:  aws.String("SERVICE"),
Type: aws.String("DIMENSION"),

よって、SERVICE(サービス)のDIMENTION(種類)でグループ化するという意味になります。

その他サービス毎以外にも、タグ毎など様々な条件のグループでコストを取得することができます。
参考:AWS SDK for Go API Reference

注意点

GroupByの要素を追加することで、Totalの値が取得できなくなります。(実行結果参照)
なので、今回のように合計料金とサービス毎の両方のコストを取得したい場合は、2度リクエスト投げる必要があります。

ちなみに、AWSのサポートに問い合わせてみましたが、現状は1度のリクエストで両方のコストが取得できる方法は確認できておりませんとの返事が返ってきました。(2020/03/19時点)

おわりに

合計料金とサービス毎の料金を1度に取得できないので、2回APIをたたく必要がある、つまり、倍のコストがかかってしまうというところがありますので…利用料金とその明細が知りたい!!ってときには、サービス毎の利用料金を自分で合計してしまいましょう。

おまけ

ちなみに、自分で合計してみました。

func SumCost(cost *costexplorer.GetCostAndUsageOutput) (total string){

    sum := 0.0
    for _, data := range cost.ResultsByTime[0].Groups {
        amount, _ := strconv.ParseFloat(*data.Metrics["UnblendedCost"].Amount, 64)
        sum = sum + amount
    }
    total = fmt.Sprintf("%.10f", sum)
    return total
}

取得したコストはString型なので、float64型にしてから足していきます。
これでサービス毎の利用料金を取得するだけで、合計料金も合わせて通知することができそうです!

32
33
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
32
33