2
0

More than 1 year has passed since last update.

PowerQueryでAWS Price List API (AKA the Bulk API) からオファーファイルの一覧を取得する

Last updated at Posted at 2021-09-03

AWSには多岐に渡るサービスがあり、その価格表が各サービスのページに表示されています。
それと同時にAWS Price List APIとしてAPIも提供されているため、コチラをPowerQueryから実行してAWS価格表(オファーファイル)URL一覧を取得してみます。

今回紹介するPowerQueryを実行すると下記のような形で取得できます。

image.png

AWSから提供されているAWS Price List APIについて

Using the AWS Price List API

AWS Price List APIとしては下記2種類のAPIが存在し。
それぞれからAWS価格情報を取得する事ができます。

  • Price List Service API (AKA the Query API)
  • Price List API (AKA the Bulk API)

Price List Service API (AKA the Query API)についてはAWS-CLIpricingで利用されていますが、今回は利用しません。

Price List API (AKA the Bulk API) について

Using the bulk API

Price List API (AKA the Bulk API)では下記2種類のファイルが提供されている。

  • オファーインデックスファイル
  • オファーファイル

オファーファイルにはAWS製品の価格されており。そのオファーファイルを見つけるためにはオファーインデックスファイルを読み解く必要があります。

なおオファーインデックスファイルにはjson形式のオファーファイルURLが記載されていますがcsvに変更することでcsv形式のオファーファイルを取得する事ができます。

余談 もともとはリージョン別のオファーファイルはなかった。

オファーファイルについては、過去サービス別単位での提供でしたが今現在はリージョン別のオファーファイルも用意されています。

AWS Price List API Update – Regional Price Lists

なおこのオファーファイル、製品の組み合わせが多いサービスだとこれを網羅するために非常に巨大なjson、csvファイルとなります。

2021年9月2日現在。

最新の東京リージョンEC2サービスのオファーファイルは143MB(json)。
最新の全リージョンEC2サービスのオファーファイルは2.3GB(json)。

オファーインデックスファイルとオファーファイルについて

オファーファイルのURLは下記URLから取得できるオファーインデックスファイルに含まれる情報を読み解くことで取得する事ができます。

https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json

image.png

オファーインデックスファイルはサービス毎に下記の項目を持っている。
savingsPlanVersionIndexUrl と currentSavingsPlanIndexUrl についてはSavingsPlansがあるサービスのみ。

項目 摘要
versionIndexUrl 【インデックスファイル】バージョン単位
currentVersionUrl 【オファーファイル】全リージョン含まれている最新オファーファイル
currentRegionIndexUrl 【インデックスファイル】最新のリージョン単位
savingsPlanVersionIndexUrl 【インデックスファイル】SavingsPlansバージョン単位
currentSavingsPlanIndexUrl 【インデックスファイル】SavingsPlansリージョン単位

それぞれの項目先頭にhttps://pricing.us-east-1.amazonaws.comをつけてアクセスします。

EC2サービスの場合は下記のような感じ。

種別 URL
versionIndexUrl https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/index.json
currentVersionUrl https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/index.json
currentRegionIndexUrl https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/region_index.json
savingsPlanVersionIndexUrl https://pricing.us-east-1.amazonaws.com/savingsPlan/v1.0/aws/AWSComputeSavingsPlan/current/index.json
currentSavingsPlanIndexUrl https://pricing.us-east-1.amazonaws.com/savingsPlan/v1.0/aws/AWSComputeSavingsPlan/current/region_index.json

インデックスファイルからバージョン別・リージョン別のインデックスファイルURLが取得できました。

なおversionIndexUrlファイルの中に含まれるofferVersionUrl項目は全リージョンのオファーファイルとなりますが。

リージョン別AWSサービスの場合、index.jsonregion_index.jsonに変更するとリージョン別のインデックスファイルが取得できます。

こっちはオファーファイル。

こっちはオファーインデックスファイル。

この動作、AWS Price List API Update – Regional Price ListscurrentVersionUrlcurrentRegionIndexUrlの関係をみてそんな感じだろうと思い使っていましたが。

改めてドキュメント読み返していてcurrentではなくバージョン別でもこの形式となると記述されている所が無い気がします?

なおリージョン別ファイル提供開始以前のバージョンにはregion_index.jsonが存在しないようです。

またCodeCommitAWSGlobalAcceleratorなどのサービスはregion_index.jsonファイル自体は存在するのですが、regions項目が空になっているようです。

https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AWSCodeCommit/20170419201333/region_index.json

