【小ネタ】マルチアカウント環境のCost Optimization Hub(EC2サイズ推奨項目)を見やすく加工

【小ネタ】マルチアカウント環境のCost Optimization Hub(EC2サイズ推奨項目)を見やすく加工

Clock Icon2025.03.31

Cost Optimization Hub はコスト最適化に関する推奨事項を一元管理するためのサービスです。

https://dev.classmethod.jp/articles/introduction-2024-cost-optimization-hub/

AWS Organizationsを使ったマルチアカウント環境では、 委任管理者アカウントから推奨事項の一覧を集約して確認できます。

しかしマネコンの情報(およびダウンロードできるCSV) には AWSアカウントとリソースの "ID" しかありません。 AWSアカウント名やタグ情報が無いため、少々不便です。

そこで、今回はCost Optimization Hubの EC2ライトサイジングの推奨事項に絞って、 追加でAWSアカウント名やタグ情報を付与、結合してみます。 泥臭く整理してヒューマンフレンドリーにしてみました。

sc_2025-03-31_09-48-44_29576
やりたいこと

情報を持ってくる

以下3つのCSVをローカルに持ってきます。

  • Cost Optimization Hub 推奨事項
  • AWSアカウント情報
  • EC2インスタンス情報

なおデータ加工のツールとして DuckDB を使います。

Cost Optimization Hub 推奨事項

以下アップデートから、 データエクスポート設定にて CSVを定期的に出力できるようになっています。

https://dev.classmethod.jp/articles/cost-optimization-hub-export/

出力設定をしたうえで、得られたCSVをローカルに置いておきます。

# S3からダウンロード(coh.csv.gz)
aws s3 cp s3://amzn-s3-demo-bucket/example-export/data/date=2025-03-27/..../example-export-00001.csv.gz ./coh.csv.gz

# gzファイルを展開(coh.csv)
gzip -d coh.csv.gz

本CSVの列情報については以下ドキュメントを参照ください。

https://docs.aws.amazon.com/ja_jp/cur/latest/userguide/table-dictionary-cor-columns.html

AWSアカウント情報

Organizationsの管理アカウントにて organizations list-accounts APIを実行して取得します。

AWSアカウント情報をCSVとして保存(accounts.csv)
aws organizations list-accounts --output json \
  --query "Accounts[].{account_name:Name, account_id:Id}" \
| duckdb -csv -c "select * from read_json('/dev/stdin') order by account_name" > accounts.csv

EC2インスタンス情報

少々泥臭いですが、AWSアカウントごとに ec2 describe-instances を実行していきます。 タグ情報としては「Nameタグ」と 「コスト配分タグ(今回は CmBillingGroup)」 を含めるようにしました。

AWSアカウント1個分のEC2インスタンス情報をCSVとして保存
account_name="EXAMPLE"

# EC2インスタンス一覧を取得してCSV化
output=$(
  aws ec2 describe-instances --output json \
  --query 'Reservations[*].Instances[].{
    instance_name: Tags[?Key==`Name`] | [0].Value,
    instance_cmbillinggroup: Tags[?Key==`CmBillingGroup`] | [0].Value,
    instance_id: InstanceId
    }' \
  | duckdb -csv -c "select * from read_json('/dev/stdin')"
)

# EC2インスタンスが存在するもののみ、instances-per-account フォルダに保存
if [ -n "$output" ]; then
  mkdir -p instances-per-account
  echo "$output" > "instances-per-account/${account_name}.csv"
fi

最後にそれぞれのCSVをまとめて、 1つのCSVにしておきます。

1つのCSVにまとめる(instances.csv)
duckdb -csv -c "select * from read_csv('instances-per-account/*.csv')" > instances.csv

head instances.csv
# instance_name,instance_cmbillinggroup,instance_id
# app-test-bastion-01,NULL,i-0abc123def456789a
# app-dev-bastion-02,NULL,i-0def456789abcdef0
# web-prod-server01,WEB-PROD,i-a1b2c3d4
# web-prod-admin,WEB-PROD,i-e5f6g7h8
# shop-prod-web01,SHOP-PROD,i-12345abc
# shop-dev-web01,SHOP-DEV,i-67890def
# shop-prod-web03,SHOP-PROD,i-abcd1234
# shop-prod-web05,SHOP-PROD,i-5678efgh
# shop-prod-cms01,SHOP-PROD,i-ijkl9012

