Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2866

AWSのコスト配分レポートをいじり倒し、料金を超絶見える化する

$
0
0

はじめに

AWS料金、どこにどれだけかかってるか、とても分かりづらくないですか?
CostExplorerを使えばそれなりに可視化できますが、例えばEC2。
EC2インスタンス料金は見えますが、そこにアタッチされたEBSやそのEBSスナップショット、そのEC2で発生したデータ通信料金などは、すべてバラバラでしか確認できません。
これらすべて、発生元のEC2に紐づけて見たいと思ったことはないでしょうか。

Auroraもインスタンス料金は見えますが、ストレージ、I/O、バックアップなど、そのインスタンスで発生している料金は紐付けて確認したいですよね。

会社の取締役から「どのサーバでいくら、どのシステムでいくら、かかってるんだ?」
と聞かれ、ぐぬぬとなってしまったのをきっかけに徹底的に見える化することにしました。

ゴール

先に完成版をお見せしたいと思います。
今回はコストを抑えたかったため、DBは使わず単純CSVファイルとAWSのBIサービスであるQuickSightを使いました。
(CSVファイルをQuickSightにロードしています)

例えば、ある月のリソース単位の料金を円グラフにするとこんな感じ
AWS料金可視化01.png

一番お高いリソース(水色の¥262,753)はこれ、Auroraなんですが、グラフをクリックしてドリルダウンすると以下のようになります。
AWS料金可視化02.png

お高いAurora料金の内訳が見れます。
例えばこのAuroraは

  • インスタンス料金で¥114,857
  • I/O料金で¥85,069
  • ストレージ料金で¥61,574
  • データ転送Outで¥1,253

かかっていることが分かります。I/Oたけぇ・・・:sweat_smile:
Cost Explorerでは、Aurora全体でI/O料金はいくら、というのは確認できますが、このようにインスタンス単位では確認できません。

QuickSightはBIツールですから、データさえあれば紹介したようなグラフ以外にも様々なグラフや表を自在に作成できます。
単月コストだけでなく、長期間のトレンドも詳細に把握できるようになりますし、削減のためのアクションプラン策定にも効果抜群です。

今回はQuickSightにロードするためのCSVデータを、AWSから提供されるコスト配分レポートをもとに作成する方法を、サンプルコード交えてご紹介したいと思います。
※コーディングの拙さはご容赦ください:laughing:

本記事に書かないこと

コスト配分タグ、コスト配分レポートについては公式ドキュメントをご確認ください。

また、QuickSightの使い方も今回は触れません。

事前準備

まずリソースへのタグ付けが必要です。今回は以下のタグを付与することとします。

タグ名付与対象リソース
Nameリソース名Nameタグがリソース名となるようなサービスは全て。
とりあえずEC2だけでもOK
systemシステム名課金対象リソースは基本全部
environment本番ならproduction、ステージングならstaging、テストならtestなど課金対象リソースは基本全部

全部付けるのが面倒ならとりあえず課金額が多めなリソースだけでもいいです。

データの準備

コスト配分レポートを分析するにあたり、必要なデータを用意します。
具体的には以下の2つです。

  1. EC2用マッピングデータ・・・EC2、EBS、EBSスナップショット等のマッピングデータ
  2. RDS用マッピングデータ・・・RDS、クラスターリソースID、スナップショット等のマッピングデータ

EC2用マッピングデータの取得

1は、EC2、EBS、EBSスナップショットの紐付け状態をCSV形式で取得して作成します。これは毎日作成し、日毎に保管しておきます(日毎に変動する可能性があるため)
例えば以下のようなCSVデータになります。

