Azure
PowerShell

Azure EAポータルからAPIで課金情報を取得してみる

はじめに

AzureをEA契約で使用している皆様へ
突然ですが、どのように課金情報をご覧になっていますか?
EAポータルで直接見たり、PowerBIに落とし込んで見たり、様々な方法があると思います。
そんな中、最近こんな機能がGAされていました。
Announcing General Availability of Consumption and Charge APIs for Enterprise Azure customers

そうです。EAポータル用の課金情報取得APIです!!
Public Previewは昨年からされていたものの、ようやく正式リリースになりました。

APIについて

今回GAされたAPIは以下の通りです。

  • Balance and Summary API
  • Usage Details API
  • Marketplace Store Charge API
  • Price Sheet API
  • Billing Periods API

今回はその中でも使用したリソース及び課金額が取得できるUsage Details APIを使用します。

Usage Detail API

Method Request URI
GET https://consumption.azure.com/v2/enrollments/{enrollmentNumber}/usagedetails
GET https://consumption.azure.com/v2/enrollments/{enrollmentNumber}/billingPeriods/{billingPeriod}/usagedetails
GET https://consumption.azure.com/v2/enrollments/{enrollmentNumber}/usagedetailsbycustomdate?startTime=2017-01-01&endTime=2017-01-10

下二つのURIのように期間指定をしない場合は当月分の課金情報が取得されます。
レスポンスは以下のようにJSON形式で返されます。

response.json
{
    "id": "string",
    "data": [
        {                       
        "accountId": 0,
        "productId": 0,
        "resourceLocationId": 0,
        "consumedServiceId": 0,
        "departmentId": 0,
        "accountOwnerEmail": "string",
        "accountName": "string",
        "serviceAdministratorId": "string",
        "subscriptionId": 0,
        "subscriptionGuid": "string",
        "subscriptionName": "string",
        "date": "2017-04-27T23:01:43.799Z",
        "product": "string",
        "meterId": "string",
        "meterCategory": "string",
        "meterSubCategory": "string",
        "meterRegion": "string",
        "meterName": "string",
        "consumedQuantity": 0,
        "resourceRate": 0,
        "Cost": 0,
        "resourceLocation": "string",
        "consumedService": "string",
        "instanceId": "string",
        "serviceInfo1": "string",
        "serviceInfo2": "string",
        "additionalInfo": "string",
        "tags": "string",
        "storeServiceIdentifier": "string",
        "departmentName": "string",
        "costCenter": "string",
        "unitOfMeasure": "string",
        "resourceGroup": "string"
        }
    ],
    "nextLink": "string"
}

今回の目的

このままJSONファイルとして取得して管理してもいいのですが、何かと勝手が悪いためcsv形式で自動的に保存されるような仕組みを作成しようと思います。
EA 契約における Azure 使用状況と料金をタイムリーに把握しよう
↑ここにもcsvでダウンロードできるって書いてあるので行けるはず!
と思ったのですが、意外と苦戦しました。

構成

基本的な流れとしては上記のRequest URIをInvoke-WebRequestでGETし、帰ってきたJSONデータをcsvに変換するといった内容です。
ただ、一回のAPIで取得できる件数が1,000件となっているので、課金情報が1,000件以上ある場合はJSONの最後に記載されている"nextLink"のURIを叩き続きを取得するような仕組みを実装しています。
(使用量によっては1日分の情報で1,000件超えることもあります。。)

リクエストに必要は加入契約番号、アクセスキー、期間指定はスクリプトに入れ込むのもアレなので
引数としてParamに入れてます。

作成スクリプト

今回作成したスクリプトが以下になります

get_csv.ps1
Param( 
 [string]$EnrollmentNo, 
 [string]$Accesskey, 
 [string]$startTime,
 [string]$endTime
) 

