Cost Explorer API で特定のコストカテゴリの料金を取得する

## モチベーション
AWS Cost Explorer は 日次や月次レベルで AWS のコストと使用量の可視化、把握、管理を行うことができるサービスです。コンソールから利用できますが、API が用意されているため、プログラムから定期的に目的のコストを状況を抽出して通知するなどといった自動化が可能です。

https://aws.amazon.com/jp/aws-cost-management/aws-cost-explorer/

CloudWatch の Billing メトリクスでも各アカウントの概算費用は取得できますが、アカウント ID の情報しか持っていません。そのためシステム単位等の特定のアカウント郡のコストだけを抽出することは CloudWatch メトリクスの機能だけでは実現できません。

AWS Billing and Cost Management にはコストカテゴリという機能があり、特定のルールでコストのグループを作成できます。Cost Explorer ではコストカテゴリでフィルターした料金を抽出できるため、CloudWatch メトリクスより簡単かつ、柔軟にコストの分析が可能です。

https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/manage-cost-categories.html

## コストカテゴリの作成
AWS Billing Management コンソールからコストカテゴリを作成できます。例えば以下のコストカテゴリおよびルールはアカウント名が `SampleSystem-` で始まるアカウントをグループ化しています。現状、AWS Organizations の OU 単位等でのグループ化はサポートされていないため、ここではアカウント名をベースにカテゴリを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/89ee03bd-6464-50b8-2014-8bb20dfafca1.png)

コストカテゴリを作成すると、指定したルールによって分類されたコストを確認することができます。以下の例では全体 AWS コストのうち、51% がコストカテゴリでグループ化したアカウントのコストであることがわかります。(金額等はマスキングしています。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/09b782e9-a80e-01b2-a823-2642ceac95ae.png)

コストカテゴリ作成後、Cost Explorer でフィルター条件として指定できるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/3ef2f92a-b65c-571c-2a3c-6916936f49b8.png)


## Cost Explorer API 経由で取得する
例えば AWS Lambda から boto3 を使用して月の合計料金を取得する場合、最低限の処理は以下のようになります。

```lambda_function.py
import boto3

def lambda_handler(event, context):
    ce = boto3.client('ce')
    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': '2022-02-01',
            'End' :  '2022-02-28',
        },
        Granularity='MONTHLY',
        Metrics= [
            'NetUnblendedCost'
        ],
        Filter={
            'CostCategories': {
                'Key': 'Sample System Accounts',
                'Values': [
                    'Sample System Accounts',
                ]
            }
        }
    )
    print(response['ResultsByTime'][0]['Total'])
```


https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ce.html#CostExplorer.Client.get_cost_and_usage

結果例

```
{'NetUnblendedCost': {'Amount': '42969.6600696831', 'Unit': 'USD'}}
```

`GroupBy` を使用するとコストをグループ化して取得することが可能です。Cost Explorer コンソールではグループ化条件は 1 つまでしか適用できませんが、Cost Explorer API では最大 2 つのグループ化条件を指定することができます。以下の例ではコストカテゴリでアカウントを Filter し、GroupBy で Linked Account ごとにサービス別のコストを取得しています。

```lambda_function.py
import boto3

def lambda_handler(event, context):
    ce = boto3.client('ce')
    response = ce.get_cost_and_usage(
        TimePeriod={
            'Start': '2022-02-01',
            'End' :  '2022-03-01',
        },
        Granularity='MONTHLY',
        Metrics= [
            'NetUnblendedCost'
        ],
        Filter={
            'CostCategories': {
                'Key': 'Sample System Accounts',
                'Values': [
                    'Sample System Accounts',
                ]
            }
        },
        GroupBy=[
            {
                'Type': 'DIMENSION',
                'Key': 'LINKED_ACCOUNT'
            },
            {
                'Type': 'DIMENSION',
                'Key': 'SERVICE'
            }
       ]
    )
    print(response['ResultsByTime'][0]['Groups'])
```

結果例 (見やすくするため改行をいれています)

```
[
  {'Keys': ['123456789012', 'AWS Key Management Service'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.1175595208', 'Unit': 'USD'}}}, 
  {'Keys': ['123456789012', 'AWS Secrets Manager'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.095238096', 'Unit': 'USD'}}}, 
  {'Keys': ['123456789012', 'AWS Security Hub'], 'Metrics': {'NetUnblendedCost': {'Amount': '1.904', 'Unit': 'USD'}}}, 
  {'Keys': ['123456789012', 'EC2 - Other'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.4245898221', 'Unit': 'USD'}}}, 
  {'Keys': ['123456789012', 'Amazon Elastic Compute Cloud - Compute'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.0002534851', 'Unit': 'USD'}}}, 
  {'Keys': ['111111111111', 'Amazon Elastic Container Service for Kubernetes'], 'Metrics': {'NetUnblendedCost': {'Amount': '25.302975656', 'Unit': 'USD'}}}, 
  {'Keys': ['111111111111', 'Amazon Elastic Load Balancing'], 'Metrics': {'NetUnblendedCost': {'Amount': '3.888', 'Unit': 'USD'}}}, 
  {'Keys': ['111111111111', 'Amazon Kinesis'], 'Metrics': {'NetUnblendedCost': {'Amount': '1.56', 'Unit': 'USD'}}}, 
  {'Keys': ['111111111111', 'Amazon Simple Storage Service'], 'Metrics': {'NetUnblendedCost': {'Amount': '0.0000000128', 'Unit': 'USD'}}}, 
  {'Keys': ['222222222222', 'Amazon Virtual Private Cloud'], 'Metrics': {'NetUnblendedCost': {'Amount': '5.6000999356', 'Unit': 'USD'}}}, 
]
```

あとはこれらの結果を加工して Slack 通知するなど、煮るなり焼くなり好きにできますね。


## 注意点
Cost Explorer API は 1 リクエストあたり、$0.01 の料金が発生します。呼び出し回数が増えると料金も高額になってきますのでご注意ください。

https://aws.amazon.com/jp/aws-cost-management/aws-cost-explorer/pricing/

Cost Explorer API を使用してアプリケーションを作成する場合は、キャッシュレイヤーを含むように設計することが推奨されています。

https://docs.aws.amazon.com/cost-management/latest/userguide/ce-api-best-practices.html

> **Best practices for optimizing your Cost Explorer API costs**
> If you're creating an application using the Cost Explorer API, we recommend architecting the application so that it has a caching layer. 

以上です。
参考になれば幸いです。