yyyy-mm-dd_ec2_mapping_sample.csv
AWSAccountID,AccountDesc,System,Environment,Name,InstanceID,VolumeID,SnapshotID123456789012,○○用アカウント,A-system,production,web-server01,i-01xxxxxxxxx,vol-01xxxxxxxxxx,snap-0101xxxx123456789012,○○用アカウント,A-system,production,web-server01,i-01xxxxxxxxx,vol-01xxxxxxxxxx,snap-0102xxxx123456789012,○○用アカウント,A-system,production,web-server01,i-01xxxxxxxxx,vol-01xxxxxxxxxx,snap-0103xxxx123456789012,○○用アカウント,A-system,production,web-server01,i-01xxxxxxxxx,vol-01xxxxxxxxxx,snap-0104xxxx123456789012,○○用アカウント,B-system,test,web-server02,i-02xxxxxxxxxx,02xxxxxxxxxx,123456789012,○○用アカウント,C-system,production,web-server03,i-03xxxxxxxxx,vol-03xxxxxxxxxx,snap-0301xxxx123456789012,○○用アカウント,C-system,production,web-server04,i-04xxxxxxxxx,vol-04xxxxxxxxxx,snap-0401xxxx123456789012,○○用アカウント,C-system,production,web-server04,i-04xxxxxxxxx,vol-04xxxxxxxxxx,snap-0402xxxx

各カラムの内容は次の通りです。

カラム説明
AWS Account IDAWSのアカウントID。複数のアカウントをお持ちの場合はあったほうがいいでしょう
Account DescアカウントIDの説明。無くてもいいです
System事前準備でリソースに付与したタグ
Environment事前準備でリソースに付与したタグ
Name事前準備でEC2等に付与したNameタグ
Instance IDEC2インスタンスID
Volume ID上記EC2にアタッチされているEBSボリュームID
Snapshot ID上記EBSのスナップショットID

このCSVを作成するためのサンプルコードは次の通りです。今回はjqを使ってみたかったのでBashで書きましたが言語は何でもいいです。
メインとなる紐付けのところだけ抜粋します。
AWSCLIと、JSONパースのためjqを使います。

create_ec2_mapping_sample.sh
#!/bin/bash# まず必要な情報をAWSCLIで取得
aws ec2 describe-volumes >${TMPFILE1}
aws ec2 describe-instances >${TMPFILE2}
aws ec2 describe-snapshots --filters"Name=owner-id,Values=<AWSアカウントID>">${TMPFILE3}#AWSアカウントIDでフィルタしないと公開されているスナップショットすべて取得してしまいます# EBSボリューム毎にループ(EBSを基準に対応するEC2とスナップショットを特定する)for volumeId in$(jq -r'.Volumes[] | .VolumeId'${TMPFILE1});do# アタッチされているEC2インスタンスのIDを取得instanceId=$(jq -r'.Volumes[] | select(.VolumeId == "'${volumeId}'") | .Attachments[].InstanceId'${TMPFILE1})# EC2インスタンスのタグを取得nameTag=$(jq -r'.Reservations[].Instances[] | select(.InstanceId == "'${instanceId}'") | .Tags[] | select(.Key == "Name") | .Value'${TMPFILE2})systemTag=$(jq -r'.Reservations[].Instances[] | select(.InstanceId == "'${instanceId}'") | .Tags[] | select(.Key == "system") | .Value'${TMPFILE2})envTag=$(jq -r'.Reservations[].Instances[] | select(.InstanceId == "'${instanceId}'") | .Tags[] | select(.Key == "environment") | .Value'${TMPFILE2})# EBSのスナップショット一覧を取得snapshotIds=$(jq -r'.Snapshots[] | select(.VolumeId == "'${volumeId}'") | .SnapshotId'${TMPFILE3})# スナップショットが無かったらそのまま空文字を出力if[[-z${snapshotIds}]];then
    snapshotId=""echo"${account},${accountDesc},${systemTag},${envTag},${nameTag},${instanceId},${volumeId},${snapshotId}"# スナップショットがあったら1スナップショット毎に1行出力else
    for snapshotId in${snapshotIds};do
      echo"${account},${accountDesc},${systemTag},${envTag},${nameTag},${instanceId},${volumeId},${snapshotId}"done
  fi
done

RDS用マッピングデータの取得