function nextcsv{
Param(
    [string]$nextLink
)

Remove-Item $filename_detailRpt
$count ++
$filename_detailRpt = ".\Usage-Details_" + (Get-Date).ToString('yyyyMMddHHmmss')+ "_" + $count + ".json" 
$filename_detailRptcsv = ".\Usage-Details_" + (Get-Date).ToString('yyyyMMddHHmmss') + "_" + $count +  ".csv" 
$filename_outputcsv = ".\output_" + (Get-Date).ToString('yyyyMMddHHmmss') + ".csv"

Write-Host "Gathering billing Reports number $count ..." -ForegroundColor Green 
Invoke-WebRequest $nextLink -Headers $authHeaders -OutFile $filename_detailRpt
$json = get-content $filename_detailRpt -raw -encoding UTF8
$json = ConvertFrom-JSON $json
$csv = $json.data |
       Select "cost","accountId","productId","resourceLocationId", `
       "consumedServiceId","departmentId","accountOwnerEmail","accountName", `
       "serviceAdministratorId","subscriptionId","subscriptionGuid","subscriptionName", `
       "date","product","meterId","meterCategory","meterSubCategory", `
       "meterRegion","meterName","consumedQuantity","resourceRate", `
       "resourceLocation","consumedService","instanceId","serviceInfo1", `
       "serviceInfo2","additionalInfo","tags","storeServiceIdentifier", `
       "departmentName","costCenter","unitOfMeasure","resourceGroup" |
       ConvertTo-CSV -NoTypeInformation
$csv | Set-Content $filename_detailRptcsv
$nextLink = $json.nextLink

if([String]::IsNullOrEmpty($nextLink)){
    Remove-Item $filename_detailRpt
    Write-host "Merge csv Files" -ForegroundColor Green
    Get-ChildItem "*.csv" | Foreach{
        Import-Csv -Path $_ -encoding Default |
        Select "cost","accountId","productId","resourceLocationId", `
       "consumedServiceId","departmentId","accountOwnerEmail","accountName", `
       "serviceAdministratorId","subscriptionId","subscriptionGuid","subscriptionName", `
       "date","product","meterId","meterCategory","meterSubCategory", `
       "meterRegion","meterName","consumedQuantity","resourceRate", `
       "resourceLocation","consumedService","instanceId","serviceInfo1", `
       "serviceInfo2","additionalInfo","tags","storeServiceIdentifier", `
       "departmentName","costCenter","unitOfMeasure","resourceGroup" |
       Export-Csv -Append -NoTypeInformation -encoding Default -Path $filename_outputcsv
    }
    Remove-Item Usage-Details_*.csv
    Write-host "Completed Successfully!" -ForegroundColor Green
}else{
    nextcsv($nextLink)
}
}

$count = 1
$baseurlRest = "https://consumption.azure.com/v2/enrollments/" 

$authHeaders = @{"authorization"="bearer $accesskey";"api-version"="1.0"} 

$filename_detailRpt = ".\Usage-Details_" + (Get-Date).ToString('yyyyMMddHHmmss')+ "_" + $count + ".json" 
$filename_detailRptcsv = ".\Usage-Details_" + (Get-Date).ToString('yyyyMMddHHmmss') + "_" + $count +  ".csv" 


Write-Host "Gathering billing Reports number $count ..." -ForegroundColor Green 
$url= $baseurlRest + $enrollmentNo + "/usagedetailsbycustomdate?startTime=" `
      + $startTime + "&endTime=" + $endTime 

Invoke-WebRequest $url -Headers $authHeaders -OutFile $filename_detailRpt

$json = get-content $filename_detailRpt -raw -encoding UTF8
$json = ConvertFrom-JSON $json
$csv = $json.data |
       Select "cost","accountId","productId","resourceLocationId", `
       "consumedServiceId","departmentId","accountOwnerEmail","accountName", `
       "serviceAdministratorId","subscriptionId","subscriptionGuid","subscriptionName", `
       "date","product","meterId","meterCategory","meterSubCategory", `
       "meterRegion","meterName","consumedQuantity","resourceRate", `
       "resourceLocation","consumedService","instanceId","serviceInfo1", `
       "serviceInfo2","additionalInfo","tags","storeServiceIdentifier", `
       "departmentName","costCenter","unitOfMeasure","resourceGroup" |
       ConvertTo-CSV -NoTypeInformation
$csv | Set-Content $filename_detailRptcsv
$nextLink = $json.nextLink

if([String]::IsNullOrEmpty($nextLink)){
    Remove-Item $filename_detailRpt
}else{
    nextcsv($nextLink)
}

取得データ

実際にスクリプトを実行すると
PS.png
このように進行し(センシティブな情報が多いために黒塗りが多くてすみません。。)、
image.png
csvファイルが出力されます。

今回作成したスクリプトだととってこれる情報を全てcsvに落とし込んでいますが、Selectの部分の項目を必要最低限にすることでスリムなcsvにすることも可能です。

全部のデータを落とし込んだ場合

PS2.png
量が多くてすこぶる見辛い

特定のデータだけ落とし込んだ場合

PS3.png
ある程度はすっきりとした外観に!

実際にデータを参照する際はcsvをテーブル化してサブスクリプションやリソースグループでフィルタリングすると見やすいです。
各列の情報は以下の通りです。

列名 表示内容
cost 実際に発生した課金額
accountName 課金が紐づくEAアカウント名
subscriptionName 課金が紐づくサブスクリプション名
date 課金が発生した日付け
product サービス名
consumedQuantity サービスを使用した量
resourceRate サービスを使用した単位ごとの課金額
resourceLocation リソースの地域
tags 付与されているタグ
departmentName EAポータルでの部門名
unitOfMeasure サービスの課金を換算する単位
resourceGroup リソースグループ名

最後に

このように機械的にcsvが取得できれば、このcsvをデータベースにため込んで何かしらのアプリケーションから取得できるようにしたり、色々と活用できそうです。
私は一旦力尽きたので、気が向いたらそういった仕組みの実装も考えます。
この分野の情報は調べてもあまり出てこないので、皆さんガシガシ使って情報を豊富にしていきましょう!