AWSには多岐に渡るサービスがあり、その価格表が各サービスのページに表示されています。
それと同時にAWS Price List API
としてAPIも提供されているため、コチラをPowerQuery
から実行してAWS価格表(オファーファイル)URL一覧を取得してみます。
今回紹介するPowerQuery
を実行すると下記のような形で取得できます。
AWSから提供されている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-CLI
のpricing
で利用されていますが、今回は利用しません。
Price List API (AKA 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
オファーインデックスファイルはサービス毎に下記の項目を持っている。
※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.json
をregion_index.json
に変更するとリージョン別のインデックスファイルが取得できます。
こっちはオファーファイル。
こっちはオファーインデックスファイル。
この動作、AWS Price List API Update – Regional Price Lists のcurrentVersionUrl
とcurrentRegionIndexUrl
の関係をみてそんな感じだろうと思い使っていましたが。
改めてドキュメント読み返していてcurrent
ではなくバージョン別でもこの形式となると記述されている所が無い気がします?
なおリージョン別ファイル提供開始以前のバージョンにはregion_index.jsonが存在しないようです。
またCodeCommit
、AWSGlobalAccelerator
などのサービスはregion_index.json
ファイル自体は存在するのですが、regions
項目が空になっているようです。
{
"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サービスでは組み合わせが爆発してるさまをみると凄まじいの一言。