2は、RDS、クラスター、クラスターのリソースID、スナップショットの紐付け状態をCSV形式で取得して作成します。これも毎日作成し、日毎に保管しておきます(日毎に変動する可能性があるため)
例えば以下のようなCSVデータになります。

yyyy-mm-dd_rds_mapping_sample.csv
AWSAccountID,AccountDesc,System,Environment,InstanceID,ClusterID,ResourceID,SnapshotID123456789012,○○用アカウント,A-system,production,db01,db01-cluster,cluster-01xxxxxxxxxx,rds:db01-cluster-2021-02-21-15-08123456789012,○○用アカウント,A-system,test,db02,db02-cluster,cluster-02xxxxxxxxxx,rds:db02-cluster-2021-02-21-14-04123456789012,○○用アカウント,B-system,production,db03,db03-cluster,cluster-03xxxxxxxxxx,rds:db03-cluster-2021-02-21-15-07123456789012,○○用アカウント,B-system,production,db04-1,db04-cluster,cluster-04xxxxxxxxxx,rds:db04-cluster-2021-02-21-14-06123456789012,○○用アカウント,B-system,production,db04-2,db04-cluster,cluster-04xxxxxxxxxx,rds:db04-cluster-2021-02-21-14-06123456789012,○○用アカウント,B-system,production,db05-1,db05-cluster,cluster-05xxxxxxxxxx,rds:db05-cluster-2021-02-21-15-42123456789012,○○用アカウント,B-system,production,db05-2,db05-cluster,cluster-05xxxxxxxxxx,rds:db05-cluster-2021-02-21-15-42123456789012,○○用アカウント,C-system,production,db06,,,rds:db06-2017-11-16-02-00123456789012,○○用アカウント,C-system,production,db06,,,rds:db06-2021-02-19-02-00123456789012,○○用アカウント,C-system,production,db06,,,rds:db06-2021-02-20-02-00123456789012,○○用アカウント,C-system,production,db06,,,rds:db06-2021-02-21-02-00

普通のRDSとAuroraで出力され方が異なります。
(普通のRDSはクラスターIDとクラスターのリソースIDが存在しないので空になっています)

各カラムの内容は次の通りです。(EC2の方と同じカラムは省略します)

カラム説明
Instance IDRDSインスタンスID
Cluster IDAuroraのクラスターID
Resource IDAuroraクラスターのリソースID。普段あまり気にしないものですが、分析に必要なので取得しています

このCSVを作成するためのサンプルコードは次の通りです。EC2と同様にBashです(書きっぷりがEC2側と少し違うのはご容赦下さい)
メインとなる紐付けのところだけ抜粋します。
普通のRDSとAurora(クラスター)でデータの取得方法が変わるのでご注意ください。

