pslaboが試したことの記録

はてなダイヤリーからはてなブログに引っ越してきました

この日記は現在実行中の減量記録を含む個人的なメモとして始めましたが、最近はコンピュータやガジェット、ハック、セキュリティネタのほうがメインになっております。

はてなダイヤリー時代はカテゴリ分けが適当だったのですが、これはそのうち直します。


AWS で使用料金明細をタブ区切りテキストで取得する。(利用明細のS3出力を有効化していない期間のデータサルベージ)

正確にいうと JSON でダウンロードしたものをタブ区切りテキストに変換する話ですけど、まあいいか。


AWS の利用料金明細は月別レポートや詳細な請求レポート等を S3 に自動保存できますから、そのように設定されているのが普通だと思います。これはこれで便利なのですが設定を有効にした日以降のデータしか手に入りません。

ちょいと事情があってAWS の過去の usage をデータ化して比較しようと思ったところ、手元のアカウントでは S3 出力を有効化したのが割と最近だったために過去の請求明細がデータで手に入らない。。。どうにか方法はないかと思って考えた挙句、管理コンソールの「請求書」画面の裏で叩いている、以下のURLから情報が取れそうなことが分かりました。


https://console.aws.amazon.com/billing/rest/v1.0/bill?year=YYYY&month=MM

このURLは、AWSのコンソールにログインした後ならブラウザで叩きホーダイです。(対象期間が多い方はブックマークレットを作ったほうが良いのでしょうけど、それを作っている間に手作業で保存できる可能性も高いかも。)

で、ダウンロードで、取得できるのは以下のような JSON 形式のデータです。