{
  "formatVersion" : "v1.0",
  "disclaimer" : "This pricing list is for informational purposes only. All prices are subject to the additional terms included in the pricing pages on http://aws.amazon.com. All Free Tier prices are also subject to the terms included at https://aws.amazon.com/free/",
  "regions" : { }
}

PowerQueryでオファーファイルの一覧を取得する

ここまで理解できば各種プログラムでオファーファイルが取得できるようになっているかと思います。

今回はオファーファイルURL一覧を取得するPowerQueryを紹介します。

オファーインデックスファイルを見ればわかりますが、SavingsPlansとそれ以外にわかれているため。
それぞれつくります。

全サービス全バージョン全リージョン別

全サービス全バージョン全リージョン別(リージョン別ファイルがない場合は全リージョン)。
サービス、リージョン毎にwebリクエストをするため実行すると処理時間がかなり掛かるかと思います。

欲しいサービス等でフィルタを掛けて利用するのが良いかと思います。

let
//baseURI
    baseURI="https://pricing.us-east-1.amazonaws.com",
//インデックスオファーファイル取得
    Source = Json.Document(Web.Contents("https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")),
    offers = Source[offers],
    #"Converted to Table" = Record.ToTable(offers),
//サービス別のフィルタをいれるならココ
//versionIndexUrl展開
    #"Expanded Value" = Table.ExpandRecordColumn(#"Converted to Table", "Value", {"offerCode", "versionIndexUrl"}, {"offerCode", "versionIndexUrl"}),
    #"Add versionIndexUrlFull" = Table.AddColumn(#"Expanded Value", "versionIndexUrlFull", each #"baseURI"&[versionIndexUrl]),
    #"Added versionIndexUrlContents" = Table.AddColumn(#"Add versionIndexUrlFull", "versionIndexUrlContents", each Record.ToTable(Record.Field(Json.Document(Web.Contents([versionIndexUrlFull])),"versions"))),
    #"ExpandedTable versionIndexUrlContents" = Table.ExpandTableColumn(#"Added versionIndexUrlContents", "versionIndexUrlContents", {"Value"}, {"Value"}),
    #"ExpandedRecord Value" = Table.ExpandRecordColumn(#"ExpandedTable versionIndexUrlContents", "Value", {"versionEffectiveBeginDate", "versionEffectiveEndDate", "offerVersionUrl"}, {"versionEffectiveBeginDate", "versionEffectiveEndDate", "offerVersionUrl"}),
    #"Added offerVersionUrlFull" = Table.AddColumn(#"ExpandedRecord Value", "offerVersionUrlFull", each #"baseURI"&[offerVersionUrl]),
    #"Added regionIndex" = Table.AddColumn(#"Added offerVersionUrlFull", "regionIndex", each Text.Replace([offerVersionUrlFull],"index.json","region_index.json")),
//region_indexが存在しない場合があるのでhttpResponseを取得して判断
    #"Added regionIndexhttpResponse" = Table.AddColumn(#"Added regionIndex", "httpResponse", each Record.Field(Value.Metadata(Web.Contents([regionIndex],[ManualStatusHandling = {400,401,403,404,429,500,503}])),"Response.Status")),
    #"Added RegionIndexContents" = Table.AddColumn(#"Added regionIndexhttpResponse", "RegionIndexContents", each if [httpResponse] = 200 then
Table.SelectColumns(Record.ToTable(Record.Field(Json.Document(Web.Contents([regionIndex])),"regions")),"Value") else null),
    #"ExpandedTable RegionIndexContents" = Table.ExpandTableColumn(#"Added RegionIndexContents", "RegionIndexContents", {"Value"}, {"Value"}),
    #"ExpandedRecord RegionIndexContents" = Table.ExpandRecordColumn(#"ExpandedTable RegionIndexContents", "Value", {"regionCode", "currentVersionUrl"}, {"regionCode", "currentVersionUrl"}),
//MEMO 一部サービスではregion_index.jsonは存在するがregionsが空のケースがある
//currentVersionUrlが取得できなかった時は全リージョンのオファーファイル
    #"Added offerFile" = Table.AddColumn(#"ExpandedRecord RegionIndexContents", "offerFile", each if [currentVersionUrl] = null then [offerVersionUrlFull] else #"baseURI"&[currentVersionUrl]),
    #"Added offerFile-csv" = Table.AddColumn(#"Added offerFile", "offerFile-csv", each Replacer.ReplaceText([offerFile],"json","csv")),
    #"Removed Other Columns" = Table.SelectColumns(#"Added offerFile-csv",{"Name", "offerCode", "versionIndexUrlFull", "versionEffectiveBeginDate", "versionEffectiveEndDate", "offerVersionUrlFull", "regionIndex", "httpResponse", "regionCode", "offerFile", "offerFile-csv"}),
    #"Sorted Rows" = Table.Sort(#"Removed Other Columns",{{"Name", Order.Ascending}, {"versionEffectiveBeginDate", Order.Ascending}, {"regionCode", Order.Ascending}})