create_rds_mapping_sample.sh
#!/bin/bash# クラスタリソースID取得関数function get_resourceid(){resourceId=$(jq -r'.DBClusters[] | select(.DBClusterIdentifier == "'${1}'") | .DbClusterResourceId'${TMPFILE3})resourceId=${resourceId,,}echo${resourceId}}# タグ取得関数function get_tags(){if[[${1}=="instance"]];then
    rdsArn=$(jq -r'.DBInstances[] | select(.DBInstanceIdentifier == "'${2}'") | .DBInstanceArn'${TMPFILE1})else
    rdsArn=$(jq -r'.DBClusters[] | select(.DBClusterIdentifier == "'${2}'") | .DBClusterArn'${TMPFILE3})fi
  aws --profile${account} rds list-tags-for-resource --resource-name"${rdsArn}">${TMPFILE4}systemTag=$(jq -r'.TagList[] | select(.Key == "system") | .Value'${TMPFILE4})envTag=$(jq -r'.TagList[] | select(.Key == "environment") | .Value'${TMPFILE4})}# スナップショット取得関数(非Aurora)function get_snapshots_noaurora(){snapshotIds=$(jq -r'.DBSnapshots[] | select(.DBInstanceIdentifier == "'${1}'") | .DBSnapshotIdentifier'${TMPFILE2})echo"${snapshotIds}"}# スナップショット取得関数(Aurora)function get_snapshots_aurora(){snapshotIds=$(jq -r'.DBClusterSnapshots[] | select(.DBClusterIdentifier == "'${1}'") | .DBClusterSnapshotIdentifier'${TMPFILE5})echo"${snapshotIds}"}# CSVレコード出力関数function out_csv_record(){if[[-z${1}]];then
    snapshotId=""echo"${account},${accountDesc},${systemTag},${envTag},${rdsId},${clusterId},${resourceId},${snapshotId}"else
    for snapshotId in${1}do
      echo"${account},${accountDesc},${systemTag},${envTag},${rdsId},${clusterId},${resourceId},${snapshotId}"done
  fi}function main(){# タイトル行出力echo"AWS Account ID,Account Desc,System,Environment,Instance ID,Cluster ID,Resource ID,Snapshot ID"# まず必要な情報をAWSCLIで取得
  aws rds describe-db-instances >${TMPFILE1}
  aws rds describe-db-snapshots >${TMPFILE2}
  aws rds describe-db-clusters  >${TMPFILE3}
  aws rds describe-db-cluster-snapshots  >${TMPFILE5}# RDS毎にループ(RDSを基準に対応するクラスタとスナップショットを特定する)for rdsId in$(jq -r'.DBInstances[] | .DBInstanceIdentifier'${TMPFILE1});do## 変数初期化systemTag=""envTag=""clusterId=""resourceId=""snapshotId=""## 対応するクラスタのIDを取得clusterId=$(jq -r'.DBInstances[] | select(.DBInstanceIdentifier == "'${rdsId}'") | .DBClusterIdentifier'${TMPFILE1})## クラスタに対応するリソースIDを取得if[[${clusterId}=="null"]];then
      clusterId=""else
      resourceId=$(get_resourceid ${clusterId})fi## 対応するタグ(system、environment)を取得
    get_tags instance ${rdsId}## RDSのスナップショット一覧を取得### Auroraじゃない場合if[[-z"${clusterId}"]];then
      snapshotIds=$(get_snapshots_noaurora ${rdsId})
      out_csv_record "${snapshotIds}"### Auroraの場合else
      snapshotIds=$(get_snapshots_aurora ${clusterId})
      out_csv_record "${snapshotIds}"fi
  done# Aurora Serverless毎にループfor clusterId in$(jq -r'.DBClusters[] | select(.EngineMode == "serverless") | .DBClusterIdentifier'${TMPFILE3});do#省略done

  rm-f${TMPFILE1}${TMPFILE2}${TMPFILE3}${TMPFILE4}${TMPFILE5}exit 0
}# メイン処理実行
main

コスト配分レポートの分析