結合させる

  • coh.csv : Cost Optimization Hub 推奨事項
  • accounts.csv : AWSアカウント情報
  • instances.csv : EC2インスタンス情報

3つのCSVをローカルに持ってこれたので、 あとは結合するだけです。

以下コマンド(SQL)を実行しました。

Cost Optimization HubのEC2サイズ推奨項目を見やすく加工(coh_readable.csv)
duckdb_sql=$(cat <<EOF
SELECT
    a.account_name,
    a.account_id,
    i.instance_name,
    i.instance_id,
    i.instance_cmbillinggroup AS cmbillinggroup,
    c.current_resource_summary AS current_size,
    c.recommended_resource_summary AS recommended_size,
    round(c.estimated_monthly_savings_after_discount,1) AS monthly_savings
FROM
    'coh.csv' c
JOIN
    'accounts.csv' a ON c.account_id = a.account_id
LEFT JOIN
    'instances.csv' i ON regexp_replace(c.resource_arn, '^arn:aws:.+:\d+:instance/', '') = i.instance_id
WHERE
    c.current_resource_type = 'Ec2Instance'
    AND c.action_type = 'Rightsize'
ORDER BY
    monthly_savings DESC;
EOF
)

duckdb -csv -c "${duckdb_sql}" >| coh_readable.csv
SQL部分抜粋
SELECT
    a.account_name,
    a.account_id,
    i.instance_name,
    i.instance_id,
    i.instance_cmbillinggroup AS cmbillinggroup,
    c.current_resource_summary AS current_size,
    c.recommended_resource_summary AS recommended_size,
    round(c.estimated_monthly_savings_after_discount,1) AS monthly_savings
FROM
    'coh.csv' c
JOIN
    'accounts.csv' a ON c.account_id = a.account_id
LEFT JOIN
    'instances.csv' i ON regexp_replace(c.resource_arn, '^arn:aws:.+:\d+:instance/', '') = i.instance_id
WHERE
    c.current_resource_type = 'Ec2Instance'
    AND c.action_type = 'Rightsize'
ORDER BY
    monthly_savings DESC;

内容はこんな感じです。

head coh_readable.csv
# account_name,account_id,instance_name,instance_id,cmbillinggroup,current_size,recommended_size,monthly_savings
# Account-A,111111111111,server-app-01,i-0abc123def456789a,APP-PROD,m5.4xlarge,r6i.xlarge,685.2
# Account-A,111111111111,server-db-01,i-0bcd234efg567890b,DB-PROD,m5.2xlarge,t3.large,562.8
# Account-A,111111111111,server-app-09,i-0cde345fgh678901c,APP-PROD,r5.4xlarge,c6i.2xlarge,510.4
# Account-B,222222222222,server-app-08,i-0def456ghi789012d,NULL,m5.8xlarge,r6i.2xlarge,483.7
# Account-B,222222222222,server-service-01,i-0efg567hij890123e,SVC-PROD,r5.4xlarge,m6i.xlarge,442.3
# Account-B,222222222222,server-cache-01,i-0fgh678ijk901234f,NULL,c5.2xlarge,t3.medium,345.9
# Account-C,333333333333,server-file-02,i-0ghi789jkl012345g,STORAGE,m5.2xlarge,t3a.large,319.5
# Account-C,333333333333,server-file-01,i-0hij890klm123456h,STORAGE,r5.2xlarge,c6a.large,332.6
# Account-C,333333333333,bi-prod-tableau,i-0ijk901lmn234567i,BI-PROD,m5.12xlarge,r6i.4xlarge,315.8
account_name account_id instance_name instance_id cmbillinggroup current_size recommended_size monthly_savings
Account-A 111111111111 server-app-01 i-0abc123def456789a APP-PROD m5.4xlarge r6i.xlarge 685.2
Account-A 111111111111 server-db-01 i-0bcd234efg567890b DB-PROD m5.2xlarge t3.large 562.8
Account-A 111111111111 server-app-09 i-0cde345fgh678901c APP-PROD r5.4xlarge c6i.2xlarge 510.4
...略...

多少見やすくなったのでは無いでしょうか。 元ネタ(Cost Optimization Hub)のCSVから情報を結構削ぎ落としています。 足りないものがあれば適宜追加ください。

おわりに

マルチアカウント環境のCost Optimization Hub(EC2サイズ推奨項目)を見やすく加工してみました。 泥臭くローカルでこねこねしました。 マネコン上ではなかなか識別しづらい!と思ったときには役に立つかもしれません。

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

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.