[
  {
    "linkedBillIdentifiers": [],
    "startDate": 1343779200000,
    "allocatedBills": null,
    "bills": [
      {
        "invoice": {
          "billingPeriodEndDate": null,
          "invoiceVersionId": null,
          "marketplaceId": null,
          "invoiceStatus": "Paid",
          "invoiceAmount": null,
          "invoiceType": null,
          "invoiceDueDate": null,
          "description": null,
          "retriable": false,
          "invoiceId": "12345678",
          "customerId": null,
          "billingPeriodStartDate": null,
          "invoiceSettleDate": 1346739288000,
          "accountId": null,
          "invoiceFxAmount": null,
          "invoiceCreationDate": 1346718207000,
          "invoiceGroupId": "12345678"
        },
        "billVersionId": "33584063",
        "billTypeCode": "Anniversary",
        "billId": "12345678",
        "marketplaceId": "xxxxxxxxxxxxx",
        "cutOffDate": 1440365059666,
        "payerAccountId": "123456789012",
        "totalFxCharge": null,
        "lineItems": [
          {
            "lineItemId": "123456789",
            "description": "$0.000 per GB - data transfer out under the monthly global free tier",
            "productName": "AWS Data Transfer",
            "productCode": "AWSDataTransfer",
            "chargePeriodStartDate": 1344579530000,
            "offeringId": "0",
            "usageAmount": "0.89573851",
            "usageUnitName": "GB",
            "instanceType": "Bandwidth",
            "chargePeriodEndDate": 1346457599000,
            "pricePerUnit": {
              "amount": 0,
              "baseCurrencyCode": "USD"
            },
            "chargeAmountBeforeTax": {
              "amount": 0,
              "baseCurrencyCode": "USD"
            },
            "blended": false,
            "riType": "None",
            "fxChargeAmountAfterTax": null,
            "pricingPlanId": "12345",
            "sellerOfRecordName": null,
            "lineItemType": "Usage",
            "clientProductCode": "AWSDataTransfer",
            "usageType": "APN1-DataTransfer-Out-Bytes",
            "operation": null,
            "region": "Asia Pacific (Tokyo) Region",
            "chargeAmountAfterTax": {
              "amount": 123.45,
              "baseCurrencyCode": "USD"
            },
            "fxChargeAmountBeforeTax": null
          },
          .
          .
          .
        "totalChargeAfterTax": {
          "amount": 123.45,
          "baseCurrencyCode": "USD"
        },
        "totalChargeBeforeTax": {
          "amount": 123.45,
          "baseCurrencyCode": "USD"
        },
        "estimated": false
      }
    ],
    "endDate": 1346457599000
  }
]

こういうデータは jq で処理するに限るので、例えば以下のような感じで処理します。私がほしい明細データは ..bills.lineItems[] の部分にありますから、この部分をまとめて抜く処理と、その中から部分的に抽出する処理に分けています。 (以下の例は jq のパラメータを読みやすくするために整形していますが、本来は改行や余計な空白ナシのワンライナーで記述します)

jq -r '{ ProductCode:.[].bills[].lineItems[] } | 
	"\(.ProductCode.productCode)\t
	\(.ProductCode.usageType)\t
	\(.ProductCode.operation)\t
	\(.ProductCode.description)\t
	\(.ProductCode.chargePeriodStartDate / 1000)\t
	\(.ProductCode.chargePeriodEndDate / 1000)\t
	\(.ProductCode.usageAmount)\t
	\(.ProductCode.pricePerUnit.baseCurrencyCode)\t
	\(.ProductCode.chargeAmountBeforeTax.amount)\t
	\(.ProductCode.chargeAmountAfterTax.amount)"'

こうすると、以下のような出力が得られます。タブ区切りなので、他のツールでの使いまわしが便利です。出力する項目の調整は jq 側で自由に弄れますから、必要に応じて適当に加減すればよい、と。

AWSDataTransfer APN1-DataTransfer-Out-Bytes     null    $0.000 per GB - data transfer out under the monthly global free tier    1344579530000   1346457599000 0.89573851      USD     0       0
 .
 .
 .


さて、ブラウザ経由でダウンロードした JSON をチマチマと TSV に変換するのは単なるコピペ仕事なので省略したい。よって以下のようなスクリプトにしてみました。FirefoxAWS コンソールにログインの後、セッションクッキーの情報を FirefoxLive http headers あたりで抜いてきて aws_console_cookie に指定すれば使えます。用いる cookie は "aws-creds=" を含むものです。対象期間はお好みでどうぞ。

#!/bin/bash

startdate=2011/01
enddate=2015/07

aws_console_cookie='Cookie: xxxxxx'

do_main() {

        startyear=${startdate%%/*}
        startmonth=${startdate##*/}

        endyear=${enddate%%/*}
        endmonth=${enddate##*/}

        startdatenum="${startyear}${startmonth}"
        enddatenum="${endyear}${endmonth}"

        for y in $( seq $startyear $endyear ); do
                for m in $( seq 1 12 ); do
                        if [ $m -ge 1 ] && [ $m -le 9 ]; then
                                m="0${m}"
                        fi

                        targetdatenum="${y}${m}"

                        if [ $startdatenum -le $targetdatenum ] && [ $targetdatenum -le $enddatenum ]; then
                                wgetcmd awsreport $y $m
                        fi
                done
        done
}


wgetcmd() {
        PREFIX=$1
        YEAR=$2
        MONTH=$3

        wget --header "$aws_console_cookie" \
        --output-document ${PREFIX}.${YEAR}.${MONTH}.json \
        "https://console.aws.amazon.com/billing/rest/v1.0/bill?year=${YEAR}&month=${MONTH}"

        awsreport2tsv ${PREFIX}.${YEAR}.${MONTH}.json
}

awsreport2tsv() {
                filename=$1
                if [ -e $filename ]; then
                        echo $filename ${filename%%.json}
                        filename_tsv=${filename%%.json}.tsv

                        cat $filename | jq -r '{ ProductCode:.[].bills[].lineItems[] } | "\(.ProductCode.productCode)\t\(.ProductCode.usageType)\t\(.ProductCode.operation)\t\(.ProductCode.RateId)\t\(.ProductCode.description)\t\(.ProductCode.chargePeriodStartDate)\t\(.ProductCode.chargePeriodEndDate)\t\(.ProductCode.usageAmount)\t\(.ProductCode.pricePerUnit.baseCurrencyCode)\t\(.ProductCode.chargeAmountBeforeTax.amount)\t\(.ProductCode.Credits)\t\(.ProductCode.TaxAmount)\t\(.ProductCode.TotalCost)\t\(.ProductCode.chargeAmountAfterTax.amount)"' > $filename_tsv
                fi
}

do_main "$@"