さて、ここからが本題です。(長くてすみません・・・:confounded:

まずコスト配分レポートがどのようなものなのか分からないとイメージが湧きづらいと思いますので、今回の分析で使用するカラムだけでもご説明したいと思います(カラム数が非常に多いので全部説明するのは避けます)

コスト配分レポートの中身

以下のようなカラムがあります。

代表的なカラム説明
LinkedAccountId123456789012料金が発生したAWSアカウントID
ProductNameAmazon Elastic Compute Cloud
Amazon Relational Database Service
など
料金が発生したAWSサービスの名前
UsageTypeAPN1-BoxUsage:r4.large
APN1-DataTransfer-Out-Bytes
など
料金発生対象の詳細
OperationRunInstances
CreateVolume-Gp2
など
料金発生時のオペレーション(イベント)
イベントをキャッチして実行するようなコードを書いたことある方ならピンとくると思います
ItemDescription\$0.0152 per On Demand Linux t2.micro Instance Hour
$0.05 per GB-Month of snapshot data stored - Asia Pacific (Tokyo)
など
料金の説明
UsageStartDate2020-12-01 23:00:00
など
料金発生の対象日時
UnBlendedCost1.9354838712
など
料金の金額(USD)
ResourceIdi-xxxxxxxx
arn:aws:rds:ap-northeast-1:123456789012:db:XXXXXX
リソースのARNや、EC2とかの場合はインスタンスIDが入ります
user:Name事前に設定したNameタグ
user:system事前に設定したsystemタグ
user:environment事前に設定したenvironmentタグ

コスト配分レポートを変換する

コスト配分レポートと、事前に用意したマッピングデータを使って変換処理をかけていきます。
変換処理のポイントは、「Name」(user:Name)カラムに値を入れていくことです。
この「Name」カラムを料金集計のキーにします。
例えば冒頭にお見せしたグラフの例で言うと、

  • EC2インスタンス
  • アタッチされているEBSボリューム
  • そのEBSボリュームのスナップショット
  • その他EC2に紐づく料金レコード(データ転送など)

これらの料金レコードの「Name」カラムには全てEC2インスタンス名を入れます。
これにより、とあるEC2インスタンスとそれに付随する料金がまとめて何ドルか可視化でき、ドリルダウンするとさらにその内訳が可視化できるようになるわけです。

今回はPython3です。(bashはオススメしません)
CSVを扱うため、pandasを使いました。

少し長いのでいくつかに分割します。

data_transfer_sample.py
importpandasaspdimportre# コスト配分レポートをpandasデータフレーム化
# この際、使うカラムだけ抽出する
df=pd.read_csv('./123456789012-2020-12.csv',usecols=['LinkedAccountId','ProductName','UsageType','Operation','ReservedInstance','ItemDescription','UsageStartDate','UnBlendedCost','ResourceId','user:system','user:environment','user:Name'],dtype='object')## 見やすくするため、タグ名につく「user:」を削除しておく
df=df.rename(columns={'user:system':'System','user:environment':'Environment','user:Name':'Name'})## 不要な行を削除しておく
df=df[~df.ItemDescription.str.contains('Tax of type CT')]# 消費税
df=df[~df.ItemDescription.str.contains('Total for linked account')]# アカウント毎のサマリ行

まずコスト配分レポートをpandasデータフレーム化します。
カラム名を調整したり、不要なレコードの削除をしておきます。
データ型はobjectを指定します。そのままだとUnblendedCostなどの少数がfloatにされてしまうのでこれを避けるためです。
つぎ。

data_transfer_sample.py
# 1行(各料金レコード)毎にName列を埋めていく
forindex,rowindf.iterrows():## ResourceIdが空になっているレコードへの処理
ifpd.isnull(row['ResourceId']):### EC2系レコード
ifrow['ProductName']=='Amazon Elastic Compute Cloud':#### EIP料金
ifrow['Operation']=='AssociateAddressVPC':row['Name']='EIP'#### EC2 RI料金
elifre.search('.*USD.*hourly fee per.*',row['ItemDescription']):row['Name']='EC2 RI applied instance fee'#### EC2系その他
else:row['Name']='EC2 Other'### RDS系レコード
elifrow['ProductName']=='Amazon Relational Database Service':#### RDSバックアップ料金
ifre.search('.*ChargedBackupUsage.*',row['UsageType']):row['Name']='RDS Backup Storage'#### RDS RI 前払い料金
elifre.search('.*Sign up charge for subscription.*',row['ItemDescription']):row['Name']='RDS RI Upfront'#### RDS RI料金
elifre.search('.*USD.*hourly fee per.*',row['ItemDescription']):row['Name']='RDS RI applied instance fee'### Dynamo系レコード
elifrow['ProductName']=='Amazon DynamoDB':row['Name']='Dynamo DataTransfer'### CloudWatch系レコード
elifrow['ProductName']=='AmazonCloudWatch':row['Name']='CloudWatch'### CloudTrail系レコード
elifrow['ProductName']=='AWS CloudTrail':row['Name']='CloudTrail'### IoT系レコード
elifrow['ProductName']=='AWS IoT':row['Name']='IoT'### SQS系レコード
elifrow['ProductName']=='Amazon Simple Queue Service':row['Name']='SQS Other'### 以下略

Dataframeを1行ずつチェックし、Name列を埋めていきます。
コスト配分レポートには、ResourceId列が空のレコードとそうじゃないレコードがあるので、まず空のレコードの部分の処理です。
ResourceIdが空ということは、どこのリソースで発生した料金なのかわからないということです。
なので一旦NameにはAWSのサービス名を入れます。QuickSightでの分析時は、ItemDescription列の内容で何の料金かある程度は分かります。
つぎ。

data_transfer_sample.py
## ResourceIdが空でないレコードへの処理
else:### ELB系レコード
ifrow['ProductName']=='Elastic Load Balancing':### ALBレコードの場合NameにALB名をセット
ifre.search('.*loadbalancer\/app\/.*',row['ResourceId']):row['Name']=row['ResourceId'].split('/')[2]### CLBレコードの場合NameにCLB名をセット
elifre.search('.*loadbalancer\/.*',row['ResourceId']):row['Name']=row['ResourceId'].split('/')[1]else:row['Name']='ELB Other'# NLB未使用なのでどんなレコードが出てくるかわからずこんな書き方で逃げてます
### EC2系レコード
elifrow['ProductName']=='Amazon Elastic Compute Cloud':### Name設定済の場合は何もしない
ifnotpd.isnull(row['Name']):pass### NAT Gatewayレコードの場合はNameにNAT GatewayのIDをセット
elifre.search('.*natgateway.*',row['ResourceId']):row['Name']=row['ResourceId'].split('/')[1]### 上記以外(EBS、スナップショット、T系インスタンスの課金クレジット、その他EC2系料金)
else:### レコードと同じ日のEC2マッピングデータをpandasデータフレーム化
record_date=row['UsageStartDate'][0:10]df_ec2=pd.read_csv('./'+record_date+'_ec2_mapping.csv',dtype='object')### スナップショットの場合は後段の処理のためリソースIDを抽出しておく
ifre.search('.*snapshot.*',row['ResourceId']):row['ResourceId']=row['ResourceId'].split('/')[1]### EC2マッピングデータからResourceIdが見つかればそこからNameを取得する
### 一致するResourceIdを持つ行を検索
matched_row=df_ec2[(df_ec2['Instance ID']==row['ResourceId'])|(df_ec2['Volume ID']==row['ResourceId'])|(df_ec2['Snapshot ID']==row['ResourceId'])]### 検索にマッチする行があればその行からName、System、Environmentを取得
ifnotlen(matched_row.index)==0:row['Name']=matched_row.iloc[0]['Name']row['System']=matched_row.iloc[0]['System']row['Environment']=matched_row.iloc[0]['Environment']ifpd.isnull(row['Name']):ifre.search('.*snap-.*',row['ResourceId']):row['Name']='未使用EBSのスナップショット'else:row['Name']='未使用EBS'else:row['Name']='削除済EBS、対応するEBSの無いスナップショット、リテンションされたスナップショット、AMIのスナップショット等'### 使い終わったDataframeを破棄
deldf_ec2### RDS系レコード
elifrow['ProductName']=='Amazon Relational Database Service':### クラスタバックアップ料金の場合
ifre.search('.*cluster-backup.*',row['ResourceId']):row['Name']='RDS Backup Storage'else:# レコードと同じ日のRDSマッピングデータをpandasデータフレーム化
record_date=row['UsageStartDate'][0:10]df_rds=pd.read_csv('./'+record_date+'_rds_mapping.csv',dtype='object')### コスト配分レポートのResourceIdにはARNが入っているので余計な文字列をカットしておく
row['ResourceId']=row['ResourceId'].split(':')[6]### RDSマッピングデータから一致するResourceIdを持つ行を検索
matched_row=df_rds[(df_rds['Instance ID']==row['ResourceId'])|(df_rds['Cluster ID']==row['ResourceId'])|(df_rds['Resource ID']==row['ResourceId'])|(df_rds['Snapshot ID']==row['ResourceId'])]### 検索にマッチする行があればその行からName(クラスターID)、System、Environmentを取得
ifnotlen(matched_row.index)==0:row['Name']=matched_row.iloc[0]['Cluster ID']row['System']=matched_row.iloc[0]['System']row['Environment']=matched_row.iloc[0]['Environment']### クラスターIDが空だった場合は非AuroraなのでインスタンスIDを取得
ifpd.isnull(row['Name']):row['Name']=matched_row.iloc[0]['Instance ID']### 使い終わったDataframeを破棄
deldf_rds### CloudWatch系レコード
elifrow['ProductName']=='AmazonCloudWatch':row['Name']='CloudWatch'### Route53系レコード
elifrow['ProductName']=='Amazon Route 53':row['Name']='Route53'### DynamoDB系レコード
elifrow['ProductName']=='Amazon DynamoDB':row['Name']='Dynamo - '+row['ResourceId'].split('/')[1]### Kinesis系レコード
elifrow['ProductName']=='Amazon Kinesis':row['Name']='Kinesis - '+row['ResourceId'].split('/')[1]### データ転送系レコード
elifrow['ProductName']=='AWS Database Migration Service':ifre.search('*CloudFront*',row['UsageType']):row['Name']='CloudFront'elifre.search('*DataTransfer*',row['UsageType']):row['Name']='DataTransfer'elifre.search('*InstanceUsg*',row['UsageType']):row['Name']='DMS Instance'else:row['Name']='DMS Other'### DirectConnect系レコード
elifrow['ProductName']=='AWS Direct Connect':row['Name']='DirectConnect'### CloudFront系レコード
elifrow['ProductName']=='Amazon CloudFront':row['Name']='DirectConnect'### KMS系レコード
elifrow['ProductName']=='AWS Key Management Service':row['Name']='KMS'### SQS系レコード
elifrow['ProductName']=='Amazon Simple Queue Service':row['Name']='SQS - '+row['ResourceId'].split(':')[5]### Lambda系レコード
elifrow['ProductName']=='AWS Lambda':row['Name']='Lambda'### S3系レコード
elifrow['ProductName']=='Amazon Simple Storage Service':row['Name']='S3 - '+row['ResourceId']### SNS系レコード
elifrow['ProductName']=='Amazon Simple Notification Service':row['Name']='SNS'### Athena系レコード
elifrow['ProductName']=='Amazon Athena':row['Name']='Athena - '+row['ResourceId'].split(':')[5]### Secrets Manager系レコード
elifrow['ProductName']=='AWS Secrets Manager':row['Name']='Secrets Manager'### 以下略

そしてResourceIdが空じゃない場合の処理です。
ここは実際のコスト配分レポートとにらめっこしながら、Nameに何を入れていくのか考えつつ書いていく感じです。
EC2とRDSは、事前に用意したマッピングデータを使い、EBSやスナップショット等のデータを紐付けていくようにNameに値を入れていきます。

時々、ResourceIdに対してsplitをかけていますが、これはARNからリソース名を抜き出すためです。
ARNのままでも分かるっちゃ分かりますが、さすがに長すぎて見づらいので。

data_transfer_sample.py
# CSV出力
df.to_csv('out.csv',encoding='utf-8',index=False,quoting=0,quotechar='"')

最後にcsv化してS3にアップし、それをQuickSightで読み込ませます。

まとめ

コードや文章が拙く、分かりづらいところあったらすみません。
私の職場ではこれで定期的にレポートを作成してコスト削減につなげています。BIってすごいなと素直に思いました。

また、コードの書き方で「こうしたほうがいいよ!」とか「ウチではこうしたよ!」なんかあればぜひ教えてください!
勉強したいので:sweat:


Viewing all articles
Browse latest Browse all 2866

Trending Articles