in
    #"Sorted Rows"

SavingsPlans全リージョン全バージョン

let
    baseURI="https://pricing.us-east-1.amazonaws.com",
    Source = Json.Document(Web.Contents("https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")),
    offers = Source[offers],
    #"Converted to Table" = Record.ToTable(offers),
    #"Expanded Value" = Table.ExpandRecordColumn(#"Converted to Table", "Value", {"savingsPlanVersionIndexUrl", "offerCode"}, {"savingsPlanVersionIndexUrl", "offerCode"}),
    #"Filtered Rows" = Table.SelectRows(#"Expanded Value", each ([savingsPlanVersionIndexUrl] <> null)),
    #"Add savingsPlanVersionIndexUrl" = Table.AddColumn(#"Filtered Rows", "savingsPlanVersionIndexUrlFull", each baseURI&[savingsPlanVersionIndexUrl]),
    #"Add savingsPlanVersionIndexContents" = Table.AddColumn(#"Add savingsPlanVersionIndexUrl", "savingsPlanVersionIndexContents", each Json.Document(Web.Contents([savingsPlanVersionIndexUrlFull]))),
    #"Expanded savingsPlanVersionIndexContents" = Table.ExpandRecordColumn(#"Add savingsPlanVersionIndexContents", "savingsPlanVersionIndexContents", {"versions"}, {"versions"}),
    #"ExpandedList versions" = Table.ExpandListColumn(#"Expanded savingsPlanVersionIndexContents", "versions"),
    #"ExpandedRecord versions" = Table.ExpandRecordColumn(#"ExpandedList versions", "versions", {"publicationDate", "offerVersionUrl"}, {"publicationDate", "offerVersionUrl"}),
    #"Added offerVersionUrlFull" = Table.AddColumn(#"ExpandedRecord versions", "offerVersionUrlFull", each baseURI&[offerVersionUrl]),
    #"Added offerVersionUrlContents" = Table.AddColumn(#"Added offerVersionUrlFull", "offerVersionUrlContents", each Json.Document(Web.Contents([offerVersionUrlFull]))[regions]),
    #"ExpandedList offerVersionUrlContents" = Table.ExpandListColumn(#"Added offerVersionUrlContents", "offerVersionUrlContents"),
    #"ExpandedRecord offerVersionUrlContents" = Table.ExpandRecordColumn(#"ExpandedList offerVersionUrlContents", "offerVersionUrlContents", {"regionCode", "versionUrl"}, {"regionCode", "versionUrl"}),
    #"Added offerFile" = Table.AddColumn(#"ExpandedRecord offerVersionUrlContents", "offerFile", each baseURI&[versionUrl]),
    #"Added offerFile-csv" = Table.AddColumn(#"Added offerFile", "offerFile-csv", each Replacer.ReplaceText([offerFile],"json","csv")),
    #"Removed Other Columns" = Table.SelectColumns(#"Added offerFile-csv",{"Name", "offerCode", "savingsPlanVersionIndexUrlFull", "publicationDate", "offerVersionUrlFull", "regionCode", "offerFile", "offerFile-csv"}),
    #"Sorted Rows" = Table.Sort(#"Removed Other Columns",{{"Name", Order.Ascending}, {"publicationDate", Order.Ascending}, {"regionCode", Order.Ascending}})
in
    #"Sorted Rows"

最新のバージョンのみ 全サービス全リージョン別

let
//baseURI
    baseURI="https://pricing.us-east-1.amazonaws.com",
//インデックスオファーファイル取得
    Source = Json.Document(Web.Contents("https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")),
    offers = Source[offers],
    #"Converted to Table" = Record.ToTable(offers),
    //サービス別のフィルタをいれるならココ
