AWS Price List Query API で AWS オンデマンド料金の表を作成する

# はじめに

前払いコストの投資による費用対効果を纏めるために現在のオンデマンド料金の表が欲しくなったため、`AWS Price List Query API`を利用しAWS CLIで特定サービスの料金表を作成しました。

https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/price-changes.html

## Require

- AWS CLI
- jq

## 指定したサービスの情報取得

抽出したかったサービスは限られていたのでまずは必要なサービスのメタデータを確認しました

```bash
#!/bin/bash

# --------------------------------------------------------------------------------
# 指定したサービスコードのメタデータのリストを取得
# --------------------------------------------------------------------------------

SERVICE_CODES=(\
    "AmazonEC2"\
    "AmazonElastiCache"\
    "AmazonES"\
    "AmazonRDS"\
    "AmazonRedshift"\
)

for service_code in "${SERVICE_CODES[@]}"
do
    aws pricing describe-services \
        --service-code ${service_code} \
        --region us-east-1 \
    | jq -r '.Services[] | {
        ServiceCode: .ServiceCode, 
        AttributeNames: .AttributeNames[] 
    } | [
        .ServiceCode, 
        .AttributeNames
    ] | @tsv'
done
```

## 指定したサービスの選択肢の取得

`get-products`コマンドで料金情報を取得できるのですが何もフィルタしないと情報量が多すぎるため、特定の属性でフィルターをするための選択肢を確認しました

```bash
# リージョンの選択肢を取得
aws pricing get-attribute-values \
    --region us-east-1 \
    --service-code AmazonRDS \
    --attribute-name location \
    --query 'AttributeValues[*].[Value]' \
    --output text
```

```bash
# データベースエンジンの選択肢を取得
aws pricing get-attribute-values \
    --region us-east-1 \
    --service-code AmazonRDS \
    --attribute-name databaseEngine \
    --query 'AttributeValues[*].[Value]' \
    --output text
```

## 料金情報の取得

データベースエンジンを配列定義し、データベースエンジン毎に料金情報を取得し結果を標準出力します。
TSV出力された結果をスプレッドシートなどに貼り付けて利用します。

### EC2

```bash
#!/bin/bash

# --------------------------------------------------------------------------------
# EC2の指定したOSの料金情報を取得
# --------------------------------------------------------------------------------

SERVICE_CODE=AmazonEC2

OPERATING_SYSTEMS=(\
    "Linux"\
    "Windows"\
)

echo -e "key\tserviceCode\
\tproductFamily\
\tinstanceFamily\
\tinstanceType\
\tlocationType\
\tlocation\
\toperation\
\tstorage\
\toperatingSystem\
\ttenancy\
\tusagetype\
\tlicenseModel\
\tprocessorArchitecture\
\tmarketoption\
\tavailabilityzone\
\tsku\
\tpricePerUnit\
\tpricePerUnitYear\
\tunit\
\tbeginRange\
\tendRange\
\trateCode"

for operation_system in "${OPERATING_SYSTEMS[@]}"
do
    aws pricing get-products \
        --region us-east-1 \
        --service-code ${SERVICE_CODE} \
        --filters \
            "Type=TERM_MATCH,Field=location,Value=Asia Pacific (Tokyo)" \
            "Type=TERM_MATCH,Field=OfferingClass,Value=standard" \
            "Type=TERM_MATCH,Field=operatingSystem,Value=\"${operation_system}\"" \
            "Type=TERM_MATCH,Field=tenancy,Value=Shared" \
        | jq -r '.PriceList[] | fromjson | { product: .product, serviceCode: .serviceCode, termsOnDemand: .terms.OnDemand | to_entries | .[].value | to_entries | .[] | select(.key == "priceDimensions") | .value | to_entries | .[].value }
            | [
                .product.attributes.operatingSystem + "-" + .product.attributes.instanceType
                , .serviceCode
                , .product.productFamily
                , .product.attributes.instanceFamily
                , .product.attributes.instanceType
                , .product.attributes.locationType
                , .product.attributes.location
                , .product.attributes.operation
                , .product.attributes.storage
                , .product.attributes.operatingSystem
                , .product.attributes.tenancy
                , .product.attributes.usagetype
                , .product.attributes.licenseModel
                , .product.attributes.processorArchitecture
                , .product.attributes.marketoption
                , .product.attributes.availabilityzone
                , .product.sku
                , .termsOnDemand.pricePerUnit.USD
                , (.termsOnDemand.pricePerUnit.USD | tonumber) * 24 * 365
                , .termsOnDemand.unit
                , .termsOnDemand.beginRange
                , .termsOnDemand.endRange
                , .termsOnDemand.rateCode
            ] | @tsv'
done
```


### RDS

```bash
#!/bin/bash

# --------------------------------------------------------------------------------
# RDSの指定したデータベースエンジンの料金情報を取得
# --------------------------------------------------------------------------------

SERVICE_CODE=AmazonRDS

DATABASE_ENGINES=(\
    "Aurora MySQL"\
    "Aurora PostgreSQL"\
    "MySQL"\
    "PostgreSQL"\
)

TARGET_REGION="Asia Pacific (Tokyo)"

echo -e "key\
\tserviceCode\
\tproductFamily\
\tdatabaseEngine\
\tinstanceTypeFamily\
\tinstanceType\
\tlocationType\
\tlocation\
\toperation\
\tgroup\
\tsku\
\tpricePerUnit\
\tpricePerUnitYear\
\tunit\
\tbeginRange\
\tendRange\
\trateCode"
    
for database_engine in "${DATABASE_ENGINES[@]}"
do
    aws pricing get-products \
        --region us-east-1 \
        --service-code ${SERVICE_CODE} \
        --filters \
            "Type=TERM_MATCH,Field=databaseEngine,Value=\"${database_engine}\"" \
            "Type=TERM_MATCH,Field=location,Value=\"${TARGET_REGION}\"" \
        | jq -r '.PriceList[] | fromjson | { product: .product, serviceCode: .serviceCode, termsOnDemand: .terms.OnDemand | to_entries | .[].value | to_entries | .[] | select(.key == "priceDimensions") | .value | to_entries | .[].value } |
            [
                .product.attributes.databaseEngine + "-" + .product.attributes.instanceType
                , .serviceCode
                , .product.productFamily
                , .product.attributes.databaseEngine
                , .product.attributes.instanceTypeFamily
                , .product.attributes.instanceType
                , .product.attributes.locationType
                , .product.attributes.location
                , .product.attributes.operation
                , .product.attributes.group
                , .product.sku
                , .termsOnDemand.pricePerUnit.USD
                , (.termsOnDemand.pricePerUnit.USD | tonumber) * 24 * 365
                , .termsOnDemand.unit
                , .termsOnDemand.beginRange
                , .termsOnDemand.endRange
                , .termsOnDemand.rateCode
            ] | @tsv'
done
```