//versionIndexUrl展開
    #"Expanded Value" = Table.ExpandRecordColumn(#"Converted to Table", "Value", {"currentRegionIndexUrl", "offerCode"}, {"currentRegionIndexUrl", "offerCode"}),
    #"Add currentRegionIndexUrlFull" = Table.AddColumn(#"Expanded Value", "currentRegionIndexUrlFull", each #"baseURI"&[currentRegionIndexUrl]),
    #"Added currentRegionIndexContents" = Table.AddColumn(#"Add currentRegionIndexUrlFull", "versionIndexUrlContents", each Record.ToTable(Record.Field(Json.Document(Web.Contents([currentRegionIndexUrlFull])),"regions"))),
    #"ExpandedTable versionIndexUrlContents" = Table.ExpandTableColumn(#"Added currentRegionIndexContents", "versionIndexUrlContents", {"Value"}, {"Value"}),
    #"ExpandedRecord Value" = Table.ExpandRecordColumn(#"ExpandedTable versionIndexUrlContents", "Value", {"regionCode", "currentVersionUrl"}, {"regionCode", "currentVersionUrl"}),
    #"Added currentVersionUrlFull" = Table.AddColumn(#"ExpandedRecord Value", "currentVersionUrlFull", each #"baseURI"&[currentVersionUrl]),
//MEMO 一部サービスではregion_index.jsonは存在するがregionsが空のケースがある
//currentVersionUrlFullが取得できなかった時は全リージョンのオファーファイル
    #"Added offerFile" = Table.AddColumn(#"Added currentVersionUrlFull", "offerFile", each if [currentVersionUrlFull] = null then Replacer.ReplaceText([currentRegionIndexUrlFull],"region_index","index") else [currentVersionUrlFull]),
//csv形式
    #"Added offerFile-csv" = Table.AddColumn(#"Added offerFile", "offerFile-csv", each Replacer.ReplaceText([offerFile],"json","csv")),
    #"Removed Other Columns" = Table.SelectColumns(#"Added offerFile-csv",{"Name", "offerCode", "regionCode", "currentRegionIndexUrlFull", "offerFile", "offerFile-csv"}),
    #"Sorted Rows" = Table.Sort(#"Removed Other Columns",{{"Name", Order.Ascending}, {"regionCode", Order.Ascending}})
in
    #"Sorted Rows"

最新のバージョンのみ SavingsPlans全リージョン

let
    baseURI="https://pricing.us-east-1.amazonaws.com",
    Source = Json.Document(Web.Contents("https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/index.json")),
    offers = Source[offers],
    #"Converted to Table" = Record.ToTable(offers),
    #"Expanded Value" = Table.ExpandRecordColumn(#"Converted to Table", "Value", {"offerCode", "currentSavingsPlanIndexUrl"}, {"offerCode", "currentSavingsPlanIndexUrl"}),
    #"Filtered Rows" = Table.SelectRows(#"Expanded Value", each ([currentSavingsPlanIndexUrl] <> null)),
    #"Add savingsPlanVersionIndexUrl" = Table.AddColumn(#"Filtered Rows", "currentSavingsPlanIndexUrlFull", each baseURI&[currentSavingsPlanIndexUrl]),
    #"Add currentSavingsPlanIndexUrlContents" = Table.AddColumn(#"Add savingsPlanVersionIndexUrl", "currentSavingsPlanIndexUrlContents", each Json.Document(Web.Contents([currentSavingsPlanIndexUrlFull]))),
    #"ExpandedRecord currentSavingsPlanIndexUrlContents" = Table.ExpandRecordColumn(#"Add currentSavingsPlanIndexUrlContents", "currentSavingsPlanIndexUrlContents", {"publicationDate", "regions"}, {"publicationDate", "regions"}),
    #"ExpandedList regions" = Table.ExpandListColumn(#"ExpandedRecord currentSavingsPlanIndexUrlContents", "regions"),
    #"ExpandedRecord regions" = Table.ExpandRecordColumn(#"ExpandedList regions", "regions", {"regionCode", "versionUrl"}, {"regionCode", "versionUrl"}),
    #"Added versionUrlFull" = Table.AddColumn(#"ExpandedRecord regions", "offerVersionUrlFull", each baseURI&[versionUrl]),
    #"Added offerFile" = Table.AddColumn(#"Added versionUrlFull", "offerFile", each [offerVersionUrlFull]),
    #"Added offerFile-csv" = Table.AddColumn(#"Added offerFile", "offerFile-csv", each Replacer.ReplaceText([offerFile],"json","csv")),
    #"Removed Other Columns" = Table.SelectColumns(#"Added offerFile-csv",{"Name", "offerCode", "currentSavingsPlanIndexUrlFull", "publicationDate", "regionCode", "offerFile", "offerFile-csv"}),
    #"Sorted Rows" = Table.Sort(#"Removed Other Columns",{{"Name", Order.Ascending}, {"publicationDate", Order.Ascending}, {"regionCode", Order.Ascending}})
in
    #"Sorted Rows"

総評

これでオファーファイルの一覧を取得できたわけですが。
AWSが提供するサービスの多さと、EC2サービスでは組み合わせが爆発してるさまをみると凄まじいの一言。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0